Raku

Материал из Википедии — свободной энциклопедии
(перенаправлено с «Perl 6»)
Перейти к навигации Перейти к поиску
Raku (ранее Perl 6)
Изображение логотипа
Класс языка Мультипарадигмальный
Появился в Разработка ведётся с 2000 года. Первый релиз состоялся 25 декабря 2015 года
Автор Ларри Уолл
Разработчик Ларри Уолл и Одри Тан
Расширение файлов .raku, .rakumod, .rakudoc, .rakutest или .t
Выпуск  (Интерпретатор «Rakudo Star» версии «2017.04» опубликован 1 мая 2017 года)
Система типов динамическая, статическая
Испытал влияние
Повлиял на Perl, Haskell, AntLang
Лицензия GNU General Public License, Artistic License
Сайт raku.org
ОС кроссплатформенность

Raku (от яп. ラクダпроизн. ракуда — верблюд,[1] и от , произн. раку — лёгкость, комфорт, сукха[2][3]) — язык программирования из семейства Perl-подобных языков. Серьёзный пересмотр как дизайна, так и реализации языка Perl, нарушающий обратную совместимость с ним, хотя до 2010 года еще предполагалось наличие режима совместимости.[4]

Подобно языку Perl, Raku оставляет программистам много свободы. Он всё еще позволяет выражаться кратко, в том числе писать однострочники, но также упрощает и написание больших программ, благодаря статической типизации и улучшенной поддержке ООП.

Прежнее название Raku — Perl 6.[5] В течение многих лет в сообществе Perl имели место шуточные замечания о дате релиза. На вопрос «когда выйдет Perl 6» обычным ответом было «на Рождество», но без указания года.[6][7] В 2015 году, то есть после пятнадцати лет ожидания, наконец была анонсирована так называемая «рождественская» версия.[8][9][10]

История[править | править код]

В Perl 6 мы решили, что лучше исправить язык, чем исправлять пользователя.Ларри Уолл[11]

Разработка Perl 6 была впервые анонсирована Ларри Уоллом 19 июля 2000 года, в четвертый день Perl Conference того года,[12] в его выступлении State of the Onion.[13] В то время первоочередными задачами было: удалить из языка «исторические бородавки»; «простые вещи должны оставаться простыми, сложные вещи должны становиться проще, и невозможные вещи должны стать сложными»; общая чистка внутреннего дизайна и API. Процесс начался с серии RFC. Этот процесс был открыт для всех участников, и ни один аспект языка не оставался закрытым для изменений.[14]

Был получен 361 запрос, каждый из которых был рассмотрен Уоллом. Затем он начал процесс написания нескольких «Апокалипсисов» — христианский термин, означающий «раскрытие хороших новостей хорошим людям».[15] Хотя первоначальной целью было написать по одному Апокалипсису для каждой главы книги en:Programming Perl, стало очевидно, что по мере написания каждого Апокалипсиса, предыдущие Апокалипсисы аннулировались более поздними изменениями. По этой причине был опубликован набор Синопсисов, каждый из которых относился к одному Апокалипсису, но включал корректировки из новых Апокалипсисов. Сегодня спецификация Raku управляется набором тестов «roast»,[16] в то время как Синопсисы хранятся в качестве исторической справки.[17]

Есть также серия Экзегез, написанных Дэмиэном Конуэем, которые объясняют содержание каждого Апокалипсиса с точки зрения практического использования. Каждая Экзегеза состоит из примеров кода с обсуждением их использования и значения.[18]

Первоначальные цели и последствия[править | править код]

Основной целью, которую Уолл предложил в своей первоначальной речи, было удаление «исторических бородавок». К ним относилась путаница в сигилах массивов и хэшей, неоднозначность функций select, проблемы с использованием голых[19] (без пунктуации[20]) файловых дескрипторов. Уолл также упомянул в своей речи много других проблем, решение которых Perl-программисты обсуждали годами.[источник не указан 75 дней]

Последствием этих целей стала потеря обратной совместимости. Поскольку обратная совместимость обычно подразумевается при улучшении программного обеспечения, ломающие её изменения в Perl 6 должны были быть явно сформулированы. Постепенно различие между Perl 5 и Perl 6 стало настолько большим, что 12 октября 2019 года Perl 6 был переименован в Raku.[5]

За прошедшие годы вектор развития Raku менялся несколько раз. Введение концепций из Python и Ruby было ранним влиянием.[21][22][23] Кроме того, Pugs, первый интерпретатор Raku, написан на функциональном языке Haskell, и многие элементы функционального программирования были впитаны командой разработчиков Raku.[24][25]

Маскот[править | править код]

Ларри Уолл на фоне бабочки Камелии — талисмана Perl 6

Маскот языка — насекомое Камелия.[26] Его имя — отсылка к эмблеме языка Perl, верблюду («Camel»), а его форма, в традициях любящего каламбуры сообщества Perl, перекликается со словом «bug». Спиральные узоры, вписанные в его крылья, похожие на крылья бабочки, напоминают символы «P6», а расходящееся косоглазие — это преднамеренный каламбур с выражением «Wall-eyed».[27]

Одна из целей живого и красочного дизайна логотипа заключалась в том, чтобы препятствовать мизогинии в сообществе и дать возможность людям с «мужскими убеждениями» показать свою чувствительную сторону.[28]

Реализации[править | править код]

Rakudo (楽土, «рай»)[29][2] — наиболее развитая реализация.[30] Однако это не делает её официальной версией языка. Ни одна реализация не может быть признана главной: всё, что проходит тесты языка, им и является.[31]

