问题 Int(酷)强制的重点是什么?


关于功能的Perl 6网站说

强制类型可以帮助您在例程中使用特定类型,但接受更广泛的输入。调用例程时,参数会自动转换为较窄的类型。

sub double(Int(Cool) $x) {
    2 * $x
}

say double '21';    # 42
say double Any;     # Type check failed in binding $x; expected 'Cool' but got 'Any'

这里Int是强制参数的目标类型,而Cool是例程接受作为输入的类型。

但是这个子有什么意义呢?是不是 $x 只是一个 Int?为什么要限制调用者实现 Cool 争论?

我对这个例子感到困惑,因为 Int 已经 is Cool。所以我做了一个示例,其中类型不共享层次结构:

class Foo { method foomethod { say 'foomethod' } }
class Bar {}

class Quux is Foo {
# class Quux { # compile error
  method Bar { Bar.new }
}

sub foo(Bar(Foo) $c) {
  say $c.WHAT;    # (Bar)
  # $c.foomethod  # fails if uncommented: Method 'foomethod' not found for invocant of class 'Bar'
}

foo(Quux.new)

在这里的呼吁 foo 被限制提供一个 Foo 可以转换为 Bar 但 foo 甚至不能称之为方法 Foo 上 $c 因为它的类型   Bar。那么为什么呢 foo 关心被胁迫的类型是什么 Foo 首先?

有人会对此有所了解吗?此外,还赞赏指向相应文档和规范部分的链接。我找不到任何有用的东西。


4977
2018-01-19 10:47


起源

你不限制来电者 也 实行 Cool,你限制它 只要 实行 Cool 并提供强制方法; double '21' 传递进去 Str,但是被叫方到达了 Int - Christoph
@Christoph我放弃了“也”。我希望它现在准确无误。 - musiKk
听起来像是你向后理解它。签名 Int(Cool)  接受 一个 Cool (更一般的类型)并促进到 Int 在当地,而不是相反。所以你可以给它一个 Str 并将它转换为 Int,确实如此 不 如果你指定就会发生 Int 在签名中。你一直在谈论给打电话者带来的不便 - 打电话者在没有被迫自己施加压力的情况下会遇到什么不便? - darch
不便之处在于,如果我的类型不是 Cool 但是可以强迫 Int 我要么必须做到 Cool 或在呼叫方执行转换。 - musiKk
如果它强迫 Int 然后它强迫 Cool因为 Int 是一个 Cool。 - darch


答案:


指定参数类型,没有强制: Int $x

我们可以声明:

sub double (Int $x) { ... } # Accept only Int. (No coercion.)

然后这将工作:

double(42);

但不幸的是打字 42 对此:

double(prompt('')); # `prompt` returns the string the user types

导致 double 打电话给失败 Type check failed in binding $x; expected Int but got Str ("42") 因为 42虽然看起来像一个数字,但在技术上是一串类型 Str,我们要求不要强迫。

使用全面强制指定参数类型: Int() $x

我们可以在sub的签名中引入任意值的毯式强制:

sub double (Int(Any) $x) { ... } # Take Any value. Coerce to an Int.

要么:

sub double (Int() $x)    { ... } # Same -- `Int()` coerces from Any.

现在,如果你输入 42 当提示时 double(prompt('')); 语句,运行时类型检查失败不再适用,而是运行时尝试将字符串强制转换为Int。如果用户键入格式正确的数字,则代码才有效。如果他们打字 123abc 强制将在运行时失败,并显示一条错误消息:

Cannot convert string to number: trailing characters after number in '123abc'

任何值的毯式强制的一个问题是像这样的代码:

class City { ... } # City has no Int coercion
my City $city;
double($city);

在运行时失败并显示以下消息:“找不到'City'类调用的方法'Int'”。

使用Cool值强制指定参数类型: Int(Cool) $x

我们可以选择任何价值的强制和全面强制之间的平衡点。

强迫来自的最佳阶层往往是 Cool class,因为Cool值可以保证很好地强制转换为其他基本类型或生成一个很好的错误消息:

# Accept argument of type Cool or a subclass and coerce to Int:
sub double (Int(Cool) $x) { ... }

有了这个定义,以下内容:

