После нескольких лет перерыва в программировании я пытаюсь снова набрать скорость, изучая Perl 6. Эта серия статей задумана как своего рода отчет о прогрессе, демонстрирующий не только то, что я узнал, но и все мои недоразумения и ошибки.

Те из вас, кто знаком с Perl 5, знают, что он сильно отличается от большинства других языков при объявлении подпрограмм. При объявлении подпрограммы / метода вы вообще не объявляете параметры.

Другие языки, подобные C, заставляют вас делать это; даже Python делает. Но когда дело доходит до таких вещей, Perl 5 придерживается принципа невмешательства:

sub a_sub {
    # You can call this method with an arbitrary
    # amount of parameters. They will all be stored
    # in the array @_.
    print $_[0] + $_[0];
}
&a_sub(1, 2); # Output: 3

Это нормально, если вы единственный программист, но если вы команда или через некоторое время пересматриваете свой собственный код, под-объявление дает несколько подсказок или подсказок как для интерпретатора, так и для других программистов о том, как вызывать it: вы не знаете, сколько параметров принимает метод, и какие типы параметров должны быть (например, целые числа, строки, даты и т. д.).

Кроме того, нет ничего, что мешало бы вам изменить значения параметров внутри самого подпрограммы.

sub a_sub {
    $_[1] *= 2;
    print $_[0] + $_[1];
}
&a_sub(1, 2); # Output: 5

Так что по замыслу вы можете нанести здесь вред.

Perl 6 держит вашу руку несколько больше - если вы захотите. Как и раньше, массив @_ доступен и в Perl 6, но вы также можете объявить подпись более явно:

sub a-sub($param1, $param2) {
    say $param1 + $param2;
} 
a-sub(1, 2); # Output: 3

Вы даже можете ввести их:

sub a-sub(Int $param1, Int $param2) {
    say $param1 + param2;
}
a-sub(1, 2);   # Output: 3
a-sub(1.01, 2); # Throws an error

Пока что ничем не отличается от других C-подобных языков. Но теперь начинается самое интересное!

sub a-sub(Int $param1 where * > 0, Int $param2 where * % 2 == 0) {
    say $param1 + param2;
}
a-sub(1, 2);  # Output: 3
a-sub(0, 2);  # Throws an error because $param1 has to be > 0
a-sub(1, 3);  # Throws an error because $param2 has to be even

Таким образом, вы можете не только проверять типы, но и определять ограничения. На других языках вы должны программировать проверки, генерировать исключения и создавать обработку исключений, чтобы получить такие функции. Здесь вы получаете все это, а также сообщения об ошибках, которые пытаются указать, в чем заключается ваша ошибка (если таковая имеется).

Вы даже можете установить ограничения на основе ранее объявленных параметров, например:

sub a-sub(Int $param1, Int $param2 where * > $param1) {
    say $param1 + $param2;
}

a-sub(1, 2); # Output: 3
a-sub(2, 1); # Throws an error

Мощная штука!

Но что произойдет, если вы попытаетесь манипулировать параметром внутри подпрограммы, т.е. как мы это делали в Perl 5, примере №2 выше?

sub a-sub($param1, $param2) {
    $param2 *= 2;
    say $param1 + $param2;
}
a-sub(1,2); # Throws an error: 
            # Cannot assign to a readonly variable ($param2)

Но Perl 6 открыт для представления о том, что вы действительно знаете, что делаете. Так что, если вам действительно нужно такое поведение, вы можете получить его, явно указав интерпретатору, что вы хотите:

sub a-sub($param1, $param2 is copy) {
    $param2 *= 2;
    say $param1 + $param2;
}
a-sub(1,2); # Output: 5

В этом смысле Perl 6 противоположен многим другим языкам в том, что вы должны объявить что-то копией; в других случаях вы должны заявить об этом, если это не так.

А теперь другой сценарий. Допустим, вы хотите передать значение по ссылке, чтобы операции, выполняемые в подпрограмме, отражались переменной, которую вы использовали в качестве значения параметра.

sub a-sub($param1, $param2 is rw) {
    $param2 *= 2;
    say $param1 + $param2;
}
my $value = 2;
a-sub(1, $value); # Output: 5
say $value;       # Output: 4

Подпрограмма может даже включать подпрограммы, которые доступны только внутри этих подписок. Эти «частные» подпрограммы имеют доступ к переменным, определенным в контексте внешней подпрограммы:

sub a-sub($param1, $param2) {
    sub max-val() {
        return $param2 > $param1 ?? $param2 !! $param1;
    }
    say "Of these, " ~ max-val() ~ " is biggest, and sum is " ~
        $param 1 + $param2;
}
a-sub(1, 2); # Output: Of these 2 is biggest, and sum is 3;
max-val();   # Throws an error: Undefined routine

Несмотря на то, что @_ доступен, вы также можете явно объявить аналогичные массивы:

sub a-sub(*@a) { # What this really says: Store all, if any,
                 # parameters in an array for me, i.e. = @_
    say @a.join(", ");
}

a-sub(1, 2, 3, 4, 5); # Output: 1, 2, 3, 4, 5

Что хорошо в возможности самостоятельно объявить такой "slurpy" массив, так это то, что вы можете смешивать именованные и slurpy параметры:

sub a-sub($param1, $param2, *@a) {
    say "Sum is " ~ $param1 + $param2 ~ " and rest is " ~ 
        @a.join(", ");
}

a-sub(1, 2, 3, 4, 5); # Output: Sum is 3 and rest is 3, 4, 5

Вы хотите вызвать что-либо из этого как именованные параметры? Конечно вы можете; вы даже можете задать для параметров значения по умолчанию, чтобы вам не приходилось передавать значения, если значения по умолчанию соответствуют вашим требованиям.

sub a-sub(:$param1 = 1, :$param2 = 2, *@a) {
    say "Sum is " ~ $param1 + $param2 ~ " and rest is " ~
        @a.join(", ");
}

a-sub(param1 => 1, param2 => 2, 3, 4, 5);                                                    
a-sub(3, 4, 5); 
# Both output: Sum is 3 and rest is 3, 4, 5

Вы даже можете сделать их жидкими хешами:

sub a-sub(:$a1, *%h) {
    say %h;
}
a-sub(a1=>1, a2=>2, a3=>3, a4=>4);
# Output: {a2 => 2, a3 => 3, a4 => 4}

И наконец ... несколько подписок с одинаковым именем, но с разными подписями:

multi a-sub($param1, $param2) {
    say "Sub 1: " ~ $param1 + $param2;
}
multi a-sub($param1, $param2, $param3) {
    say "Sub 2: " ~ $param1 + $param2 + $param3;
}
multi a-sub(*@params) {
    say "Sub 3: " ~ [+] @params;
}
a-sub(1, 2);             # Output: Sub 1: 3
a-sub(1, 2, 3);          # Output: Sub 2: 6
a-sub(1, 2, 3, 4, 5, 6); # Output: Sub 3: 21
a-sub(1..7);             # Output: Sub 3: 28

Возможностей много, и ошеломляющие. Но при правильном использовании они в сумме превосходят то, что вы использовали раньше, или даже превосходят его. Я, например, люблю это.