Rakudo позволяет выполнять[32] код в виртуальных машинах MoarVM, JVM и на платформе Node.js.[33] MoarVM — виртуальная машина, созданная специально для Rakudo и компилятора NQP.[34][35] Компилятор NQP (Not Quite Perl 6, с англ. — «не совсем Perl 6») реализует подмножество языка, включающее правила Raku для разбора исходного кода, работы с абстрактным синтаксическим деревом и бэкенд-специфичной кодогенерацией. Значительная часть Rakudo написана на Raku и NQP. Rakudo — не самодостаточный компилятор, и в данный момент раскрутка компилятора не планируется.

После версии «2015.02», в Rakudo была прекращена поддержка виртуальной машины Parrot[36] (предшественницы MoarVM), создающейся исходя из ошибочного предположения, что Perl 6 будет похож на Perl 5.[37][38]

Исторические реализации[править | править код]

Первой реализацией Raku был Pugs[39] — интерпретатор и компилятор, написанный на Haskell. Был самой продвинутой реализацией, однако с 2007 года вносились только минимальные исправления для соответствия новым версиям GHC, и по состоянию на ноябрь 2014 года Pugs не поддерживается.[40] В августе 2006 года Yichun Zhang разбил файлы тестов Pugs на фрагменты, прикрепив к ним соответствующие параграфы Синапсисов,[41][42] а в январе 2008 года эти тесты были интегрированы в официальные тесты языка («roast»).[43][44] В феврале 2015 года сообщество объявило тесты языка его спецификацией.[45]

  • «Niecza» — компилятор из Perl 6 в байт-код виртуальной машины «Common Language Runtime» (CLR);
  • «Yapsi» — компилятор, написанный на языке Perl 6 реализации «Rakudo».

Основные отличия от Perl[править | править код]

Raku и Perl отличаются фундаментально, хоть в основном и было намерение оставить Raku Perl'овым. Большая часть изменений предназначены для нормализации языка, чтобы его было легче понять как новичкам, так и опытным программистам, и сделать «простые вещи проще, а сложные — более возможными».

Спецификация[править | править код]

Основное нетехническое различие заключается в том, что Raku начинался как спецификация.[31] Это значит, что при необходимости Raku может быть повторно реализован, а также, что программистам не нужно читать исходный код, чтобы получить полный контроль над любой его возможностью. В случае же языка Perl, официальная документация только описывает поведение текущего интерпретатора. Любые расхождения, обнаруженные между документацией и реализацией, могут привести к тому, что либо одно, либо другое будет изменено, что является движущей силой постоянной разработки и совершенствования выпусков Perl.

Система типов[править | править код]

В Raku динамическая система типов была дополнена статическими типами.[46] Например:

my Int $i = 0;
my Rat $r = 3.142;
my Str $s = "Hello, world";

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

my $i = "25" + 10; # $i is 35

Список формальных параметров подпрограмм[править | править код]

В Perl у подпрограмм нет формальных параметров, хоть простая проверка количества и типов параметров возможна с помощью прототипов подпрограмм.[47] Вместо этого, фактические параметры, передаваемые по ссылке, прячутся за элементами массива @_ в теле подпрограммы.

В Raku появляются настоящие формальные параметры.[48] Например:

sub do_something(Str $thing, Int $other) {
    ...
}

Также, как в языке Perl, в Raku параметры передаются по ссылке, но по умолчанию в Raku они константны, т.е. значения в них не могут быть изменены. Они могут быть явно объявлены как позволяющие менять оригинальное значение (is rw) или как копии (is copy), что эквивалентно передаче по значению.

Способы передачи параметров[править | править код]

В Raku три способа передавать параметры: позиционный, именованный и хлюпающий (slurpy).

Позиционные параметры — это обычный упорядоченный список, как в большинстве языков программирования. Даже они могут быть переданы в произвольном порядке, если указывать их имена. Параметры же, которые могут быть переданы только по имени, обозначаются символом : перед сигилом формального параметра. Хлюпающие параметры — это способ создавать вариативные функции в Raku. Они обозначаются символом * перед сигилом формального параметра. Slurpy-хэш будет захватывать неупомянутые при объявлении подпрограммы именованные параметры, а slurpy-массив — последующие позиционные фактические параметры.

В следующем примере присутствуют все три способа, включая slurpy-массив.

sub somefunction($a, $b, :$c, :$d, *@e) {
    ...
}

somefunction(1, 2, :d(3), 4, 5, 6); # $a=1, $b=2, $d=3, @e=(4,5,6)

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

Блоки и замыкания[править | править код]

Параметры также могут быть переданы в любые блоки кода, которые ведут себя как замыкания. В частности, например, тела циклов for и while являются замыканиями. В следующем примере цикл берёт из списка по три элемента за раз и передаёт их в блок как переменные $a, $b и $c.[49]

for @list -> $a, $b, $c {
    ...
}

Это называется «pointy sub» или «pointy block», и стрелка в нём ведёт себя примерно как ключевое слово sub, вводя анонимное замыкание (или анонимную подпрограмму в терминологии Perl).[48]

Инвариантность сигилов[править | править код]

В Perl, сигилы (знаки пунктуации, находящиеся перед именами переменных) меняются в зависимости от вида использования переменной.

# Perl-код
my @array = ('a', 'b', 'c');
my $element = $array[1];    # возвращает 'b',
my @extract = @array[1, 2]; # возвращает ('b', 'c')
my $element = @array[1];    # возвращает 'b' с предупреждением (с версии 5.10)

В Raku же сигилы инвариантны: как у массива, так и у элемента массива будет один и тот же сигил.[46]

# Raku-код
my @array = 'a', 'b', 'c';
my $element = @array[1];    # в $element записывается 'b'
my @extract = @array[1];    # в @extract записывается ('b')
my @extract = @array[1, 2]; # в @extract записывается ('b', 'c')

