Types 为什么将Perl 6命名参数约束为确定值会使其成为必需值?

Types 为什么将Perl 6命名参数约束为确定值会使其成为必需值?,types,signature,raku,Types,Signature,Raku,考虑这些子例程,它们都采用单个命名参数。命名参数应该是可选的,我还没有看到任何东西说有例外 没有类型约束就没有问题;命名参数不是必需的。使用可以接受类型对象的类型约束(无注释、:U、和:)没有问题 Parameter '$quux' of routine 'quux' must be an object instance of type 'Int', not a type object of type 'Int'. Did you forget a '.new'? in sub quux

考虑这些子例程,它们都采用单个命名参数。命名参数应该是可选的,我还没有看到任何东西说有例外

没有类型约束就没有问题;命名参数不是必需的。使用可以接受类型对象的类型约束(无注释、
:U
、和
)没有问题

Parameter '$quux' of routine 'quux' must be an object instance of type 'Int', 
not a type object of type 'Int'.  Did you forget a '.new'?
  in sub quux at /Users/brian/Desktop/type.p6 line 16
  in block <unit> at /Users/brian/Desktop/type.p6 line 37

FWIW,可选位置参数也存在类似问题:

sub a(Int:D $number?) { ... }
a; # Parameter '$number' of routine 'a' must be an object instance of type 'Int', not a type object of type 'Int'.  Did you forget a '.new'
如果将类型对象指定为默认值,则会出现相同的问题:

sub a(Int:D $number = Int) { ... };
a; #  # Parameter '$number' of routine 'a' must be an object instance of type 'Int', not a type object of type 'Int'.  Did you forget a '.new'
恐怕这只是应用于参数时的
:D
约束的结果:您只需指定一个定义的默认值,它就可以在没有任何参数的情况下调用

当然,另一种方法可以是使用
多子
和必需的命名参数:

multi sub a() { say "no foo named" }
multi sub a(Int:D :$foo!) { say "foo is an Int:D" }

希望这有帮助

所有参数都有一定的值,即使它们是可选的。我澄清了您在和中引用的文档

可选参数具有作为显式或隐式类型约束的类型对象的默认值(对于例程,隐式约束为
Any
,对于块,隐式约束为
Mu

在上面,默认值传递参数上的类型约束。如果使用
:U
类型smileys,情况也是如此

但是,当您使用
:D
类型smiley时,默认值不再与类型约束匹配。如果不选中该选项,您将失去指定
:d
约束的好处。例如,由于默认类型对象会在该例程的主体中引起爆炸,因此期望参数为确定值

sub (Int:D $a?, Num:D :$b) { say $a/$b }()

并回答指定
:D
是否应自动生成所需参数的名义问题。我对这个想法持-1观点,因为它引入了一种特殊情况,用户必须学会只保存一个输入字符(根据需要标记param的
)。这样做也会产生一个不太有用的错误,它讨论的是算术或所需参数,而不是实际问题:参数的默认值未通过参数的类型检查。

我通过检查类型
Any
或我实际想要的智能匹配来解决这个问题:

sub f ( :$f where { $^a.^name eq 'Any' or $^a ~~ Int:D } ) {
    put $f.defined ?? "f defined ($f)" !! 'f not defined';
    }

f( f => 5 );
f();
回答我原来的问题:所有参数都是必需的。这只是他们如何获得价值的问题。可以通过参数、显式默认或隐式默认(基于其类型约束)。

此答案:

  • 说明此情况适用于所有参数和变量

  • 解释了为什么约束为确定值会导致Raku/Rakudo抱怨

  • 为组合可选参数和定值约束提供了一个健壮的解决方案

这种情况适用于所有受约束的参数和变量 为什么将Raku命名参数约束为确定值会使其成为必需值

这不仅仅是命名参数。你的例子是:

sub quux ( Int:D :$quux ) {}
quux # "Parameter '$quux' ... must be an object instance ..."
但对于可选位置,情况也是如此:

sub quux ( Int:D $quux? ) {}
quux # "Parameter '$quux' ... must be an object instance ..."
Raku也需要
:D
变量的值:

my Int   $foo;   # Won't complain if you constrain to a plain Int
my Int:U $foo;   # Int:U is fine too
my Int:_ $foo;   # Still fine
my Int:D $foo; # "... type Int:D requires an initializer"
为什么约束到一个确定的值会让Raku/Rakudo抱怨 首先,撇开NativeCall不谈,Raku/Rakudo是。因此,虽然用户可以假装或认为变量或参数没有赋值/绑定值,但实际上,如果用户代码可以访问它,则必须始终有赋值/绑定值。因此,每个变量/参数都必须有一个隐式默认初始值,如果没有显式的初始值,它将被初始化为该初始值


如果你不要求它变魔术,Raku会悄悄地保护内存安全。如果根本不指定类型,则默认初始值为
Mu
type对象。如果指定类型,例如
Int
Int:U
Int:
,则它是
Int
类型对象。但是如果您指定
Int:D
而不指定默认初始值,它将放弃,因为它以与您相同的方式看待问题:

我可以通过指定一个默认值来解决这个问题,但是。。。什么确切的
Int
适合?无论它是什么,都会有一些神奇的价值,会使代码变得复杂和分散注意力

Raku认为没有你的帮助这个问题是无法解决的。它认为,在一般情况下,无论选择什么样的启发式算法,选择一些任意的定值都会导致很多错误。因此,它拒绝悄悄地处理这个问题


在参数情况下,它会引发一个可恢复的异常:

sub quux ( Int:D :$quux? ) { say 42 } # Does NOT display 42
quux;
CATCH { .resume }
say 99; # 99
my Int:D $quux; # "Error while compiling ... requires an initializer"
CATCH { .resume }
say 99; # Does NOT display 99
Rakudo拒绝将
qux
调用绑定到
qux
sub,并抛出异常。这足以维持内存安全。如果你选择继续,那就在你头上吧

在可变情况下,Rakudo无法保持内存安全(假定它拒绝选择任意确定值),并立即退出程序而不引发异常:

sub quux ( Int:D :$quux? ) { say 42 } # Does NOT display 42
quux;
CATCH { .resume }
say 99; # 99
my Int:D $quux; # "Error while compiling ... requires an initializer"
CATCH { .resume }
say 99; # Does NOT display 99

潜在的词汇歧义可能会让人对这种行为感到惊讶。参数可以称为“可选”。这意味着与该参数对应的参数是可选的,而不是参数本身或分配/绑定到该参数的值是可选的

组合定值约束和可选参数的稳健解 你可以:

  • 将默认值设置为用户不会通过的某个值

  • 应用一个约束,该约束可以是该值,也可以是您希望应用的其他约束

布赖恩的解决方案是朝着这个方向迈出的一步。以下是同一解决方案的更清晰版本:

subset OptionalInt where Any:U | Int:D;

sub f ( OptionalInt :$f ) { say $f }

f( f => 5 );   # 5
f();           # (Any)
f( f => Any);  # (Any)
f( f => 'a' ); # expected OptionalInt but got Str ("a")
虽然这是一种改进(更易于阅读代码、更快、更好的错误消息),但上述解决方案仍然存在以下缺点:
class NoArgPassed {}

subset OptionalInt where NoArgPassed | Int:D;

sub f ( OptionalInt :$f = NoArgPassed ) { say $f }

f( f => 5 );   # 5
f();           # (NoArgPassed)
f( f => Any);  # expected OptionalInt but got Any (Any)
f( f => 'a' ); # expected OptionalInt but got Str ("a")