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

Raku: how do I make an argument optional, have a default, with a where test?

无法找到让它工作的方法:

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)

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

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

TL;DR 您已经在答案中确定了问题——优先级——并提供了解决方案。这个答案涵盖了发生的事情;为什么会出现优先级问题;为什么 Raku 的 grammar/parser 不正确;并列出了一些解决方案,我将从其中几个开始。


而不是:

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

我建议移动 any 并写下其中一个:

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

发生了什么事

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

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

  2. any 使用一个元素 ('command') 创建一个连接。

  3. 现在 triple 将接受的唯一参数是 'command'

为什么会出现优先级问题

Raku 的语法设计符合人体工程学。这包括 减少对圆括号和大括号 需求的设计细节。总的来说,这些设计细节产生了巨大的净赢。但是皱纹是有的,你遇到过

Raku 允许编写 where ... 来指定一个 where 子句,而无需 要求 使用显式大括号 lambda ({...}) ... 位。甚至可以只使用 * 创建一个 lambda。好的!但是 lambda end 在哪里呢?如果您使用显式大括号,则很清楚。如果不是,是什么决定了 lambda 的结束?

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

人们很容易看出歧义(一旦有人注意到它),does/could Raku 的 grammar/parser 也是如此。它只需要 解决 歧义,要么拒绝这种语法,要求编码器明确消除歧义(例如,使用括号,就像你所做的那样),要么选择解析的方式。

Raku 的 grammar/parser 面对歧义所做的就是选择。它选择了错误的。 (当然,除非有人 想要 它是 = 左侧的某个值的赋值,而不是参数的默认值,尽管这不太可能。)

为什么 Raku 的 grammar/parser 没有完全正确

为什么解析器不拒绝此代码太模棱两可,或者不聪明地选择“这是默认”解释?它肯定 可以 -- Raku grammar/parser 特性是图灵完备的,在功能上等同于 Chomsky 解析层次结构中的 unrestricted grammars 类别 -- 那为什么不呢它只是做对了吗?

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


每个 Raku 设计决策都会公开讨论,并且基本上所有决策都有可搜索的 public 记录。要深入研究这些讨论,我建议从 Liz++ 的出色 IRC 日志服务开始,并在列出的众多频道中关注 the #perl6 logs that ran from 2005 thru 2019 or so.


尽管过去 20 年我参与了很多关于 Raku 设计的讨论,但我对围绕这个关于 = ...where 子句的末尾,以及如何处理它。而且我自己最近还没有完成我建议的挖掘工作;现在我会把它留给任何感兴趣的读者。相反,我将概述我认为会产生的一些影响因素:

  • 单遍解析

    Raku 的 "braided" 语言设计方法需要单程解析。

  • 最长解析方向

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

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

    的赋值
  • 回溯有限

    回溯慢,病态回溯万恶。所以 Raku 的 grammar/parser 避免了除了三种情况之外的所有潜在回溯,它确实是正确的解决方案,并且 完全 避免了任何 病态 [=183= 的风险]回溯。

  • 处理歧义

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


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

  • 理性预测

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

    虽然优先级问题可能出人意料,错误消息也无济于事,但我,呃,我预测你会发现你的 ERN 信号会在相当短的时间内很好地调整,只是因为在您学习 Raku 时,大多数可能会绊倒您的事情都会如此。

  • 使用预测解析

    虽然有多种方法可以满足上述所有条件,但 predictive parsing1 是一个不错的选择,而且——并非巧合! -- 使用 Raku 语法最自然地编写的一种,以及用于 Raku 自己的语法的一种 grammar/parser.

其他一些解决方案

以下是按预期工作的原因:

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

你提出了一个解决方案,我在开始时提出了几个。还有一些跟进。

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

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

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

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

脚注

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