Raku:如何使参数可选,有默认值,并带有 where 测试?

Ste*_*ieD 6 raku

找不到让它发挥作用的方法:

sub triple(Str:D $mod where * ~~ any @modifiers = 'command' ) { }

如果我不传入参数,则会收到错误:

Too few positionals passed; expected 1 argument but got 0

后面带问号$mod

sub triple(Str:D $mod? where * ~~ any @modifiers = 'command' ) { }

我得到:

Constraint type check failed in binding to parameter '$mod'; expected anonymous constraint to be met but got Str (Str)

Ste*_*ieD 5

看来这可能是一个优先级问题。这有效:

sub triple(Str:D $mod? where (* ~~ any @modifiers) = 'command' ) {}
Run Code Online (Sandbox Code Playgroud)


rai*_*iph 3

TL;DR您已经在答案中确定了问题(优先顺序)并提供了解决方案。这个答案涵盖了发生的事情;为什么会出现优先权问题;为什么 Raku 的语法/解析器不只是正确;并列出了一些解决方案,我将从其中的几个开始。


代替:

sub triple(Str:D $mod? where * ~~ any @modifiers = 'command' ) { }
Run Code Online (Sandbox Code Playgroud)

我建议移动any并编写其中之一:

sub triple(Str:D $mod? where * ~~ @modifiers.any = 'command' ) { }
sub triple(Str:D $mod? where      @modifiers.any = 'command' ) { }
Run Code Online (Sandbox Code Playgroud)

发生了什么

子句= ...末尾的where被解析为赋值 (to @modifiers) 而不是默认值 (for $mod):

  1. @modifiers = 'command'被评估,覆盖任何值@modifiers

  2. 创建any与一个元素 ( ) 的连接'command'

  3. 现在唯一triple可以接受的论点是'command'

为什么会出现优先权问题

Raku 的语法设计具有良好的人体工程学特性。这包括减少对括号和大括号的需要的设计细节。总的来说,这些设计细节带来了巨大的净胜利。但皱纹是有的,你也遇到过。

Raku 允许写入where ...来指定where子句,而不要求对位使用显式花括号 lambda ( {...}) ...。人们甚至可以仅使用*. 好的!但 lambda 在哪里结束呢?如果您使用显式大括号,那就很清楚了。如果不是,什么决定了 lambda 的结束?

更一般地说,解析器不应该只知道in不是任何lambda 的一部分吗=?如果在解析该部分之前有一个子句,它应该自动完成一个子句吗?应该始终被解析为参数的默认值吗?= 'command'where= ...= ...

人们可以很容易地看到其中的歧义(一旦注意到它),Raku 的语法/解析器也可以这样做。它只需要通过拒绝此类语法、要求编码器显式消除歧义(例如使用括号,就像您所做的那样)或选择解析方式来解决这种歧义。

面对歧义,Raku 的语法/解析器所做的是选择。而且它选择错误。(当然,除非有人希望它是左侧某个值的分配=,而不是参数的默认值,尽管这不太可能。)

为什么 Raku 的语法/解析器不正确

为什么解析器不会因为代码过于模糊而拒绝该代码,或者足够聪明地选择“这是默认的”解释?它当然可以——Raku 语法/解析器功能是图灵完备的,在能力上相当于乔姆斯基解析层次结构中的不受限制的语法类别——那么为什么它不正确呢?

简而言之,至少在我看来,它得到了正确的数量。但这是主观的、措辞奇怪且模糊的,所以它可能不是一个令人满意的总结。因此,我将尝试提供更多细节,希望能提供更多信息。


Raku 的每一项设计决策都会公开讨论,并且几乎所有决策都可以搜索到公共记录。为了深入研究这些讨论,我建议从 Liz++ 出色的 IRC 日志服务开始,并在列出的众多频道中,重点关注从 2005 年到 2019 年左右运行的日志#perl6


尽管过去 20 年我参加过许多 Raku 设计讨论,但我不太记得围绕这个决定的所有讨论,即= ...子句末尾a 的含义不明确where,以及什么去做这件事。我自己最近还没有完成我建议的挖掘工作;现在我将把它留给任何感兴趣的读者。相反,我将概述我认为的一些促成因素:

  • 单遍解析

    Raku 的“编织”语言设计方法需要单遍解析。

  • 最长解析方向

    最长的令牌匹配对于用户可定义的编织(请参阅上一点中的链接)真正可行来说几乎是至关重要的。LTM 反映了一个普遍原则,即人类自然倾向于识别最长的“令牌”(当然在合理范围内)。也就是说,如果一个人看到它$100,它会在认知上给人留下一百美元的印象,而不是一个美元符号,一个1,一个0,然后另一个0

    类似的处理也适用于解析一串标记(同样,在合理范围内);如果不是因为人们学会了将其视为= ...指定参数的默认值,那么@modifiers = 'command'自然会被视为对 的赋值@modifiers

  • 有限回溯

    回溯是缓慢的,病态的回溯是万恶的。因此,Raku 的语法/解析器在除三种情况之外的所有情况下都避免了潜在的回溯,这三种情况确实是正确的解决方案,并且完全避免了任何病态回溯的风险。

  • 处理歧义

    虽然人工语言的目标是消除所有歧义,但越接近消除所有歧义,需要的无关和分散注意力的语法就越多,例如需要频繁使用分隔符(括号、大括号、方括号等)以确保消歧义。这使得语言变得越来越不友好冗长。乐文化避免意识形态上的“愚蠢的一致性”极端。


Raku 的设计师(主要是 Larry Wall)考虑了所有这些因素以及更多因素,并得出了 Raku 的解决方案:

  • 合理预测

    足够简单和可预测的解析方法,以及对用户可能遇到的任何意外的可能性和成本的必要敏感性,会大有帮助,与where子句相关的设计就是一个很好的例子。

    虽然优先级问题可能令人惊讶,并且错误消息没有帮助,但我,呃,预测您会发现与此相关的ERN信号会在相当短的时间内很好地调整,就像大多数事情一样当你学习 Raku 时可能会绊倒你。

  • 使用预测解析

    虽然有多种方法可以满足上述所有要求,但预测解析1是一个不错的选择,而且——并非巧合!-- 使用 Raku 语法最自然地编写的一种,以及用于 Raku 自己的语法/解析器的一种。

其他一些解决方案

以下是未按预期工作的情况:

sub triple(Str:D $mod? where * ~~ any @modifiers = 'command' ) { }
                                                ^ Needs to be end of `where` clause
Run Code Online (Sandbox Code Playgroud)

您提出了一个解决方案,我在一开始就提出了几个解决方案。接下来还有一些。

你用了括号。以下是使用括号的一些其他方法:

sub triple(Str:D $mod? where * ~~ any(@modifiers) = 'command' ) { }
sub triple(Str:D $mod? where * ~~ (any @modifiers) = 'command' ) { }
Run Code Online (Sandbox Code Playgroud)

或者,切换到在大括号内使用$_(又名“it”)而不是(又名“whatever”):*

sub triple(Str:D $mod? where { $_ ~~ any @modifiers } = 'command' ) { }
Run Code Online (Sandbox Code Playgroud)

脚注

1维基百科页面以一种可能令人困惑的方式讨论“语法”和“歧义”,因为它们的使用方式与 Raku 和本答案上下文中使用这些词的方式不同。但讨论这个问题对于这个 SO 来说是一个不合适的兔子洞。