Изменчивость сигилов в Perl вдохновлена согласованием числа в английском и многих других естественных языках.

"Это яблоко."                    # $a        верно
"Эти яблоки."                    # @a        верно
"Это третье яблоко."             # $a[3]     верно
"Эти третье яблоко."             # @a[3]     не верно

Однако эта концепция нарушается, когда в игру вступают ссылки, поскольку они могут относиться к структурам данных, являясь в то же время скалярами. Таким образом, работа с вложенными структурами данных может потребовать выражения как единственного, так и множественного числа в одном термине:

# Perl-код: получение массива из элемента хэша.
# В этом хэше хранятся хэши, содержащие массивы.
my @trans_verbs = @{ $dictionary{ 'verb' }{ 'transitive' } };

Подобные конструкции не имеют аналогов в распространённых естественных языках, что вызывает высокую когнитивную нагрузку при написании кода. Тот же код на языке Raku:

# Raku-код: получение массива из элемента хэша.
# В этом хэше хранятся хэши, содержащие массивы.
my @trans_verbs = %dictionary<verb><transitive><>;

Объектно-ориентированное программирование[править | править код]

В языке Perl объектно-ориентированное программирование поддерживается через функцию bless, превращающую любую переменную в объект определенного класса, у которого становятся доступными для вызова методы, объявленные в классе.[50]

Будучи чрезвычайно мощным, этот механизм в то же время делает трудным описание даже самого базового ООП — простых структуро-подобных объектов со связанными процедурами. Кроме того, поскольку Perl не делает никаких предположений об объектной модели, вызов методов не может быть оптимизирован компилятором.

Raku оставляет низкоуровневый метод bless, но также предоставляет более жестко огранивающую объектную модель для распространенных случаев.[51][52] Например, класс, инкапсулирующий точку в декартовой системе координат, может быть определён следующим образом:

class Point is rw {
    has $.x;
    has $.y;
    
    method distance( Point $p ) {
        sqrt(($!x - $p.x) ** 2 + ($!y - $p.y) ** 2)
    }
    
    method distance-to-center {
        self.distance: Point.new(x => 0, y => 0)
    }
}

my $point = Point.new( x => 1.2, y => -3.7 );
say "Позиция точки: (", $point.x, ', ', $point.y, ')';
# Позиция точки: (1.2, -3.7)

# Изменение x и y (используются методы "x" и "y"):
$point.x = 3;
$point.y = 4;
say "Позиция точки: (", $point.x, ', ', $point.y, ')';
# Позиция точки: (3, 4)

my $other-point = Point.new(x => -5, y => 10);
$point.distance($other-point); #=> 10
$point.distance-to-center;     #=> 5

В Perl методы вызываются с помощью стрелки: $object->method(). В Raku вместо стрелки используется точка, как во многих других языках программирования (например, Java и Python).

В терминологии Raku $.x называется атрибутом. Метод, используемый для доступа к атрибуту называется аксессором[53] (от англ. access — доступ). Он создаётся автоматически (при объявлении атрибута через точку[54]) и называется так же, как атрибут. Он работает как геттер и, когда класс или атрибут is rw, приобретает свойства сеттера и может использоваться в левой части присваивания. Вместо автоматических аксессоров, программист может определить свои нестандартные методы. Также, в теле класса ко всем атрибутам, вне зависимости от того, как они объявлены, можно обратиться напрямую, используя синтаксис $!.

Наследование, роли и классы[править | править код]

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

Raku тоже предоставляет наследование c помощью классов, подобных классам в других языках программирования, а также c помощью ролей.

Роли в Raku берут на себя функцию интерфейсов в Java, примесей в Ruby, трейтов[55] в PHP и Squeak (диалекте языка Smalltalk). Они похожи на классы, но обеспечивают более безопасный, чем наследование, механизм композиции, предотвращающий конфликты имён атрибутов и методов.[56][57] Роли не встраиваются в цепочку наследования. Роли относятся к номинальной системе типов (см. en:Nominal type system), а не структурной, которая применяется, например, в Go. Они предоставляют семантические названия наборов поведений и состояний. Фундаментальное различие между ролями и классами в том, что роли не инстанцируют объекты.[58]

Хоть роли и отличаются от классов, возможно написать код, создающий объект из роли. Попытка использовать роль для создания объекта приведёт к созданию класса с тем же именем. В документации Raku этот механизм назван автоматическим каламбурированием ролей, т.к. сгенерированный класс является каламбуром.[59]

В сущности, роли — пачки атрибутов и потенциально абстрактных методов, которые могут быть добавлены в класс без использования наследования. Роль может быть добавлена даже к отдельному объекту. В последнем случае, Raku создаст анонимный подкласс, добавит к нему роль и заменит класс объекта на этот анонимный подкласс.

Например, собака — это млекопитающее, потому что собаки наследуют у млекопитающих некоторые черты, такие как молочные железы и, через их предков позвоночных, позвоночник. С другой стороны, собаки могут обладать разными типами поведения, которые могут меняться со временем. Например, собака может быть домашней, бродячей, быть поводырём. Эти наборы дополнительных поведений могут быть добавлены к собаке. Можно описать их таким образом, чтобы можно было их применять по отношению к другим животным. Например, кошка может быть домашней и бродячей. Собака и кошка отличаются друг от друга, но остаются в общей категории млекопитающих. Итак, Млекопитающее — это класс, Собака и Кошка — классы, унаследованные от млекопитающего. Но вышеназванные поведения — это роли, которые можно добавить в классы или объекты, созданные из классов.

class Млекопитающее is Позвоночное {
    ...
}
class Собака is Млекопитающее {
    ...
}
role Питомец {
    ...
}
role Бездомный {
    ...
}
role Поводырь {
    ...
}