double(42);
double(prompt(''));

尽可能地工作,并且:

double($city);

失败的“类型检查失败绑定$ x;预期酷但得到城市(城市)”这对于程序员而言可以说比“方法'Int'找不到类'城''的调用更好一点。


为什么foo会首先关注那个被胁迫的类型是Foo?

希望现在显而易见的是,值得限制从类型到强制的唯一原因 Foo是因为这是一种预期成功强迫的类型 Bar 价值(或者,也许是友好消息失败)。

有人会对此有所了解吗?此外,还赞赏指向相应文档和规范部分的链接。我找不到任何有用的东西。

您最初引用的文档几乎都是针对最终用户文档的。希望它现在有意义,你已经完成了。如果没有请评论,我们将从那里开始。


4
2018-01-20 02:38



我想我现在明白了。它对我来说仍然有点武断,但我可能只需要更有经验。因此,如果您尝试编译,抛出编译错误(警告?)会不会有用 X(Y) 哪里 Y 不是子类型 X?你还知道其他有用的例子吗? Int(Cool)? - musiKk
Perl 6使编码器可以使用静态(编译时)和动态(运行时)类型检查。一般来说,它对于方法是静态的,对于方法是动态的。但是你可以根据给定的用例移动任何方向。平原 Int $x 子参数的声明指示Perl 6静态检查任何匹配的参数是否为 Int  或者是子类 Int。如果这是可用的最佳匹配子,但仍然无法键入check,则会出现编译时错误。 - raiph
请注意,multidispatch,如中所述 关于多次发送的维基百科页面的Perl 6部分, 在 doc.perl6.org/language/functions,以及语言设计文档中的大部分内容 S12:Multisubs和Multimethods,在这里作为类型检查功能的驱动程序实际上是相关的(尽管理论上并不是因为类型检查功能对于常规单一调度的工作方式相同)。 - raiph
“除了Int(酷),你知道其他有用的例子吗?”我不记得看到任何即时引人注目的用例。这是一个小抽象的例子。想象一下 City 我在答案中发明了我 能够 强迫一个数字,但你想知道这种强制是否被使用过 double。您可以编写匹配的例程签名 Int(City) (然后在该版本的正文中记录其使用 double)而不会减慢任何调用的代码 double用一个不是a的参数 City 类型。 - raiph
来自Rakudo源代码的一对例子。第一, Grammar.parsefile 强制转换为Str,但仅限于值为Cool(或子类型,包括Str本身)。那么一个 $grammar.parsefile($val-that-isn't-Cool) 调用将无法编译。第二, 该 BUILD StrDistance的子方法。一个 $value.StrDistance 调用将编译提供 $value的类型是 Any (或。的子类 Any)。 - raiph


这样做是接受一个子类型的值 ,并试图将其转换为 诠释。那时候呢  一个 诠释 无论以前是什么。

所以

sub double ( Int(Cool) $n ) { $n * 2 }

真的可以被认为是(我认为这是它在Rakudo实际实现的方式)

# Int is a subtype of Cool otherwise it would be Any or Mu
proto sub double ( Cool $n ) {*}

# this has the interior parts that you write
multi sub double (  Int $n ) { $n * 2 }

# this is what the compiler writes for you
multi sub double ( Cool $n ) {
    # calls the other multi since it is now an Int
    samewith Int($n);
}

所以这接受任何 诠释STRFatRat排列哈希等等,并试图将其转换为 诠释 在打电话之前 &infix:<*> 用它,和 2

say double '  5  '; # 25
say double 2.5;     # 4
say double [0,0,0]; # 6
say double { a => 0, b => 0 }; # 4

您可以将其限制为a  代替 任何 所有  基本上需要价值来提供强制 诠释

:( Int(Any) $ ) 可以简化为公正 :( Int() $ ) )


你可能这样做的原因是你需要它成为一个 Int 在sub中,因为你正在调用其他使用不同类型执行不同操作的代码。

sub example ( Int(Cool) $n ) returns Int {
    other-multi( $n ) * $n;
}

multi sub other-multi ( Int $ ) { 10 }
multi sub other-multi ( Any $ ) {  1 }

say example 5;   # 50
say example 4.5; # 40

