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")