Роли добавляются к классам и объектам с помощью ключевого слова does. Для наследования же используется ключевое слово is. Эти слова отражают различие в смысле этих возможностей языка: присоединение роли наделяет класс поведением роли, но не означает, что этот класс становится буквально тем же самым, что и эта роль.

class СобакаПоводырь is Собака does Поводырь {
    ...
}   # Подкласс присоединяет роль.

my $собака = Собака.new();
$собака does Поводырь;       # Объект присоединяет роль.

И роли, и классы являются типами. Роль может быть использована в объявлении переменной. Например, роль Незрячий может содержать атрибут типа Поводырь, в котором может быть собака-поводырь, лошадь-поводырь, человек-поводырь или даже машина-поводырь.

class Человек {
    has Собака $собака;     # Может содержать любой вид собаки —
    ...                     # не важно, Поводырь это или нет.
}

role Незрячий {
    has Поводырь $поводырь; # Может содержать любой объект с ролью
    ...                     # Поводырь — не важно, Собака это или нет.
}

Регулярные выражения[править | править код]

Регулярные выражения и обработка строк всегда были одними из определяющих особенностей Perl.[60] Поскольку шаблоны Perl в определенный момент превзошли возможности регулярных выражений,[61] документация Raku называет их просто регексами, дистанцируясь от формального определения регулярных выражений.

Raku расширяет набор функций Perl в отношении регулярных выражений, вкладывая их в бо́льший фреймворк, называемый правилами.[62] Правила предоставляют возможности контекстно-зависимого парсинга (такие как синтаксический предикат из PEG и ANTLR) и ведут себя как замыкания относительно своих лексических областей видимости.[63] Правила вводятся с помощью ключевого слова rule, использование которого похоже на определение подпрограммы. Анонимные же правила могут быть введены с помощью ключевого слова regex (или rx), или их можно описывать как регулярные выражения в Perl — с помощью операторов m (сопоставление) или s (замена).

В Апокалипсисе 5 Ларри Уолл перечисляет 20 проблем текущей «культуры использования регулярных выражений». В числе прочего, регулярные выражения Perl были «слишком компактными и „милыми“», они «слишком надеялись на слишком небольшой набор специальных символов», у них была «слабая поддержка именованного захвата», «слабая поддержка грамматик» и «плохая интеграция с языком».[64]

Синтаксические упрощения[править | править код]

Некоторые конструкции Perl в Raku были изменены и оптимизированы для других синтаксических оборотов. Например, круглые скобки, которые были обязательны в инструкциях порядка выполнения, теперь опциональны.[49]

if is-true() {
    for @array {
        ...
    }
}

Оператор , (запятая) теперь является конструктором списков, поэтому скобки вокруг списков не требуются.

@array = 1, 2, 3, 4;

Цепочки сравнений[править | править код]

Raku разрешает следующие выражения:

if 20 <= $temperature <= 25 {
    say "Температура в комнате — от 20 до 25 градусов!"
}

Это воспринимается как последовательные сравнения слева направо с последующим объединением через логическое И.

Ленивые вычисления[править | править код]

Raku использует технику ленивых вычислений списков подобно некоторым функциональным языкам, таким как Haskell:[65]

@integers = 0..Inf; # целые числа от нуля до бесконечности

Этот код не выдаст ошибку, пытаясь поместить бесконечный список в массив @integers, и не зависнет, пытаясь бесконечно увеличивать список, если запрашиваться будет конечное число элементов.

Это упрощает многие распространенные в Raku задачи, в том числе операции ввода-вывода, трансформации списков и передачу параметров.

Возможно также создание ленивых списков с помощью ключевых слов gather и take. Они похожи на генераторы в языках Icon и Python.

my $squares = lazy gather for 0..Inf {
    take $_ * $_;
};

Здесь $squares — это бесконечный список квадратов натуральных чисел (включая ноль), но благодаря ленивой природе этого списка, элементы вычисляются только тогда, когда к ним происходит доступ.[66]

Скрещения[править | править код]

Raku вводит концепцию скрещений[67] (англ. junction — соединение, место пересечения; в Raku этот термин имеет также отношение к конъюнкции и дизъюнкции[65]). Это суперпозиция нескольких значений.[65] В простейшем виде, скрещение создаётся операторами скрещения:

# Пример скрещения типа | ("any"):
my $color = 'белый';
unless $color eq 'белый' | 'чёрный' | 'серый' {
    die "Печать этим цветом не поддерживается.\n";
}

# Пример скрещения типа & ("all"):
my $password = 'secret!123';
if $password ~~ /<:alpha>/ & /<:digit>/ & /<:punct>/ {
    say "Ваш пароль достаточно надёжен.";
}

Оператор | выражает значение равное либо левому, либо правому аргументу, оператор & — одновременно и левому, и правому. Эти значения могут быть использованы в коде везде, где по смыслу предполагается одно значение. Любые операции со скрещением действуют одновременно на все его составляющие, и результат объединяется через оператор этого скрещения. Так, ("apple"|"banana") ~ "s" вернёт "apples"|"bananas". Однако в булевом контексте скрещения возвращают лишь одно значение — истину или ложь: any возвращает истину, если сравнение истинно для хотя бы одного элемента; all возвращает истину, если сравнение истинно для всех элементов.[68]

Используя скрещения, можно дополнить систему типов некой формой обобщенного программирования, ограничивающей переменные скрещениями типов.

subset Color of Any where RGB_Color | CMYK_Color;
sub get_tint(Color $color, Num $opacity) {
    ...
}

