如何对Raku函数的数组参数的条目进行类型约束?
我试图定义一个子程序Raku,其理由是,比方说,一个阵列的诠释S(强加的约束,即拒绝那些参数不是 ArrayS的IntS)。
问题:实现这一目标的“最佳”方式是什么(最惯用的,或最直接的,或者您认为“最佳”在这里应该是什么意思)?
在RakuREPL 中运行的示例如下。
我所希望的会奏效
> sub f(Int @a) {1}
&f
> f([1,2,3])
Type check failed in binding to parameter '@a'; expected Positional[Int] but got Array ([1, 2, 3])
in sub f at <unknown file> line 1
in block <unit> at <unknown file> line 1
另一个非工作示例
> sub f(@a where *.all ~~ Int) {1}
&f
> f([1,2,3])
Constraint type check failed in binding to parameter '@a'; expected anonymous constraint to be met but got Array ([1, 2, 3])
in sub f at <unknown file> line 1
in block <unit> at <unknown file> line 1
虽然
> [1,2,3].all ~~ Int
True
什么工作
两种变体
> sub f(@a where { @a.all ~~ Int }) {1}
和
> sub f(@a where { $_.all ~~ Int }) {1}
给我我想要的:
> f([5])
1
> f(['x'])
Constraint type check failed in binding to parameter '@a'; expected anonymous constraint to be met but got Array (["x"])
in sub f at <unknown file> line 1
in block <unit> at <unknown file> line 1
我过去使用过它,但它让我觉得有点笨拙/冗长..
补充说明
Int @a我最初尝试的语法并不完全是假的,但我不知道什么时候应该通过,什么时候不应该通过。
例如,我可以在课堂上做到这一点:
> class A { has Int @.a }
(A)
> A.new(a => [1,2,3])
A.new(a => Array[Int].new(1, 2, 3))
> A.new(a => [1,2,'x'])
Type check failed in assignment to @!a; expected Int but got Str ("x")
in block <unit> at <unknown file> line 1
编辑 1
根据文档,这有效
> sub f(Int @a) {1}
&f
> my Int @a = 1,2,3 # or (1,2,3), or [1,2,3]
> f(@a)
1
但是如果我Int在声明中省略,@a我会回到之前报告的错误。如前所述,我无法f在匿名数组上运行,使用f([1,2,3]).
回答
我认为主要的误解是,my Int @a = 1,2,3并且[1,2,3]在某种程度上是等价的。他们不是。第一种情况定义了一个只接受Int值的数组。第二种情况定义了一个可以接受任何东西的数组,并且恰好在其中包含Int值。
我会尽量覆盖你试过了,为什么他们没有工作的所有版本,并可能如何将工作。我将使用一个裸dd作为到达函数体的证据。
#1
sub f(Int @a) { dd }
f([1,2,3])
这不起作用,因为签名接受对其容器描述符Positional有Int约束的a 。签名绑定仅查看参数的约束,而不查看值。观察:
my Int @a; say @a.of; # (Int)
say [1,2,3].of; # (Mu)
say Mu ~~ Int; # False
这种方法没有解决方案,因为没有[ ]生成Array带Int约束的语法。
#2
sub f(@a where *.all ~~ Int) { dd }
这是非常接近的,但使用*是不正确的。我不确定这是否是一个错误。
您发现这些解决方案也有效:
sub f(@a where { @a.all ~~ Int }) { dd }
sub f(@a where { $_.all ~~ Int }) { dd }
幸运的是,你不能有实际指定明确的块。这也有效:
sub f(@a where @a.all ~~ Int) { dd }
sub f(@a where $_.all ~~ Int) { dd }
除了您找到的@a.all和$_.all解决方案之外,还有第三个解决方案:将*!
sub f(@a where .all ~~ Int) { dd }
#3
class A { has Int @.a }
A.new(a => [1,2,3])
这与签名绑定不同。有效地在.new你做的是:
@!a = [1,2,3]
这是有效的,因为Int您指定的数组中只有值。正如你所展示的,如果里面还有其他东西,它就会失败。但这与:
my Int @a = [1,2,"foo"]
失败。