В чем смысл принуждений, таких как Int(Cool)?
веб-сайт 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)
здесь invocant из foo ограничено для предоставления Foo который может быть преобразован в Bar но foo не может даже вызвать метод Foo on $c типа  is Bar. Так почему бы foo позаботьтесь о том, чтобы принудительный тип был Foo в первую очередь?
может кто-нибудь пролить свет на это? Ссылки на соответствующую документацию и части спецификации также приветствуются. Я не нашел там ничего полезного.
5 ответов
указать тип параметра, без принуждения: 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 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 '123⏏abc'
одна из проблем с общим принуждением любого значения - это такой код:
class City { ... } # City has no Int coercion
my City $city;
double($city);
сбой во время выполнения с сообщением: "метод 'Int' не найден для вызывающего класса 'City'".
укажите тип параметра, с принуждением от прохладных значений:Int(Cool) $x
мы можем выбрать точку равновесия между отсутствием принуждения и общим принуждением любой ценности.
лучший класс для принуждения часто является 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 потому что это тип, который, как ожидается, успешно принудит к Bar значение (или, возможно, не с дружественным сообщением).
может кто-нибудь пролить свет на это? Ссылки на соответствующую документацию и части спецификации также ценятся. Я не нашел там ничего полезного.
документ, который вы первоначально процитировали, - это почти все, что есть для enduser doc. Надеюсь, теперь это имеет смысл, и вы все готовы. Если нет, пожалуйста, прокомментируйте, и мы пойдем оттуда.
что это делает, это принять значение, которое является подтипом прохладный, и пытается преобразовать его в Int. В этот момент is an Int независимо от того, что это было раньше.
так
sub double ( Int(Cool) $n ) { $n * 2 }
действительно можно подумать о том, как (я думаю, что это было фактически реализовано в Ракудо )
# 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);
}
так это принимает любой из Int,  Str, крыса, FatRat, Num, массив, хэш, etc. и пытается преобразовать его в Int перед вызовом &infix:<*>, и 2.
say double '  5  '; # 25
say double 2.5;     # 4
say double [0,0,0]; # 6
say double { a => 0, b => 0 }; # 4
вы можете ограничить его в прохладный вместо любой, как все прохладный значения по существу необходимы для обеспечения принуждения к Int.
( :( Int(Any) $ ) можно сократить до просто :( Int() $ ) )
причина, по которой вы можете это сделать, заключается в том, что вам нужно, чтобы это было Int внутрь, потому что вы вызываете другой код, который делает разные вещи с разными типами.
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 )
Я что-то пропустила? Я не эксперт Perl 6, но, похоже, синтаксис позволяет независимо указывать оба какие типы входных данных допустимы и как входные данные будут представлены функции.
ограничение допустимого ввода полезно, потому что это означает, что код приведет к ошибке, а не к бесшумному (бесполезному) преобразованию типа, когда функция вызывается с бессмысленным параметром.
Я не думаю, что пример, когда два типа не находятся в иерархических отношениях, имеет смысл.
Я считаю, что ответ так же прост, как вы не хотите ограничивать аргумент 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
этот переносит проверку типа на внутреннюю часть суб, что вроде побеждает цель подписи и мешает вам сделать суб multiвсе подтипы 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)