Скрещения — это особые объекты, разделяющие выполнение кода на потенциально-параллельные потоки. И они созданы специально для применения в булевом контексте: нельзя получить доступ к их содержимому непосредственно, без конвертации в строку, что отличает их, например, от множеств и других коллекций.[68]

Макросы[править | править код]

В низкоуровневых языках, макросы стали синонимом текстовой замены в исходном коде из-за ассоциаций с препроцессором языка Си. Однако в высокоуровневых языках, таких как Лисп, который появился раньше Си, макросы были более мощными.[69] Этой лиспоподобной концепцией макросов Raku и воспользовался.[48] Мощность этого типа макросов базируется на оперировании программой как высокоуровневой структурой данных, а не текстом, и на доступе ко всем возможностям языка.

Определение макроса в Raku выглядит подобно определению подпрограммы или метода. И оперировать такой макрос может и исходным кодом, и абстрактным синтаксическим деревом, и комбинацией этих двух вещей.

macro hello($what) {
    quasi { say "Hello { {{{$what}}} }" };
}

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

Идентификаторы[править | править код]

В Raku идентификаторы, помимо букв, цифр и подчеркиваний, могут также содержать апострофы и дефисы, при условии, что после них обязательно идут буквы. Буквы включают «соответствующие» (какие — зависит от реализации) символы Юникода, которые в Rakudo и MoarVM определены как все символы Юникода категории «L».[70]

Использование дефисов вместо подчеркиваний называется «kebab case».[71][72][73]

Метаоператоры[править | править код]

Метаоператоры — операторы, параметризующиеся другими операторами, подобно тому, как функции могут принимать другие функции в качестве параметров.[74]

Метаоператор присваивания[править | править код]

Perl унаследовал такие операторы языка Си, как +=, *= и т.п. Raku обобщает их до метаоператора. Для любого бинарного оператора op мы можем написать:

$x op= $y;  # или $x [op]= $y

Что значит:

$x = $x op $y;

Причем, это работает и для операторов, определённых пользователем.

Гипероператоры[править | править код]

Они похожи на оператор map в Perl. Заставляют операторы работать со всеми значениями массива. Могут применяться как к бинарным, так и к унарным операторам.[75]

Например, следующий код создаст массив, содержащий все элементы массива @a, увеличенные на единицу:

my @aPlusOne = @a »+» 1;    # или @a >>+>> 1

Направление угловых скобок влияет на поведение при передаче в качестве параметров массивов разной длины.[75] Эти «стрелки» показывают, откуда берётся длина результата операции.

Пример использования гипероператора с унарным оператором:

my @a = 1, 2, -3;
my @b = -<<@a;      # [-1 -2 3]

Гипероператоры работают не только для плоских, но и для вложенных массивов.[76]

Метаоператор редукции[править | править код]

Метаоператор редукции может использоваться с любым инфиксным оператором, преобразуя его в оператор свёртки списка. Это похоже на то, как если бы оператор был применён к первым двум элементам, затем к получившемуся значению и третьему элементу и т.д., пока не останется только одно значение. В качестве оператора-параметра может выступать сумма, произведение, максимум, минимум и т.д. Это чрезвычайно мощный механизм, который, например, переводит оператор + в оператор суммы списка, как в примере ниже.

say "Сумма целых чисел от 0 до 99 равна: ", [+] ^100;

Кросс-оператор[править | править код]

Ведёт себя одновременно и как оператор, и как метаоператор.[77]

Кросс-оператор[75] находит прямое произведение списков, упорядоченное таким образом, что перечисление элементов из правого операнда происходит быстрее, чем из левого,[77] возвращая последовательность списков:

1..3 X <a b c> X <d e f>;
# ((1 a d) (1 a e) (1 a f) 
#  (1 b d) (1 b e) (1 b f)
#  (1 c d) (1 c e) (1 c f)
#  (2 a d) (2 a e) (2 a f)
#  (2 b d) (2 b e) (2 b f)
#  (2 c d) (2 c e) (2 c f)
#  (3 a d) (3 a e) (3 a f)
#  (3 b d) (3 b e) (3 b f)
#  (3 c d) (3 c e) (3 c f))

Метаоператор сворачивает внутренние списки с помощью оператора-параметра:[77]

1..3 X~ <a b c> X~ <d e f>;
# (1ad 1ae 1af 1bd 1be 1bf 1cd 1ce 1cf
#  2ad 2ae 2af 2bd 2be 2bf 2cd 2ce 2cf
#  3ad 3ae 3af 3bd 3be 3bf 3cd 3ce 3cf)

Зип-оператор[править | править код]

От англ. zipper — застёжка-молния.[78]

Аналогично кросс-оператору, совмещает элементы списков,[75] но возвращает последовательность, содержащую сначала первые элементы каждого списка, затем вторые элементы каждого списка и т.д.[79]

<a b c> Z <1 2 3 4>;
# ((a 1) (b 2) (c 3))

100, 200 Z+ 42, 23;
# (142 223)

Обратные операторы[править | править код]

Метаоператор R (англ. reversed) позволяет менять местами аргументы исходного оператора.

say "Один делить на три равно ", 3 R/ 1;

Вложенность метаоператоров[править | править код]

Результатом применения метаоператора к оператору является другой оператор, к которому снова может быть применен метаоператор и т.д. Для устранения неоднозначности в синтаксисе разрешено использовать квадратные скобки.[80]

my @a = 1, 2, 3;
my @b = 5, 6, 7;

@a >>>>> @b;        # Ошибка разбора.
@a >>[>]>> @b;      # [False False False]
# Здесь гипероператор >> >> применяется к оператору сравнения.

# Кросс-метаоператор применяется
# к метаоператору присваивания, параметризованному
# оператором сложения:
@a X[+=] @b;        # (6 12 19 7 13 20 8 14 21)