在这种特殊情况下,您可以将其写为其中之一

sub example ( Cool $n ) returns Int {
    other-multi( Int($n) ) * Int($n);
}

sub example ( Cool $n ) returns Int {
    my $temp = Int($n);
    other-multi( $temp ) * $temp;
}

sub example ( Cool $n is copy ) returns Int {
    $n = Int($n);
    other-multi( $n ) * $n;
}

它们都不像使用签名来强制它的那个一样清晰。


通常对于这样一个简单的功能,你可以使用其中一个,它可能会做你想要的。

my &double = * * 2; # WhateverCode
my &double = * × 2; # ditto

my &double = { $_ * 2 };       # bare block
my &double = { $^n * 2 };      # block with positional placeholder
my &double = -> $n { $n * 2 }; # pointy block

my &double = sub ( $n ) { $n * 2 } # anon sub
my &double = anon sub double ( $n ) { $n * 2 } # anon sub with name

my &double = &infix:<*>.assuming(*,2); # curried
my &double = &infix:<*>.assuming(2);

sub double ( $n ) { $n * 2 } # same as :( Any $n )

4
2018-01-19 17:30



我知道 什么 它确实。我想知道 为什么 人们需要它。你有一个句子中有“理由”,我只是不明白。为什么我“基本上”要求类型提供强制 Int 什么时候 Int() 它本身已经明确地指明了这个要求? (也不熟悉 proto 有关它的信息很难得到。似乎是关于原型...) - musiKk
@musiKk当你调用一个多子你实际上调用一个proto sub然后调用适当的多候选者。 - Brad Gilbert
再一次,你在回答“什么”时非常有帮助。我想知道“为什么”你可能会给来电者带来不便。在 Socket::INET 一种方法想要一个 Cool,另一个 Int(Cool)。两次它在语义上都是字节数。这可能超出了这个问题的范围;我只是想了解原因,所以我知道在自己设计API时该怎么做。 - musiKk


我错过了什么吗?我不是Perl 6专家,但似乎语法允许一个人独立指定 允许哪些输入类型 和 如何将输入呈现给函数

限制允许的输入很有用,因为这意味着当使用无意义的参数调用函数时,代码将导致错误,而不是静默(无用)类型转换。

我不认为这两种类型不是分层关系的例子是有道理的。


2
2018-01-19 14:57





我相信答案很简单,你可能不想限制论证 Int 即使你将它视为 Int 在子内。说出于某种原因你希望能够将一个数组乘以一个哈希,但是如果args不能被视为哈希则会失败 Int (即不是 Cool)。

my @a = 1,2,3;
my %h = 'a' => 1, 'b' => 2;
say @a.Int; # 3 (List types coerced to the equivalent of .elems when treated as Int)
say %h.Int; # 2

sub m1(Int $x, Int $y) {return $x * $y}
say m1(3,2); # 6
say m1(@a,%h); # does not match

sub m2(Int(Cool) $x, Int(Cool) $y) {return $x * $y}
say m2('3',2); # 6
say m2(@a,%h); # 6
say m2('foo',2); # does not match

当然,您也可以在没有签名的情况下执行此操作,因为数学运算会自动强制类型:

sub m3($x,$y) {return $x * $y}
say m3(@a,%h); # 6

然而,这会将你的类型检查推迟到sub的内部,这会破坏签名的目的,并阻止你制作sub a multi


0
2018-01-20 15:02





所有亚型 Cool 将(正如酷要求他们)强迫他们 Int。因此,如果您的子内部的操作员或例程仅适用于 Int 参数,您不必添加额外的语句/表达式转换为Int也不需要考虑其他子类型的操作符/例程的代码 Cool。它强制论证将是一个 Int 无论你在哪里使用它都在你的子里面。

你的例子是倒退的:

class Foo { method foomethod { say 'foomethod' } }
class Bar {}

class Quux is Bar {
  method Foo { Foo.new }
}

sub foo(Foo(Bar) $c) {
#= converts $c of type Bar to type Foo
#= returns result of foomethod
  say $c.WHAT;    #-> (Foo)
  $c.foomethod    #-> foomethod
}

foo(Quux.new)

0
2018-01-21 16:53