# Из-за кросс-метаоператора, присваивание делалось
# для каждого элемента массива @a с каждым элементом массива @b,
# что эквивалентно прибавлению к каждому элементу массива @a
# суммы элементов массива @b:
say [+] @b;         # 18
say @a;             # [19 20 21]

Конкурентность и параллелизм[править | править код]

Подобно другим современным языкам, Raku спроектирован для поддержки параллелизма и асинхронного программирования.

Raku предоставляет простой модульный высокоуровневый API для написания конкурентного кода, независящий от способов реализации этого API виртуальной машиной. Кроме того, некоторые функции языка могут неявно работать асинхронно. Чтобы обеспечить управляемость и совместимость между этими функциями, пользовательскому коду следует избегать, насколько это возможно, использования низкоуровневых интерфейсов (потоков, планировщиков, блокировок).[81]

Центральным высокоуровневым механизмом являются промисы[82][83] (англ. Promise — обещание), представляющие собой результаты вычислений, полученные до их фактического завершения. Их можно давать, выполнять и нарушать. Таким образом, они обладают трёмя возможными состояниями. Сила этого механизма заключается в возможности их комбинировать и соединять в цепочки:

my $p1 = Promise.new();
my $p2 = $p1.then({ say "Результат второго промиса."});

Здесь then обеспечивает выполнение $p2 лишь после выполнения $p1.

Есть также англ. Supplies («запасы») и англ. Suppliers («поставщики») — механизм создания асинхронных потоков данных с возможностью обработки каждого сообщения сразу несколькими получателями, подобно тому, как работают обработчики событий в других языках программирования.[81] Этот механизм можно использовать для событийно-ориентированного программирования.

Наконец, каналы (англ. Channels) — это потокобезопасные FIFO-очереди, подобные именованным конвейерам в операционных системах, но функционирующие в рамках текущего процесса. Ключевое отличие от Supply в том, что чтение из канала является удалением из очереди,[81] т.е. одно значение может быть прочитано лишь единожды.

Примеры[править | править код]

Hello world[править | править код]

Программа «Здравствуй, мир!» часто используется для демонстрации базового синтаксиса языка. На языке Raku она выглядит так:

say 'Hello, world';

Хотя, конечно, есть больше одного способа выразить это.[84]

Факториал[править | править код]

Вычисление факториала, определённое несколькими способами:

# С использованием рекурсии с конструкцией «if-else».
sub fact( UInt $n --> UInt ) {
    if $n == 0 { 1 }
    else       { $n * fact($n-1) }
}

# С использованием рекурсии с «if»
# в качестве модификатора выражения.
sub fact( UInt $n --> UInt ) {
    return 1 if $n == 0;
    return $n * fact($n-1);
}

# С использованием рекурсии с конструкцией «when».
sub fact( UInt $n --> UInt ) {
    when $n == 0 { 1 }
    default      { $n * fact($n-1) }
}

# С использованием тернарного оператора.
sub fact( UInt $n --> UInt ) {
    $n == 0 ?? 1 !! $n * fact($n-1)
}

# С использованием множественной диспетчеризации.
multi fact(0) { 1 }
multi fact( UInt $n --> UInt ) {
    $n * fact($n - 1)
}

# С использованием метаоператора редукции.
sub fact( UInt $n --> UInt ) {
    [*] 1..$n
}

# Определение оператора факториала,
# реализованного через метаоператор редукции.
sub postfix:<!>( UInt $n --> UInt ) { [*] 1..$n }

# С использованием ключевого слова «state» для мемоизации.
sub fact( UInt $n --> UInt ) {
    state %known = 0 => 1;
    return %known{$n} if %known{$n}:exists;
    %known{$n} = $n * fact($n-1);
    return %known{$n};
}

QuickSort[править | править код]

QuickSort — известный алгоритм сортировки. Его реализация, использующая функциональную парадигму, может быть лаконично записана[a] так:

# Отсортированный пустой список — это пустой список.
multi quicksort([]) { () }

# Иначе, берём первый элемент в качестве опорного...
multi quicksort([$pivot, *@rest]) {
    # Делим элементы на список тех,
    # которые меньше опорного, и те,
    # которые больше опорного.
    my @before = @rest.grep(* before $pivot);
    my @after  = @rest.grep(* after $pivot);

    # Сортируем эти подсписки и объединяем результат.
    flat (quicksort(@before), $pivot, quicksort(@after))
}
  1. Если компилятор не сделает что-нибудь загадочное за кулисами, максимальная глубина рекурсии здесь — длина списка, что делает эту реализацию непригодной для больших данных. Глубина рекурсии может быть сокращена до log2(длина_списка), если использовать рекурсию только с небольшими подсписками before и after, и циклы в других случаях. Также, в этой реализации дублирующиеся значения исходного списка появятся в выводе лишь единожды, т.к. оба сравнения в параметрах метода grep строгие.

Ханойская башня[править | править код]

Эта головоломка часто используется для знакомства с рекурсией в информатике. Данная реализация использует мультиметоды с литералом 0 как часть сигнатуры.

multi sub hanoi(0, $, $, $) { }                         # Нет дисков. Нечего перекладывать.
multi sub hanoi($n, $a = 'A', $b = 'B', $c = 'C') {     # Начать с $n дисками и тремя стержнями A, B, C.
    hanoi $n - 1, $a, $c, $b;                           # Переместить ($n - 1) дисков с A на B
    say "Переместить диск $n с $a на $c.";              # Затем переместить последний диск с A на C.
    hanoi $n - 1, $b, $a, $c;                           # Наконец, переместить ($n - 1) дисков с B на C.
}

hanoi(5);   # Решение для пяти дисков.

См. также[править | править код]

Примечания[править | править код]

  1. Definition of ラクダ (англ.). JapanDict: Japanese Dictionary.
  2. 1 2 Perl 6 becomes Raku - but what does this mean? (англ.). Edument. — «It means “comfort” or “ease” in Japanese, which nicely reflects the goals of the language to be a comfortable programming experience - as mentioned earlier, often at the expense of those doing the language implementation! The most popular compiler for the language is named “Rakudo”, which approximately means “way of the camel” (the camel being a symbol commonly associated with Perl) and also “paradise”. Thus, we can see it as a way to “do” the Raku language.».
  3. Definition of 楽 (англ.). JapanDict: Japanese Dictionary. — «Buddhism: sukha (happiness)».
  4. Perl 6 FAQ (англ.). — «Perl 6 will provide a "Perl 5 compatibility mode", allowing the compiler to directly execute any code that it recognizes as being written in Perl 5. [...] In Q2 2010 Patrick Michaud will release a useful and usable (but not feature complete) Perl 6 compiler...».
  5. 1 2 Larry has approved renaming Perl 6 to Raku
  6. Perl 6 Summary for the week ending 20030713. Perl 6 Rules at OSCON. (англ.). Perl 6 Archive (13 July 2003). — «Damian spoke about Perl6::Rules, his implementation of Perl 6's rules system in pure Perl 5. [...] he told us [...] that the module would be completed and released to CPAN as time/money allowed and would be out by Christmas. He didn't say which Christmas.».
  7. Perl Humour: Perl 6 and Vapourware (англ.). Perl Beginners' Site. — «<anonuser> You know for when they finally decide to release that programatic abortion they call perl 6 <rindolf> anonuser: on Christmas. <rindolf> anonuser: don't know which one.». Архивировано 8 апреля 2012 года.
  8. Perl 6 goes live — Larry Wall on Twitter
  9. OpenNET: Состоялся рождественский релиз Perl 6 (первый релиз языка)
  10. Christmas is here.
  11. Federico Biancuzzi. Masterminds of Programming: Conversations with the Creators of Major Programming Languages / Federico Biancuzzi, Shane Warden. — 2009. — ISBN 978-0596515171.
  12. Kline, Joe. Report from the Perl Conference (21 августа 2000).
  13. Wall, Larry. State of the Onion 2000. O'Reilly Network (23 октября 2000).
  14. The Perl Foundation. About Perl 6 RFCs.
  15. Wall, Larry. Apocalypse 1: The Ugly, the Bad, and the Good (2 апреля 2001).
  16. Raku test suite.
  17. Larry Wall and the Perl 6 designers. Perl 6 Design Documents.
  18. The Perl Foundation. Exegeses.
  19. Barewords - "голые" слова в Perl.
  20. FIO00-PL. Do not use bareword file handles. (англ.). SEI CERT Perl Coding Standard. Carnegie Mellon University (16 November 2017).
  21. Wall, Larry. Apocalypse 3: Operators (англ.). Perl 6 Archive (2 October 2001).
  22. Wall, Larry. Apocalypse 4: Syntax (англ.). Perl 6 Archive (18 January 2002).
  23. Wall, Larry. Apocalypse 12: Objects (англ.). Perl 6 Archive (13 April 2004).
  24. Simon Proctor (‎Scimon‎). Day 19 – Functional Programming with Raku (англ.). Raku Advent Calendar (19 December 2019).
  25. A Plan for Pugs (англ.). Perl.com (3 March 2005). — Интервью с Autrijus Tang (Одри Тан). — «chromatic: Have you started giving Haskell tutorials? I know Larry and Patrick have started to pick up some of it. I’m pretty sure Luke and Damian have already explored it (or something from the same family tree). Autrijus: I think I’ve read a paper from Damian that says he taught Haskell in monash. It’s before the monadic revolution though. chromatic: If not Haskell, certainly something from the ML family. Autrijus: Right. So, I’ve been pointing people to YAHT and #Haskell. chromatic: It sounds like you’re attracting people from both sides of the fence then. Autrijus: It indeed is. I get svn/svk patches and darcs patches.».
  26. Camelia (англ.).
  27. Larry Wall in IRC chat log (недоступная ссылка) (15 January 2016). Дата обращения: 10 ноября 2017. Архивировано 8 апреля 2016 года. (см. код страницы)
  28. Archived "Logo considerations" email from Larry Wall (24 March 2009). Дата обращения: 10 ноября 2017.
  29. 楽土(らくど) の意味 (яп.). goo国語辞書. — «心配や苦労がなく楽しい生活ができる土地。».
  30. Download Raku: «Rakudo is currently the most actively developed and mature implementation of Raku».
  31. 1 2 Wall, Larry. Synopsis 1: Overview (10 августа 2004).
  32. GitHub Issues: compile to jar files / moarvm files — обсуждение возможности распространения скомпилированного байт-кода.
  33. Getting started with node.js rakudo.js
  34. Worthington, Jonathan MoarVM: A virtual machine for NQP and Rakudo. 6guts. Дата обращения: 24 июля 2013.
  35. MoarVM. MoarVM team. Дата обращения: 8 июля 2017.
  36. Development Release #85 (“Berlin”): «this is the last release of Rakudo that supports Parrot as a backend for the foreseeable future».
  37. YAPC::Europe 2013 “Future Perl”: MoarVM: a metamodel-focused runtime for NQP and Rakudo By Jonathan Worthington (‎jnthn‎): «We know what we need now» (шестой слайд)
  38. YAPC::Europe 2013 “Future Perl”: MoarVM: a metamodel-focused runtime for NQP and Rakudo By Jonathan Worthington (‎jnthn‎) — страница выступления на сайте конференции с видео
  39. Pugs — Perl 6 user's golfing system.
  40. Feature comparison of Perl 6 compilers. Архивировано 7 февраля 2019 года.
  41. Integrating the Pugs test suite into the Synopses (англ.) (22 August 2006). — «Well, in short, we have divided the .t files in the Pugs test suite into pieces and inserted every resulting snippet after the corresponding paragraph of the Synopses.».
  42. agentz. Raku / mu: [t/statements/do.t] (англ.). GitHub (16 August 2006).
  43. [gsoc_spectest moved do.t into spec, added tests, fudged for rakudo (added 3 tests)] (англ.). GitHub.
  44. Search · pugscode.org (англ.). GitHub. — «4,590 commit results in Raku/roast».
  45. Interview with Audrey Tang (англ.). Andrew Shitov (5 May 2015). — «In August 2006, Yichun Zhang improved the continuous integration system, displaying tests inline with the spec, as detailed in this writeup. This makes the tests part of the spec, not particular to Pugs. In January 2008, Larry implemented a fudge program that adapts the test suite to work on new implementations such as SMOP and Rakudo. In February 2015, the community declared the tests at perl6/roast as the actual specification...».
  46. 1 2 Wall, Larry. Synopsis 2: Bits and Pieces (20 мая 2009).
  47. WDH: PERL - Подпрограммы (6.6.7. Прототипы).
  48. 1 2 3 Wall, Larry. Synopsis 6: Subroutines (21 марта 2003).
  49. 1 2 Wall, Larry. Synopsis 4: Blocks and Statements (20 мая 2009).
  50. Объектно-ориентированное программирование в Perl 5. Записки программиста (7 апреля 2011).
  51. Wall, Larry. Synopsis 12: Objects (18 августа 2006).
  52. Classes and objects. Constructors. (англ.). Raku Documentation.
  53. Кроссбраузерные аксессоры в JavaScript. Хабр (20 апреля 2011).
  54. Object orientation. Classes. Attributes. (англ.). Raku Documentation.
  55. The Software Composition Group. Traits. Дата обращения: 22 сентября 2006. Архивировано 11 августа 2006 года.
  56. Jonathan Worthington. Day 18: Roles.
  57. Роли. Введение в Raku.
  58. chromatic. The Why of Perl Roles.
  59. Object orientation. docs.raku.org. Дата обращения: 22 августа 2021.
  60. Parlante, Nick. Essential Perl: String Processing with Regular Expressions.
  61. Christiansen, Tom. PERL5 Regular Expression Description. — «Perl's regexps "aren't" -- that is, they aren't "regular" because backreferences per sed and grep are also supported, which renders the language no longer strictly regular». Дата обращения: 25 марта 2010. Архивировано 31 марта 2010 года.
  62. Grammars. Rules. (англ.). Raku Documentation.
  63. Wall, Larry. Synopsis 5: Regexes and Rules (20 мая 2009).
  64. Wall, Larry. Apocalypse 5: Pattern Matching (4 июня 2002).
  65. 1 2 3 Wall, Larry. Synopsis 9: Data Structures (13 сентября 2004).
  66. Control flow: gather/take (англ.). Raku Documentation. — «The gather/take combination can generate values lazily, depending on context. If you want to force lazy evaluation use the lazy subroutine or method. Binding to a scalar or sigilless container will also force laziness.».
  67. 8.6. Скрещение. Введение в Raku.
  68. 1 2 class Junction (англ.). Raku Documentation.
  69. Lamkins, David B. Successful Lisp: How to Understand and Use Common Lisp. — bookfix.com, 2004-12-08.
  70. syntax identifiers (англ.). Raku Documentation.
  71. Lodash “Array” Methods: kebabCase (англ.).
  72. Prop Casing (camelCase vs kebab-case) (англ.). Vue.js.
  73. Ivan Grishaev. Configuration in Clojure. Environment Variables in Clojure. (англ.) (6 May 2021).
  74. Operators: Metaoperators (англ.).
  75. 1 2 3 4 Андрей Шитов. Метаоператоры в Perl 6. Pragmatic Perl (27 мая 2015).
  76. Hyper operators (англ.). Raku Documentation.
  77. 1 2 3 Operators. List infix precedence: infix X (англ.). Raku Documentation.
  78. Kurt Nørmark. Functional Programming in Scheme. Reduction and zipping (англ.). Department of Computer Science, Aalborg University, Denmark. — «The zipping function is named after a zipper, as known from pants and shirts.».
  79. Operators. List infix precedence: infix Z (англ.).
  80. Operators. Metaoperators. Nesting of metaoperators. (англ.). Raku Documentation.
  81. 1 2 3 Concurrency (англ.). Raku Documentation.
  82. Промисы. Современный учебник JavaScript (15 июля 2020).
  83. Promise. MDN. — «исполнение промиса протоколируется при помощи продолжения p1.then. Это показывает как синхронная часть метода отвязана от асинхронного завершения промиса».
  84. Raku - Difference between print, put and say? (англ.). Stack Overflow.
  85. People of Perl 6: Carl Mäsak (англ.). Perl.com (31 August 2010). — Интервью с разработчиком Rakudo по имени Carl Mäsak.. — «I’ve also gained a new respect for what a “holistic” process the design of a language such as Perl 6 can be sometimes. Whether some feature turns out to be a good idea is determined by dozens of minute interactions in the spec, not all of them “local”, and some of them outright emergent.».
  86. Larry Wall. Perl, the first postmodern computer language (англ.). Perl.com (9 March 1999). — Текст выступления Ларри Уолла на Linux World третьего марта 1999 года.

Литература[править | править код]

Книги, опубликованные до релиза языка (до версии 6.c)[править | править код]

Ссылки[править | править код]