PragmaticPerl34pragmaticperl.com/issues/34/pragmaticperl-34-ebook.pdf · http/2 и никакой...

Preview:

Citation preview

Pragmatic Perl 34

pragmaticperl.com

Выпуск 34. Декабрь 2015

Другие выпуски и форматы журнала всегдаможно загрузить с pragmaticperl.com. С во-просами и предложениями пишите на почтуeditor@pragmaticperl.com.

Комментарии к каждой статье есть в html-версии. Подписаться на новые выпуски можнопо ссылке pragmaticperl.com/subscribe.

Авторы статей: Владимир Леттиев, Андрей Ши-тов, Денис Федосеев

Обложка: Марко Иванык

Корректор: Андрей Шитов

Выпускающий редактор: Вячеслав Тиханов-ский

Ревизия: 2015-12-31 10:36

© «Pragmatic Perl»

Оглавление

1 От редактора . . . . . . . . . . 1

2 Впечатления от воркшопаSaint Perl 2015 . . . . . . . . . . 2

3 Взгляд на 2015 г. . . . . . . . . 12

4 Управление модулями и пре-компиляция в Perl 6 . . . . . . 16

5 Perl 6-винегрет . . . . . . . . . 32

6 Использование Rust из Perl . 47

7 Обзор CPAN за ноябрь 2015 г. 90

8 Интервью с Дмитрием Ша-матриным . . . . . . . . . . . . 105

1 От редактора

Всех читателей поздравляем с наступаю-щими праздниками и желаем интересныхпроектов в новом году!

Друзья, журнал ищет новых авторов. Неупускайте такой возможности! Если у васесть идеи или желание помочь, пожалуй-ста, свяжитесь с нами.

Приятного чтения.

■ Вячеслав Тихановский

2 Впечатления от воркшопа Saint Perl2015

Не изменяя традиции 19 декабря 2015 г. про-шёл седьмой ежегодный воркшоп Saint Perlдля всех любителей и профессионалов мираPerl в Санкт-Петербурге.

Дляменя незабываемые впечатления о кон-ференции начались ещё с момента прибы-тия в Санкт-Петербург: затянутое серымиоблаками небо, моросящий дождь и +6ºC.Необычно для середины декабря, да и по-следующие дни ставили температурные ре-корды.

В этом году конференция проходила наплощадке, предоставленной генеральнымпартнёром конференции — компаниейDataArt. На сайте конференции зареги-

стрировались 53 человека, причём трое изних сделали это уже в день конференции.Не все сразу смогли найти вход в здание,поэтому за 10 минут до начала зал был ещёпочти пустой, но вскоре он заполнился,подошли, возможно, не все, но близко к за-явленному числу. Была даже видеосъёмка,но получилась ли она и где будут записи,пока неизвестно.

Открыл конференцию Сергей Романов,поприветствовав слушателей и рассказаво плане конференции и намечающихсямероприятиях.

Первый доклад назывался «ПротоколHTTP/2. Зачем и как использовать в Perl»,и это был мой доклад. Мне трудно сказать,как восприняли доклад слушатели, но 40минут для меня пролетели незаметно.Большая часть доклада была посвящена

протоколу HTTP: истории протокола, недо-статкам HTTP/1.1 и достоинствам HTTP/2.Затем проводился обзор реализаций дляPerl: подробно рассмотрен Net::Curl и егоработа сHTTP/2, а также Protocol::HTTP2— как возможная основа для реализацийклиентов и серверов с поддержкой HTTP/2.

После доклада был вопрос о судьбеWebSocket, и для многих стало открове-нием, что WebSocket не работает поверхHTTP/2 и никакой альтернативы для егозамены HTTP/2 не предлагает. Разработкаспецификации WebSocket поверх HTTP/2заглохла и похоже никому не нужна.

Следующим докладчиком должен былстать Илья Чесноков, который готовил ин-терактивный доклад, но из-за техническойнакладки доклад пришлось перенести.Поэтому следующим выступал Михаил

Матвеев с докладом «Инструменты дляработы с изображениями в Perl».

В докладе был сделан обзор самых попу-лярных модулей Perl для работы с изоб-ражениями: ImageMagick, GraphicMagickи другие. Как обычно, вопрос выборамодуля возник в результате работы надпрактической задачей: написание своейсобственной капчи. Особо в докладе былвыделен модуль Imager, который в отличиеот других библиотек не требует установ-ленных C-библиотек с заголовками (чтоиногда доставляет проблемы при сборке нанекоторых дистрибутивах), так же написанс использованием C/XS, но собираетсябез проблем, работает быстрее, хоть и неподдерживает большого спектра форматов.Были представлены бенчмарки, в которыхтакже можно было сравнить различия вскорости работы модулей.

Далее был обеденный перерыв, а затемИлья, для которого нашли переходникдля видео-разъёма, смог подключить свойноутбук и показать доклад «Технологииасинхронного программирования». Пре-зентация была сделана с помощью Vroom— инструмента, позволяющего показыватьслайды в редакторе vim. Доклад шёл це-лый час и фактически объединил в себенесколько больших докладов по различ-ным техникам асинхронного программи-рования: это форки, треды и сопрограммы.Было рассказано, чем ужасны Perl тредыи почему Coro — это единственные нор-мальная реализация тредов (хотя это и несовсем треды в привычном понимании).Бенчмарки иллюстрировали различия в ра-боте различных реализаций асинхронныхтехник, как по нагрузке процессора, так ипо использованию памяти.

Следующим докладчиком был Денис Федо-сеев, который представил доклад «Легасиэто не страшно». Денис поделился опытомборьбы с огромным легаси-проектом, гденет ни тестов, ни разделения на пакеты, нидаже элементарного use strict (на этомместе особо чувствительные перловикидолжны упасть в обморок). Рассказал отехниках изоляции кода, способах веденияисправлений. Презентация была очень кра-сочной, позволяя целиком проникнутьсяатмосферой безысходности: живые мертве-цы, велосипеды с квадратными колёсами.Но в итоге был сделан вывод, что выживатьс легаси можно, и это не страшно.

После кофе-брейка выступил лидерMoscow.pm Павел Щербинин с докла-дом «Index Condition Pushdown», которыйбыл посвящён одноимённой фиче, кото-рая появилась в MySQL 5.6. Слайдов как

таковых было не много, в основном всеобъяснения зарисовывались на флипчарте,при этом очень активно происходило вза-имодействие со слушателями, собственноэто и было ближе к духу воркшопа (семи-нара), когда можно не только слушать, нои пытаться понять, о чём речь и пытатьсяотвечать на вопросы ;-)

Доклад закономерно породил вопрос:зачем MySQL, если есть PostgreSQL, нофлейм был беспощадно отсечён — каждыйиспользует то, что его устраивает и в чёмон лучше разбирается.

В завершении конференции были блиц-доклады. Надо сказать, что большинстводокладов были зарегистрированы уже походу конференции.

• Artur Penttinen сделал сразу два блиц-

доклада о том как можно распарситьконфигурационный файл одностроч-ником и частный случай с конфигура-ционным файлов в формате ini.

• Илья Чесноков, «О регулярности».Немного философский доклад особытиях в нашей жизни и фор-мировании полезных привычек,например, регулярное проведениеконференций или ежегодное ведениецикла рождественских статей PerlAdvent Calendar.

• Илья Чернов, «PSPlot — графикив EPS». Доклад был посвящён соб-ственной разработке: модулю psplot,который позволяет рисовать двумер-ные графики в формате EPS.

• Доклады Михаила Иванова «SWAT»и Алексея Мележика «SparrowHub»

были логически объединены и по-священы системе Swat — DSL-языкуи утилитам для быстрой разработкиавтоматических тестов web прило-жений и его спутнику sparrowhub,служащего для хранения сцена-риев тестирования для наиболеепопулярных веб-приложений (типаwordpress).

• Сергей Романов в докладе «Christmasis coming» наконец упомянул то,что практически не звучало на кон-ференции: Perl 6. На рождество насвсех ждёт стабильный релиз Perl 6,которого так ждали последние 15 лет.

После окончания докладов последовалатрадиционная after party в одном из баровсеверной столицы (а для некоторых и не водном). Но это уже другая история, впечат-

ления о которой трудно выразить словами,но можно измерить в единицах объёма ;-)

■ Владимир Леттиев

3 Взгляд на 2015 г.

Краткий обзор заметных событий в миреPerl за прошедший год

1 февраля

На конференции FOSDEM в БрюсселеЛарри Уолл объявил, что официальнаябета-версия Perl 6 появится 27 сентября(на его 61-летие), а релиз 6.0 ожидается наРождество 2015-го.

Статья в журнале с заметками с этого вы-ступления: Perl 6, или Get ready to party.

2 февраля

Владимир Леттиев открыл сайт perlnews.ru— новостной блог о всех заметных событи-

ях мира Perl на русском языке.

16 февраля

Разработчики Rakudo объявили, что пре-кращают поддержку виртуальной машиныParrot, чтобы не тратить силы на поддержа-ние бекенда, который все равно не сможетобеспечить некоторые нужные возможно-сти, которые потребуются в предстоящемрелизе Perl 6.0 в 2015 году.

6 апреля

The Perl Foundation объявили о созданиифонда поддержки развития ядра Perl 6 (Perl6 Core Development Fund).

28 апреля

Microsoft выпустил текстовый редактор для

программистов Visual Studio Code. Средиязыков, для которых доступна подстветкасинтаксиса, — Perl и Perl 6.

1 июня

Вышел релиз Perl 5.22. Статьи в журнале:

• Что нового в Perl 5.22• Перевод документа perl5220delta.podна русский язык

6 июня

Марк Леман анонсировал свой форк Perl5.22, провокационно назвав его stableperl.

16 августа

20 лет CPANу.

27 сентября

Perl 6 (в лице Rakudo Star 2015.09) официаль-но получил статус бета-версии.

18 декабря

Языку программирования Perl исполни-лось 28 лет.

25 декабря

Релиз Perl 6.

■ Андрей Шитов

4 Управление модулями и прекомпиля-ция в Perl 6

25 декабря 2015 г. вышел первый стабильныйрелиз Rakudo Perl 6, среди новшеств которо-го совершенно новая система управления мо-дулями и прекомпиляция. Рассмотрим в де-талях процесс загрузки, разрешения зависи-мостей и компиляции модулей.

Обычная загрузка модуля

Традиционный процесс загрузки модуля вPerl 5 выглядит достаточно просто: имя мо-дуля преобразуется к имени файла, напри-мер, Foo::Bar ожидается быть найденнымв файле Foo/Bar.pm. В массиве @INC содер-жится список каталогов, в которых следуетпроизводить поиск файлов модулей. Часть

каталогов является фиксированными и по-лучили свои обозначения, например:

• site — каталог для поиска модулей,установленных администраторомсистемы,

• vendor — каталог для модулей, постав-ляемых с дистрибутивом,

• perl — каталог модулей, идущих в по-ставке Perl

Модули ищутся и загружаются во времякомпиляции программы из исходногокода на диске. Такой же метод до недав-него времени использовался и в Perl 6, норождественский релиз внёс существенныеизменения в такое положение вещей.

Репозитории

Теперь в Perl 6 мы работаем с репозитори-ями — определённым образом организо-ванное хранилище модулей. Репозиториимогут быть различных типов, и это первоеотличие от традиционных каталогов. Про-стейший вариант репозитория совместим страдиционным подходом и хранит файлымодулей в соответствии с именем пакета.Более сложный вариант поддерживаетустановку дистрибутивов модулей вместес метаинформацией и ресурсами, а такжепроизводит прекомпиляцию модулей вбайткод.

Ранее используемый массив @*INC ушёлв небытие и теперь при загрузке моду-лей используется переменная процессаPROCESS::<$REPO>, которая являетсясвязанным списком с информацией о

доступных репозиториях.1 $ perl6 −e 'for $*REPO.repo−chain

−> $n { say $n }'23 inst#/home/user/.perl6/2015.124 inst#/usr/share/perl6/site5 inst#/usr/share/perl6/vendor6 inst#/usr/share/perl67 ...

Как видно, в отличие от Perl 5, в цепочкерепозиториев присутствует новый каталог,находящийся в каталоге пользователя,запустившего программу, и он имеетмаксимальный приоритет. Кроме того,присутствует префикс inst#, которыйуказывает на тип репозитория, в данномслучае тип CompUnit::Repository::Installation, который поддерживаетинсталляцию.

По-прежнему мы можем дополнять списокс помощьюфлага−I или переменной окру-жения PERL6LIB, например:1 $ perl6 −I /opt/perl6 'for $*REPO

.repo−chain −> $n { say $n }'23 file#/opt/perl64 inst#/home/user/.perl6/2015.125 inst#/usr/share/perl6/site6 inst#/usr/share/perl6/vendor7 ...

Как видно, если не указать тип репозито-рия перед путём, то он будет считаться тра-диционным файловым file#.

Что касается выражения use lib '/path/to/lib', то теперь её нельзя использоватьв модулях, только в скриптах.

Инсталляция в репозиторий

Инсталляция в традиционный файловыйрепозиторий — это простое копированиефайлов. С новым типом репозиториявсё несколько сложнее. Рассмотрим какорганизован такой репозиторий:1 \2 +− dist3 |4 +− sources5 |6 +− resources7 |8 +− short9 |

10 +− precomp11 \12 + compiler.id13 \14 + ..

При инсталляции дистрибутива Foo, со-держащего модуль Foo::Bar, в каталогеdist создаётся файл с именем, представля-ющим собой SHA1 от уникального иден-тификатора дистрибутива, содержащийметаинформацию о дистрибутиве в фор-мате JSON. В каталог sources помещаютсяисходные коды модуля, в каталог resource- связанные ресурсные файлы, все именафайлов генерируются как уникальные40-символьные идентификаторы.

В каталог short помещаются файлы с име-нами как результат хеш-функции SHA1 отимени соответствующего модуля. В такомфайле помещается 40-символьный иденти-фикатор дистрибутива, в состав котороговходит данный модуль.

В каталог precomp помещается результаткомпиляции исходного кода в байткод.

Внутри каталога precomp создаётся каталогдля текущей версии компилятора (в имявключается даже время сборки компилято-ра). Дальнейшая организация вложенныхкаталогов precomp напоминает git: внутрисоздаётся до 256 двухбуквенных каталогов,в который помещаются скомпилирован-ный байткод, например:1 +− 5C2 \3 + 5

C74B123E79B373BB96EC583A5C7EE4F458931BD

4 + 5C74B123E79B373BB96EC583A5C7EE4F458931BD.deps

5 + 5C74B123E79B373BB96EC583A5C7EE4F458931BD.rev−deps

6 +− 7E7 \8 + 7

E4AC40F59629C48CCBD390FEB4B272004591024

9 + 7E4AC40F59629C48CCBD390FEB4B272004591024.deps

10 + 7E4AC40F59629C48CCBD390FEB4B272004591024.rev−deps

Создаются также файлы .deps и .rev-deps ку-да помещается данные о зависимостях и об-ратных зависимостях (sha1-хеш имени мо-дуля).

Таким образом, когда в коде встречается за-пись use Foo::Bar, компилятор выполня-ет такую цепочку действий:

1. вычисляет sha1-сумму имени Foo::Bar;

2. ищет файл с получившимся id в ката-логе short;

3. если находит, то по содержимомуузнаёт id дистрибутива;

4. загружает метаинформацию дистри-бутива из каталога dist;

5. из метаинформации узнаёт располо-жение исходного кода нужного моду-ля дистрибутива;

6. проверяет наличие исходного кода вкаталога sources (но не загружает);

7. проверяет наличие прекомпилиро-ванного кода в precomp;

8. если нет прекомпилированного кода,то выполняет загрузку исходного ко-да и прекомпиляцию с сохранениембайткода;

9. загружает из каталога precomp файлс прекомпилированным кодом моду-ля;

10. проверяет наличие в каталоге

precomp файла с таким же именем,но с окончанием .deps;

11. если есть deps-файл, то находит в нёмимена source-файлов зависимостей;

12. в цикле проходит шаги 2–12 для всехзависимостей.

За инсталляцию отвечает класс CompUnit::Repository::Installation. Примеркода, выполняющего установку, можноувидеть в скрипте установки дистрибу-тива CORE при сборке Rakudo. В целомпримерно так:1 my %provides =2 "Foo::Bar" => "lib/Foo/Bar.

pm6",3 ;45 # задаём путь к репозиторию для

данного процесса

6 PROCESS::<$REPO> := CompUnit::RepositoryRegistry

7 .repository−for−spec("inst#/path/to/repo");

89 # устанавливаем/прекомпилируем

10 $*REPO.install(11 Distribution.new(12 name => "Foo",13 auth => "vendor",14 ver => "0.1",15 provides => %provides,16 ),17 %provides,18 :force,19 );

Недостатки

К сожалению, скорость, с которой появи-лись новые репозитории (хотели успеть кРождеству), помешала достаточно хорошопротестировать и нормально реализоватьфункционал.

$(DESTDIR)

Большинство дистрибутивов Linux иUNIX-систем используют переменнуюокружения $(DESTDIR), чтобы указатьвременный каталог для инсталляции присборке (например, при сборке пакетов).Новый механизм репозиториев затрудняетуказание временного префикса, поэтомувышедший Rakudo 2015.12 просто падает сошибкой при установке с использованием

$(DESTDIR).

К счастью, это быстро заметили и ввели но-вуюпеременнуюокружения RAKUDO_PREFIX, которая позволяет переопределить пре-фикс при операциях с репозиториями. Ноэто уже войдёт только в следующий релиз.

Прекомпиляция

Использование результатов прекомпиля-ции и сама прекомпиляция выполняетсятолько в самом первом репозитории изсписка. По умолчанию это репозиторий издомашнего каталога пользователя. Дажеесли исходный код находится, например,в репозитории vendor и там же есть пре-компилированный код, то rakudo не будетиспользовать результат прекомпиляции,а заново выполнит прекомпиляцию и

создаст необходимые файлы в каталогепользователя.

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

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

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

Rakudo 2016.01

16 января 2016 г. ожидается очередной ре-лиз Rakudo. Вероятно, что-то из обнаружен-ных проблем будет исправлено.

■ Владимир Леттиев

5 Perl 6-винегрет

Новогодний оливье — сборная солянка про раз-ные интересные штуки в Perl 6

Юникод

Хранение и обработка символов внутристрок Perl 6 происходит в формате NFG(Normal Form Grapheme). Это названиепридумано еще в бытность Parrot, а с прак-тической точки зрения достаточно знать,что из любого символа можно получитьего NFC-, NFD-, NFKC- и NFKD-формы:-) Это разные канонические и декомпо-зированные представления символов вюникоде.

Подробности про разные формы представ-

лений с интересными примерами опубли-кованы на сайте юникода, а история NFG —в блоге Джонатана.

Посмотреть разные формы помогут одно-именные методы, которые можно вызы-вать на односимвольных строках:1 say $s.NFC; # codepoint2 say $s.NFD;3 say $s.NFKC;4 say $s.NFKD;

Полное каноническое название возвращаетметод .uniname:1 say 'й'.uniname; # CYRILLIC SMALL

LETTER SHORT I

У строк есть полезный метод .encode, поз-воляющий получить представление в тойили иной юникодной кодировке:

1 my $name = 'Перл';2 say $name.encode('UTF−8');34 # utf8:0x<d0 9f d0 b5 d1 80 d0 bb

>

(Этот метод не поможет перекодироватьCP1215 в КОИ-8.)

Для закрепления материала предлагаю по-смотреть на вывод упомянутых методов наразных хитрых символах:1 unidump('�');2 unidump('ы');3 unidump('å');4 unidump('é');5 unidump('�'); # один из немногих

символов,6 # для которого

различаются7 # все канонические

представления

8 unidump('й');9 unidump('²');

10 unidump('Æ');1112 sub unidump($s) {13 say $s;1415 say $s.chars; # число графем16 say $s.NFC; # code point17 say $s.NFD;18 say $s.NFKC;19 say $s.NFKD;20 say $s.uniname; # юникодное

название буквы2122 say $s.NFD.list; # списком2324 say $s.encode('UTF−8').elems;

# сколько байтов25 say $s.encode('UTF−16').elems

;2627 say $s.encode('UTF−8'); # в

виде utf8:0x<...>2829 say '';30 }

Формы NFKC и NFKD, в частности, могутпреобразовать надстрочные и подстроч-ные индексы в обычные цифры:1 say '2'.NFKD; # NFKD:0x<0032>2 say '²'.NFKD; # NFKD:0x<0032>

С помощью функции unimatch можноузнать, принадлежит ли символ к тойили иной группе символов в юникоде.Например:1 say unimatch('ф', 'Cyrillic'); #

True

Но нужно быть осторожным, чтобы не раз-жигать:

1 say unimatch('ї', 'Cyrillic'); #True

2 say unimatch('ï', 'Cyrillic'); #False

Здесь были одинаково выглядящие, норазные символы. Их NFD соответственно 0x<0456 0308> и 0x<0069 0308>, а имена— CYRILLIC SMALL LETTER YI и LATINSMALL LETTER I WITH DIAERESIS.

Юникодные свойства можно проверить ирегулярными выражениями:1 say 1 if 'э' ~~ /<:Cyrillic>/;2 say 1 if 'э' ~~ /<:Ll>/; # Letter

lowercase

Для создания юникодных строк можнонапрямую воспользоваться конструкторомкласса Uni:1 say Uni.new(0x0439).Str; # й

2 say Uni.new(0xcf, 0x94).Str; # Ï

Whatever (*)

Whatever — это класс, который позволяетсоздавать объекты, тип и значение кото-рых определяются из контекста. Чтобысоздать такой объект, достаточно поста-вить звездочку (если она, разумеется, неозначает умножение).1 say *.WHAT; # (Whatever)

Whatever удобно использовать для созда-ния простых функций, например:1 my $f = * ** 2; # возведение в

квадрат2 say $f(16); # 2563

4 my $xy = * ** *; # возведение впроизвольную степень

5 say $xy(3, 4); # 81

Того же результата удастся достичь болеетрадиционным (для старого Perl 6) синтак-сисом:1 # Анонимный блок кода с одним

аргументом $x2 my $g = −> $x {$x ** 2};3 say $g(16);45 # Блок с двумя аргументами, имена

сортируются по алфавиту6 my $h = {$^a ** $^b};7 say $h(3, 4);

Вполне оправдано использование звездоч-ки для создания интервалов (Range):

Пример с циклом:

1 for (5..*) {2 .say; # построчно печатает от

5 до 103 last if $_ == 10;4 }

Пример с массивом:1 my @a = <2 4 6 8 10 12>;2 say @a[3..*]; # (8 10 12)

Отдельно следует рассмотреть, что проис-ходит с отрицательными значениями.

Все, начиная с четвертого элемента и закан-чивая предпоследним:1 say @a[3..*−2]; # (8 10)

Чтобы получить последний элемент, нель-зя просто написать @a[−1]:

1 Unsupported use of a negative −1subscript to index from theend;

2 in Perl 6 please use a functionsuch as *−1

Следует добавить звездочку:1 say @a[*−1]; # 12

Звездочка подходит и для создания лени-вых списков (то есть таких списков, очеред-ной элемент которых вычисляется по меренеобходимости):1 my @a = (1 ... *);2 for (0..5) {3 say @a[$_];4 }

В ленивых списках допустимо подсказатькомпилятору, как генерировать последова-

тельность:1 my @a = (1, 2 ... *); # с

шагом 12 my @a = (2, 4 ... *); # четные

числа3 my @a = (2, 4, 8 ... *); #

степени двойки

Либо использовать явно определенныйблок со способом вычисления:1 my @fib = 0, 1, * + * ... *; #

Фибоначчи

Файлы

Имя файла, который сейчас исполняеся, те-перь находится в переменной $*PROGRAM−NAME, а не $*PROGRAM_NAME, как было ещесовсем недавно.

А вот программа, которая использует встро-енную функцию slurp, читающую содер-жимое текущего файла:1 say slurp $*PROGRAM−NAME;

Противоположная функция — spurt(«бить струей»), она записывает данные вфайл. Вот пример реализации юниксовойфункции cp на Perl 6:1 my ($source, $dest) = @*ARGS;23 my $data = slurp $source;4 spurt $dest, $data;

По умолчанию файл либо создается, либоперезаписывается. Ключ (именованныйаргумент) :append попросит добавлятьновые данные в конец файла:1 spurt $dest, $data, :append;

Чтобы упасть с ошибкой при попытке за-писи в существующий файл, добавьте оп-цию :createonly (:append при этом ста-нет неактуальной):1 spurt $dest, $data, :createonly;

И slurp, и spurt принимают аргументс именем кодировки enc, но опять же,старых кодировок типа 'KOI8' там нет (даи не надо):1 my $data = slurp $source, enc =>

'UTF−8';2 spurt $dest, $data, enc => 'UTF

−16';

Если на строке вызвать метод .IO, то воз-вращается объект класса IO::Path. Методопределен так:1 method IO() returns IO::Path:D

(:D здесь не ржачный смайл, а означаетdefined.)

Например, как получить абсолютный путьк локальному файлу:1 say 'file1.pl'.IO.abspath;

Как получить содержимое такого-то катало-га:1 say '.'.IO.dir;

Проверки существования файлов и катало-гов теперь выглядят так:1 say 1 if 'file.txt'.IO.e; # −e '

file.txt' в Perl 52 say 1 if 'file.txt'.IO.f; # −f3 say 1 if '..'.IO.d; # −d

Остальные методы, перечисленные вдокументации, вполне понятны без объяс-

нений. Например, для выделения из именифайла расширения можно воспользоватьсяметодом .extension:1 say $filename.IO.extension;

■ Андрей Шитов

6 Использование Rust из Perl

Встраивание Rust в Perl с помощью FFI

Rust — язык системного программиро-вания, развиваемый Mozilla Fundation.Призван заменить собой С. Обеспечиваетбезопасную работу с памятью и прочиеприятные мелочи, которых иногда так нехватает в С, при этом не дает оверхеда попроизводительности (в теории).

Подробности языковых конструкций я тутрасписывать сильно не буду, в целом, всепримеры укладываются в первые 4 главыкниги по Rust.

Сначала установим Rust последней версии(1.5 на момент написания). Инструкциипо установке на все поддерживаемые

платформы можно найти на сайте —https://www.rust-lang.org/downloads.html.

Будем считать, что Rust уже установлен идаже работает. Попробуем его использоватьиз Perl.

Сначала опробуем пример из документа-ции, создадим новый проект с помощьюCargo:1 cargo new embed2 cd embed

Добавим в Cargo.toml следующее содер-жимое:1 [lib]2 name = "embed"3 crate−type = ["dylib"]

Этим мы говорим карго, что мы хотим

получить библиотеку, а не бинарник ибиблиотека должна линковаться с кодомна C.

Теперь отредактируем src/lib.rs и при-ведем его к такому виду:1 use std::thread;23 #[no_mangle]4 pub extern fn process() {5 let handles: Vec<_> = (0..10)

.map(|_| {6 thread::spawn(|| {7 let mut x = 0;8 for _ in (0..5

_000_000) {9 x += 1

10 }11 x12 })13 }).collect();14

15 for h in handles {16 println!("Thread finished

with count={}",17 h.join().map_err(|_| "

Could not join athread!").unwrap());

18 }1920 println!("done!");21 }

Это приложение запускает 10 тредов, каж-дыйиз которых считает до 5 000 000, собира-ет от них возвращаемое значение (счетчикx) и выводит это нам.

Соберем его:1 cargo build −−release

В каталоге target/release у нас появит-ся файл libembed.so (дляMac OS расшире-

ние будет dylib, для Win — dll, я все это про-делываю на маке, так что библиотеки вез-де будут с расширением dylib, вам же надобудет поправить под свою систему) — этонаша подключаемая библиотека. При фор-мировании библиотеки Cargo автоматиче-ски добавляет префикс lib к имени проек-та, указанного в name.

Настало время вызвать ее из Perl. Таккак XS-биндингов в Perl для Rust еще несуществует (ну или я их не нашел), то мыбудем использовать FFI::Raw. Этот модульпозволяет из кода на Perl дергать функ-ции в библиотеках на С. Модуль весьманизкоуровневый, но для начала это дажехорошо.

Создадим файл main.pl со следующим со-держимым:1 #!/usr/bin/env perl

23 use v5.16;4 use FFI::Raw;56 my $process = FFI::Raw−>new(7 'target/release/libembed.dylib'

, 'process',8 FFI::Raw::void,9 );

1011 say $process−>call();1213 say "Done!";

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

И запустим наш перловый скрипт:

1 alpha6$ perl ./main.pl2 Thread finished with count

=50000003 Thread finished with count

=50000004 Thread finished with count

=50000005 Thread finished with count

=50000006 Thread finished with count

=50000007 Thread finished with count

=50000008 Thread finished with count

=50000009 Thread finished with count

=500000010 Thread finished with count

=500000011 Thread finished with count

=500000012 done!13 Done!

Этот код может валиться с Segmentationfault 11 перед выходом из скрипта. Это

связано с выводом с STDOUT из кода наRust. Видимо, FFI как-то криво обрабатыва-ет закрытие дескрипторов. На маке валитсястабильно, на Linux работает без ошибок,на Windows не проверял. Видимо, влияюткакие-то особенности платформы.

Вызывать числодробилки из Perl это ко-нечно хорошо, но как насчет передачи па-раметров функции? Тоже можно, посчита-ем факториал.

Приведем src/lib.rs к виду:1 #[no_mangle]2 pub extern fn fact(x: i32) −> i32

{3 let mut fact = 1;4 for i in 1..x+1 {5 fact *= i;

6 }78 return fact;9 }

Здесь мы объявляем публичную функциюfact, которая принимает на вход число ввиде 32-битного целого и то же самое воз-вращает в качестве результата.

Так же обратите внимание: #[no_mangle]надо указывать перед каждой функцией,которую мы хотим экспортировать. Еслиубрать эту строку и попробовать вызватьрассчет факториала, мы получим ошибку,несмотря на то, что функция объявленапубличной:1 alpha6$ perl ./main.pl2 dlsym(0x7ffeda40abd0, fact):

symbol not found at ./main.plline 13.

no_mangle говорит компилятору, чтобы онне изменял имя функции во время компи-ляции, что делает возможным доступ к нейиз других языков.

Приведем main.pl к виду:1 my $fact_fn = FFI::Raw−>new(2 'target/release/libembed.dylib'

, 'fact',3 FFI::Raw::int,4 FFI::Raw::int,5 );67 my $fact_val = $fact_fn−>call(30)

;89 say "Factorial [$fact_val]";

Сохраняем, запускаем:1 alpha6$ perl ./main.pl2 Factorial [1409286144]

3 Perl code done!

Хм, кажется, что-то пошло не так…

В этом примере можно увидеть наглядноразличия Perl и языков более низкогоуровня. Если посмотреть на результатработы функции, то видно что факториал30 немножко не является факториалом 30,хотя бы потому что в нем должно быть 22цифры. При этом просто увеличение раз-рядности до u64 не поможет, потому чтотуда он тоже не влезает. В общем, моральсей басни такова — следите за перепол-нением переменных, так как компиляторэтого не отслеживает, и рантайм-проверокпо умолчанию тоже нет.

Впрочем, при изменении разрядности с intдо int64, факториал 14 рассчитывается вер-но, если верить калькулятору, а все, что вы-

ше 15 — это уже математика с хитрыми ал-горитмами, Rust из коробки с ней работатьне умеет.

Еще одна проблема — FFI::Raw не позво-ляет напрямую передавать и возвращать извнешнего кода что-то сложнее примитив-ных типов. То есть передать вфункциюхешили вернуть вектор не получится. Но все-гда можно работать со структурами С, а пе-регнать Perl-структуру в сишную — задачаэлементарная.

Приведем наш lib.rs к виду:1 pub struct Args {2 init: u32,3 by: u32,4 }56 #[no_mangle]7 pub extern fn use_struct(args: &

Args) −> u32 {8 println!("Args: {} − {}",

args.init, args.by);9

10 return args.init;11 }

Здесь мы объявляем структуру Args и дела-ем ее доступной снаружи нашей библиоте-ки указанием pub. В функции use_structмы говорим компилятору, что ожидаем навходе указатель на эту структуру. Ну и воз-вращаем обратно первый элемент структу-ры.

В перловый код вставим такое:1 my $packed = pack('LL', 42, 21);2 my $arg = FFI::Raw::MemPtr−>

new_from_buf($packed, length$packed);

3

4 my $foo = FFI::Raw−>new(5 $shared, 'test_struct',6 FFI::Raw::uint,7 FFI::Raw::ptr,8 );9

10 my $val = $foo−>($arg); #Получаемструктуру из Rust кода

11 say "result [$val]";

Здесь мы упаковываем данные для струк-туры (два числа int32), затем создаем изних объект FFI::Raw::MemPtr, которыйвыделяет область памяти и укладываеттуда упакованную стуктуру. Затем мыпривязываемся к функции test_structиз нашей библиотеки и говорим, что будемпередавать в нее указатель.

Соберем и запустим:1 alpha6$ cargo build −−release

2 alpha6$ perl ./main.pl3 Args: 42 − 214 result [42]

Поздравляю, мы только что передали струк-туру данных в нашу библиотеку на Rust.Попробуем теперь сделать с этой структу-рой что-то более осмысленное.

Создадим счетчик (действие не особоосмысленное, зато показывает как с этимвсем работать). Приведем наш lib.rs квиду:1 #![allow(non_snake_case)]2 use std::mem::transmute;34 pub struct Args {5 init: u32,6 by: u32,7 }8

9 pub struct Counter {10 val: u32,11 by: u32,12 }1314 impl Counter {15 pub fn new(args: &Args) −>

Counter {16 Counter{17 val: args.init,18 by: args.by,19 }20 }2122 pub fn get(&self) −> u32 {23 self.val24 }2526 pub fn incr(&mut self) −> u32

{27 self.val += self.by;28 self.val29 }

3031 pub fn decr(&mut self) −> u32

{32 self.val −= self.by;33 self.val34 }35 }3637 #[no_mangle]38 pub extern fn createCounter(args:

&Args) −> *mut Counter {39 let _counter = unsafe {

transmute(Box::new(Counter::new(args))) };

40 _counter41 }4243 #[no_mangle]44 pub extern fn getCounterValue(ptr

: *mut Counter) −> u32 {45 let mut _counter = unsafe { &

mut *ptr };46 let val = _counter.get();

47 return val;48 }4950 #[no_mangle]51 pub extern fn incrementCounterBy(

ptr: *mut Counter) −> u32 {52 let mut _counter = unsafe { &

mut *ptr };53 _counter.incr()54 }5556 #[no_mangle]57 pub extern fn decrementCounterBy(

ptr: *mut Counter) −> u32 {58 let mut _counter = unsafe { &

mut *ptr };59 _counter.decr()60 }6162 #[no_mangle]63 pub extern fn destroyCounter(ptr:

*mut Counter) {64 let _counter: Box<Counter> =

unsafe{ transmute(ptr) };65 // Drop66 }

Этот код я честно утащил отсюда, правдаон не заработал на новой версии компиля-тора, да и с FFI::Raw у него тоже любовьне сложилась, пришлось его немного по-править. Но ничего, нам, пользователямMojolicious, не привыкать.

Что здесь происходит? Сначала создаютсядве структуры данных: Args и Counter. Вцелом они одинаковые, но для Counterмысоздаем реализацию, которая добавляет на-бор методов к нашей структуре.

Затем мы объявляем публичную функциюcreateCounter, которая на вход прини-мает указатель на структуру данных Args.Из этой структуры мы создаем объект

Counter, который упаковываем в контей-нер Box::new, который будет доступенчерез указатель. Работа с transmute делоопасное, поэтому мы оборачиваем созда-ние контейнера в unsafe и этим самыммы говорим компилятору, что мы самипозаботимся о проверке на безопасностьвсей этой конструкции (чего мы не делаемв данном случае).

После создания контейнера мы возвраща-ем из функции изменяемый указатель, с ко-торым и будем дальше работать везде.

Теперь создадим файл counter.pl, ко-торый будет использовать нашу новуюбиблиотеку:1 #!/usr/bin/env perl23 use v5.16;4 use FFI::Raw;

56 my $shared = 'target/release/

libembed.dylib';78 my $packed = pack('LL', 42, 21);9 my $arg = FFI::Raw::MemPtr−>

new_from_buf($packed, length$packed);

1011 my $createCounter = FFI::Raw−>new

(12 $shared, 'createCounter',13 FFI::Raw::ptr, FFI::Raw::ptr14 );1516 my $getCounterValue = FFI::Raw−>

new(17 $shared, 'getCounterValue',18 FFI::Raw::uint, FFI::Raw::ptr19 );2021 my $incrementCounterBy = FFI::Raw

−>new(

22 $shared, 'incrementCounterBy',23 FFI::Raw::uint, FFI::Raw::ptr24 );2526 my $ptr = $createCounter−>($arg);2728 my $val = $getCounterValue−>($ptr

);29 say "Current value [$val]";3031 my $nval = $incrementCounterBy−>(

$ptr);32 say "New value [$nval]";

Здесь мы сначала привязываемся к функ-ции createCounter и говорим, что будемпередавать указатель и ждем на выходеуказатель. Затем привязываемся к функ-ции getCounterValue и говорим, чтопередаем ей указатель и ждем unsigned int.Для всех остальных функций должны бытьсозданы аналогичные записи, здесь я их

описывать не стал.

Дальше мы вызываем createCounter,которому передаем собранную рукамиструктуру Args и получаем обратно указа-тель на контейнер со счетчиком. Дальшемы используем этот указатель для измене-ния значений счетчика.

Соберем это все дело и запустим:1 alpha6$ cargo build −−release2 alpha6$ perl ./main.pl3 Current value [42]4 New value [63]

В выводе видно, что мы получили текущеезначение счетчика после создания, а потомизменили его значение.

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

ку. Однако есть проблема — если мы хотимиспользовать эту структуру внутри Perlкода, то нас ждет небольшое разочарова-ние. У меня так и не получилось привестито, что возвращает Rust, во что-то при-годное для использования в Perl. Есликто-то знает, как это сделать, — напишитев комментариях, а?

Но структуры-то получать хочется! Что ж,пришло время добавить еще один слой аб-стракции—используем FFI::Platypus вместоFFI::Raw.

FFI::Platypus является намного более су-ровыминструментомипредоставляет кучуопций, позволяя не заморачиваться с низ-коуровневой работой с данными в отличиеот FFI::Raw.

Проверим, что у нас все работает как

надо. Установим FFI::Platypus и FFI::Platypus::Lang::Rust, этот модульпозволяет использовать при объявленияхструктур и функций структуры из Rust, ане запоминать маппинг между Rust и C.

Изменим lib.rs и создадим файл plat.pl с содержимым, указанным ниже.

src/lib.rs:1 #[no_mangle]2 pub extern fn hello_plat(arg: u32

) {3 println!("Hello PL {}", arg);4 }

plat.pl:1 #!/usr/bin/env perl2 use v5.16;3 use FFI::Platypus;4

5 my $shared = 'target/release/libembed.dylib';

67 my $ffi = FFI::Platypus−>new();89 $ffi−>lib($shared);

10 $ffi−>lang('Rust');11 $ffi−>attach( hello_plat => ['u32

'] => 'void' );12 hello_plat(1);

Здесь мы создаем объект FFI::Platypusи указываем ему путь к библиотеке —$ffi−>lib($shared), затем указываем,какой язык мы хотим использовать $ffi−>lang('Rust'). После этого указанияFFI сам подгрузит модуль FFI::Paltypus::Lang::<LangName>, и можно будетиспользовать фичи для конкретного языка(соответствующий модуль должен бытьпредварительно установлен).

Далее мы добавляем функцию из библио-теки в перловый код, конструкция $ffi−>attach создает функцию с указаннымименем, которая транслирует данные в/изсоответстующую библиотечную функцию.Функция создается в пространстве именмодуля, так что надо следить, чтобы небыло перекрытия функций из библиотекии текущего пакета.

Собираем, запускаем:1 alpha6$ cargo build −−release2 alpha6$ perl ./plat.pl3 Hello PL 1

Все работает как и задумано.

Настало время получить желанную струк-туру из нашей библиотеки.

В очередной раз переделаем наш src/lib

.rs:1 use std::mem::transmute;23 pub struct Args {4 init: u32,5 by: u32,6 descr: String,7 }89 impl Args {

10 pub fn new(init: u32, by:u32, descr: String) −>Args {

11 Args{12 init: init,13 by: by,14 descr: descr,15 }16 }17 }1819 #[no_mangle]

20 pub extern fn get_struct() −> *mut Args {

21 let my_struct = unsafe {transmute(Box::new(Args::new(42, 1, "hi from Rust!".to_string()))) };

22 my_struct23 }

Здесьмыдобавляем к нашей старой добройArgs еще одно поле с типом String, в ко-торое мы будет записывать строку.

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

Обратите внимание, как создается объектString, в Rust есть два типа строк — strи String. В чем же отличия между str иString? str это так называемый строко-вый срез. Он имеет фиксированную длинуи неизменяем, поэтому мы не можем ис-пользовать их в структуре (на самом делеможем, но с некоторыми затейливыми те-лодвижениями). Тип String представляетсобой строку, размещаемую в куче и га-рантированно явлющуюся UTF-8 строкой.Обычно String получается из str путемпреобразования через .to_string.

Обратное преобразование осуществляетсячерез & — str = &String.

Более подробно об этом можно почитать вдокументации.

Теперь получим данные из Rust:

1 use v5.16;2 package My::RustStr;34 use FFI::Platypus::Record;56 # Описываем структуру данных,

которую мы ждем из библиотеки7 record_layout(qw(8 uint rs_counter9 uint rs_by

10 string rs_descr11 ));1213 my $shared = 'target/release/

libembed.dylib';1415 my $ffi = FFI::Platypus−>new;16 $ffi−>lib($shared);17 $ffi−>lang('Rust');1819 # Связываем структуру с нашим

пакетом MyRustStr иприсваиваем ей алиас rstruct

20 $ffi−>type("record(My::RustStr)"=> 'rstruct');

2122 # Аттачим библиотечную функцию

get_struct в наш пакет23 # и указываем, что ждем от нее

объявленную выше структуру24 $ffi−>attach( get_struct => [] =>

'rstruct', sub {25 my($inner, $class) = @_;26 $inner−>();27 });2829 package main;3031 # Используем наш пакет My::

RustStr для работы сбиблиотекой

32 my $str = My::RustStr−>get_struct;

3334 printf "Struct is [%d][%d][%s]\n"

,

35 $str−>rs_counter,36 $str−>rs_by,37 $str−>rs_descr;

Сначала мы объявляем пакет My::RustStr, в котором описываем структуру данных,которую мы хотим от нашей библиотеки.Из-за особенностей модуля FFI записьrecord_layout может быть только однана пакет (на самом деле нет, но лучше такне делать), поэтому мы выделяем нашуфункцию в отдельный пакет.

Затем мы создаем новый тип данных в па-кете My::RustStr $ffi−>type("record(My::RustStr)" => 'rstruct'); и назы-ваем его rstruct.

Далее мы, как обычно, создаем биндингдо библиотеки и эспортируем ее в про-странство имен пакета My::RustStr.

Обратите внимание на $ffi−>attach(get_struct => [] => 'rstruct',

sub {, здесь в качестве возвращаемогозначения функции мы указываем, чтохотим получить нашу структуру rstruct.Также мы указываем кастомную функцию-обработчик, передавая coderef последнимаргументом. В данном случае он не де-лает ничего кроме вызова библиотечнойфункции, но туда можно добавить какую-то дополнительную обработку до/послевызова внешней библитеки.

Затем мы просто распечатываем содержи-мое нашей структуры.

Теперь мы также можем передать структу-ру в нашу библиотеку без шаманства с packи указателями. Изменим наш перловыйкод работы со счетчиком.1 use v5.16;

23 package MyCounter;45 use FFI::Platypus::Record;67 record_layout(qw(8 uint rs_counter9 uint rs_by

10 ));1112 package main;1314 my $shared = 'target/release/

libembed.dylib';1516 my $ffi = FFI::Platypus−>new;17 $ffi−>lib($shared);18 $ffi−>lang('Rust');1920 # Создаем новую структуру данных

с именем CounterStr21 $ffi−>type('record(MyCounter)' =>

'CounterStr');

2223 # Аттачим функции из библиотеки24 $ffi−>attach(createCounter => ['

CounterStr'] => 'opaque');25 $ffi−>attach(getCounterValue => [

'opaque'] => 'u32');26 $ffi−>attach(incrementCounterBy

=> ['opaque'] => 'u32');2728 # Создаем структуру, из которой

мы создадим счетчик29 my $counter = MyCounter−>new(30 rs_counter => 10,31 rs_by => 2,32 );3334 # Создаем объект счетчика и

используем его для работы35 my $ptr = createCounter($counter)

;36 say getCounterValue($ptr);37 say incrementCounterBy($ptr);

Здесь мы используем opaque в каче-стве возвращаемого значения функцииcreateCounter так как нам нужен сы-рой указатель для работы с остальнымиподключенными функциями. Но при же-лании этот указатель можно превратитьв структуру нужного формата с помощьюфункции cast.

В Rust-коде ничего менять не надо.

Запускаем:1 alpha6$ perl ./plat.pl2 103 12

Итого, мы создали новый объект счетчикаи изменили его.

Теперь мы умеем работать с большейчастью случаев, которые нам могут приго-

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

Для этого нам пригодятся Module::Buildи Module::Build::FFI::Rust.

Приведем структуру проекта к виду:1 Build.PL2 ffi3 lib4 plat.pl

В lib у нас лежат Perl-библиотеки нашегопроекта, в ffi — Rust-исходники. Build.PL выглядит так:1 #!/usr/bin/env/perl23 use strict;4 use Module::Build;

5 use Module::Build::FFI::Rust;67 my $build = Module::Build::FFI::

Rust−>new(8 module_name => '

MyCounter::Record',9 dist_abstract => 'FFI

test',10 create_makefile_pl => '

traditional',11 dist_author => 'Denis

Fedoseev <denis.fedoseev@gmail.com>',

12 create_readme => '0',13 license => 'perl',14 configure_requires => {15 'Module::Build' => 0.38,16 'File::Which' => 0,17 'Module::Build::FFI' => '0.18

'18 },19 build_requires => {},20 requires => {

21 'perl' => '5.016000',

22 'FFI::Platypus' => 0,23 'FFI::Platypus::Lang::

Rust' => 0,2425 },26 recommends => {},27 recursive_test_files => 0,28 sign => 0,29 create_readme => 1,30 create_license => 1,31 );3233 $build−>create_build_script;

Обратите внимание на использованиеModule::Build::FFI::Rust вместообычного Module::Build. Запускаемустановку, подготавливаем Build-скрипт:1 alpha6$ perl ./Build.PL2 Can't find dist packages without

a MANIFEST file3 Run 'Build manifest' to generate

one45 WARNING: Possible missing or

corrupt 'MANIFEST' file.6 Nothing to enter for 'provides'

field in metafile.7 Created MYMETA.yml and MYMETA.

json8 Creating new 'Build' script for '

MyCounter−Record' version 'v0.0.2'

Собираем наши модули:1 alpha6$ ./Build2 cargo build −−release3 Updating registry `https://

github.com/rust−lang/crates.io−index`

4 Compiling winapi v0.2.55 Compiling libc v0.2.4

6 Compiling winapi−build v0.1.17 Compiling advapi32−sys v0.1.28 Compiling rand v0.3.129 Compiling embed v0.1.0 (file

:///Users/alpha6/projects/tests/build/ffi)

10 Building MyCounter−Record

Устанавливаем:1 alpha6$ ./Build install2 cargo build −−release3 Building MyCounter−Record4 Files found in blib/arch:

installing files in blib/libinto architecture dependentlibrary tree

5 Installing /Users/alpha6/perl5/perlbrew/perls/perl−5.20.3/lib/site_perl/5.20.3/darwin−2level/auto/MyCounter/Record/Record.bundle

6 Installing /Users/alpha6/perl5/

perlbrew/perls/perl−5.20.3/lib/site_perl/5.20.3/darwin−2level/MyCounter/Record.pm

MyCounter/Record/Record.bundle это иесть наша Rust-библиотека, установленнаяв систему.

Теперь мы умеем работать с Rust-кодомиз Perl и, самое главное, собирать из этогополноценные пакеты, которые и на CPANвыложить можно :)

В целом все, что описано в этой статьесо стороны Perl, применимо к любомуязыку, который умеет изображать из себяC-библиотеку. Так что теперь вы всегдасможете взять нужную библиотеку, хоть наJava, и использовать в корыстных целях.

■ Денис Федосеев

7 Обзор CPAN за ноябрь 2015 г.

Рубрика с обзором интересных новинок CPANза прошедший месяц.

Статистика

• Новых дистрибутивов — 154• Новых выпусков — 778

Новые модули

HTTP::Cookies::PhantomJS

HTTP::Cookies::PhantomJS — расшире-ние для HTTP::Cookies, которое позволя-

ет использовать файлы с куками формата,которые применяются в веб-скрапереPhantomJS. Это бывает полезно, когда ку-ки устанавливаются в результате работыjs-кода на стороне клиента при загрузке ре-сурса в PhantomJS, а последующие ресурсыможно загрузить, используя получен-ные куки, с помощью более легковесноговеб-клиента, например, LWP::UserAgent.

GCCJIT

GCCJIT— это обвязка к библиотеке libgccjit,которая позволяет на лету формировать икомпилировать код в машинные инструк-ции.

Locale::TextDomain::OO::Extract::Xslate

Locale::TextDomain::OO::Extract::Xslate позволяет извлекать строки дляперевода из шаблонов Text::Xslate дляпоследующего использования в Locale::TextDomain::OO.

DOM::Tiny

DOM::Tiny — это минималистичныйHTML/XML DOM-парсер с поддержкойCSS-селекторов.1 my $dom = DOM::Tiny−>new(<<EOF);2 <div>3 <p id="a">Test</p>4 <p id="b">123</p>5 </div>6 EOF

78 say $dom−>at('#b')−>text; # 1239

10 $dom−>find('div p')−>last−>append('<p id="c">456</p>');

1112 say $dom−>at('#c')−>text; # 456

App::remarkpl

App::remarkpl — это утилита, позволяю-щая запустить локальный веб-сервер на ос-нове Mojolicious для отображения презента-ций на основе remark. В качестве парамет-ра указывается путь к файлу с презентациив формате Markdown, после запуска презен-тация может быть открыта в браузере по ад-ресу localhost:3000

Devel::Trepan::Deparse

Devel::Trepan::Deparse — это плагиндля отладчика Devel::Trepan, которыйдобавляет две новые команды: deparse иdeval. Команда deparse позволяет показатькакой код будет выполняться в следующейинструкции, что бывает полезно, когдаотладчик остановился на строке, в которойнесколько операций и не ясно какая из нихбудет выполняться на следующем шаге:1 if ( grep { $_ eq 'foo' } @bar )

{...}

выражение внутри if или уже блок кодаgrep для какого-то текущего значения@bar? Команда deparse покажет какаяименно операция будет выполнятьсяследующей:1 : deparse

2 # code to be run next...3 $_4 # contained in...5 $_ eq 'foo'

Mail::DKIM::Iterator

Модуль Mail::DKIM::Iterator позволя-ет организовать проверку DKIM-записейв почтовых сообщениях и добавлятьDKIM-подпись к ним. Основное отличиеот Mail::DKIM — это возможность орга-низовать неблокирующуюся потоковуюобработку писем.

Unix::Pledge

Недавно появившийся в OpenBSD систем-ный вызов pledge(2) теперь можно исполь-зовать в perl-программах, которые будут ра-ботать на этой платформе, с помощью мо-дуля Unix::Pledge. Модуль позволяет на-ложить ограничения на процесс, например,запретить чтение или запись файлов, с ука-занием списка файлов-исключений.

Perl6::Export

Perl6::Export реализует Perl 6 рольis export для Perl 5 модулей, позволяяизящно экспортировать функции модуля.Например, сравнение с Exporter:1 package My::Module; |

package My::Module;

2 use Exporter; |use Perl6::Export;

3 |4 our @EXPORT = qw(foo bar); |

sub foo is export(:MANDATORY);5 |

sub

bar

is

export(:MANDATORY);

6 |7 our @EXPORT_OK = qw(baz); |

sub baz is export(:DEFAULT :qux);

8 our %EXPORT_TAGS = |9 qw(qux => [qw(baz)]); |

Pg::Reindex

Индексы Postgresql должны периодическипересоздаваться для достижения опти-мальной производительности. Например,это можно сделать с помощью командыREINDEX, но в этом случае потребуетсяэксклюзивная блокировка всей таблицы.

Модуль Pg::Reindex выполняет созданиеиндекса с помощью CREATE INDEXCONCURRENTLY, который может выпол-няться без блокировки. Затем он начинаеттранзакцию, в которой удаляет старыйиндекс и переименовывает новый. Такимобразом достигается пересоздание индексабез длительной блокировки.

Обновлённые модули

Sereal 3.010

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

JSON::Validator 0.63

Модуль JSON::Validator позволяет про-водить проверку данных в формате JSONна соответствие JSON-схеме. В новой вер-сии удалена поддержка использованиямодулей YAML и YAML::Tiny для загрузки

JSON-схемы в формате yaml (поддержи-ваются только YAML::XS и YAML::Syck).Связано это с тем, что YAML и YAML::Tiny в некоторых случаях некорректновыполняют разбор формата yaml.

PathTools 3.60

Вышло обновление дистрибутива PathTools,включающий модули для работы с путямина различных поддерживаемых в Perl плат-формах. В новой версии включён модульFile::Spec::AmigaOS для поддержкиплатформы AmigaOS.

MIME-Types 2.12

В новой версии дистрибутива MIME−Typesобновлена база данных MIME-типов, в со-ответствии с текущим состоянием в IANA.Требования к версии Perl снижены до 5.6.

utf8::all 0.017

Прагма utf8::all тотально включает UTF-8: для исходного кода программы, переко-дирует открываемые файлы и аргументы,переданные в командной строке, разреша-ет использование последовательностей \N{...} для задания юникод-символов по ихимени и т.д. В новой версии появилась воз-можность отключать эффект прагмы, с по-мощью no utf8::all.

PDL 2.015

Вышел новый релиз PDL — модуля для ра-боты научных вычислений и отображенияданных. Как указано в журнале изменений— это отполированный релиз с исправлени-ем всех замечаний обнаруженных в преды-дущей версии с улучшенной поддержкой64-битных целых.

perl 5.23.5

Вышла новая версия Perl 5.23.5 для раз-работчиков. В новом релизе проведенаоптимизация, которая ускорила выпол-нение арифметических операций, такихкак сложение, вычитание и умножение.Кроме того, теперь быстрее стали работатьоперации ++ и −−. Исправлено несколь-

ко ошибок с крахом интерпретатора,например, в функции pack: perl −e 'pack "WH200000", \0'

App::perlbrew 0.74

Обновился менеджер Perl-инсталляцийperlbrew. В новой версии появиласьподдержка переменной окруженияPERLBREW_LIB_PREFIX, которая позво-ляет добавить каталог с библиотеками,имеющей приоритет над локальнымикаталогами библиотек perlbrew (т.е. ./lib:$PERL5LIB). Теперь при загрузкефайлов будет отдаваться предпочтениеhttps-ресурсам и обязательно проверятьсяих сертификат.

podlators 4.00

Вышел новый мажорный релиз утилитдля конвертирования POD-файлов. Те-перь все входящие в дистрибутив модулииспользуют единую версию. Релиз содер-жит большое количество исправлений,накопившихся с последнего релиза, вы-шедшего два года назад.

■ Владимир Леттиев

8 Интервью с Дмитрием Шаматриным

Дмитрий Шаматрин — программист,с недавнего времени организатор Perl-конференций

Как и когда научился программировать?

Во всей этой кухне я относительно недав-но. Первые попытки были вшколе, это был2005-2006 год, и даже что-то получалось.

Хорошо помню, что Паскаль мне тогдаочень не понравился. Но серьезно при-годилось программирование в процессенаписания диплома в универе. Если кто нев курсе, по образованию я химик.

Так вот, есть такой большой и страшныйаппарат — масс-спектрометр. Он определя-

ет состав вещества, разбивая молекулу нафрагментарные ионы, количество которыхв единицу времени регистрируется масс-анализатором. По ним определяется состав.И вот если молекула большая, ионов бу-дет много, и они будут разные. Их надоопознать и посчитать. Руками это делатьдолго и не интересно, особенно, если такихспектров несколько. Вот пришлось писатьскрипт. Выбрал Perl, так как уже имелминимальный опыт с ним и примернопонимал, что да как.

Какой редактор используешь?

Emacs. Если/когда нет emacs, могу ис-пользовать vim. Тут уже дело привычки.Так сложилось, в основном, исторически.Долгое время под языки, с которыми яработаю, не было вменяемых IDE. А потомуже просто привык, и еще раз переучивать-

ся ради сиюминутного профита смысладля себя не вижу.

Как и когда познакомился с Perl?

Тут хочу сказать, что винда мне особо нико-гда не нравилась, да и сейчас не нравится.В 2006-2013 году моей основной системойбыл линукс, потом мак. Так вот.

С Perl я познакомился примерно в 2007году. У нас в городе была локальная сеть,подключение ВПН в которой было несамой тривиальной задачей. Провайдерытогда особо не заморачивались с поддерж-кой пользователей на линуксе. Пришлосьвсю автоматизацию подключения сделатьна Perl.

С какими другими языками интересноработать?

Erlang, Haskell, Lua.

Что, по-твоему, является самым боль-шим преимуществом Perl?

Не могу выделить одно преимущество, по-этому вот тот список, благодаря которому яеще пишу на Perl.

• Нечеловеческая гибкость.• Возможность решать проблемы, какправило, малой кровью.

• Быстрая разработка.• Вменяемое событийное программи-рование.

• Ссылочная механика.• Здравая и продуманная системамодулей/библиотек.

• Простота создания своих модулей(привет ExtUtils::MakeMaker).

Что, по-твоему, является самой важнойособенностью языков будущего?

Перестать опускать планку порога вхожде-ния в язык. Я прекрасно понимаю, что рын-ку не хватает программистов. Также я по-нимаю, что всем хочется программировать.Однако, это не повод упрощать инструмен-ты до уровня, понятного и дебилу. Слож-ность перла практически оптимальная. Помоему мнению, самое последнее скотствоэто жертвовать функциональностью во имяснижения порога вхождения. Целевая ауди-тория, как правило, не воспользуется, а вотурезанная функциональность печалит.

Что думаешь о Perl 6?

Отличный язык. Жаль, что невовремя.

Было бы приятно, если бы взлетело, но я не

понимаю двух вещей. Зачем это назвалиPerl и сколько надо затратить сил и средств,чтобы вывести его сообщество хотя-бы науровень Perl 5. Я думаю, что использовавдругое имя для языка шансов взлететьбыло бы сильно больше.

Что лучше: работать на аутсорс или напродукт?

Тут нет однозначного ответа. Мне инте-реснее продукт, например, хотя, вродекак, по статистике, аутсорс стабильнее.Короче. Аутсорс — меньше рисков, низкаявероятность большого профита в будущем.Продукт — риски, которые, в основном, оттебя не зависят. Ну, например, бабло закон-чилось. В случае успеха можно получитьнекислую материальную выгоду.

Как жить программисту, если кажется,

что вокруг одни дураки?

Понять и простить. Правило 95% непобеди-мо.

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

Лучше специализация или везде по чуть-чуть?

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

Так все-таки Erlang или Haskell?

Они решают примерно подобные задачи.

Эрланг позволяет довольно быстро постро-ить приложение, которое будет устойчиво

к рантайм-ошибкам, которых, как все мызнаем, предостаточно всегда. Причем,90% этих ошибок случается хрен поймипочему. К тому же, будучи функциональ-ным языком, он позволяет делегироватьнекоторые сложные вещи виртуальноймашине и сосредоточиться на реализации.Иммутабельные переменные, хорошаямногопоточность. Ну и OTP. Все вместеэто дает реально хороший, но при этомнесложный язык, который очень хорошоработает под нагрузкой, редко падает и до-вольно неплохо масштабируется. Ну, еслипросто и на чистоту, то эрланг правильноиспользует раззвиздяйство программиста,культивируя три основные добродетели.Да и очень идеологически близок к перлу.

Хаскель позволяет делать практически тоже самое, но при этом сильно сложнее идороже, что требует больше ресурсов. Ну

и сложная система типов поначалу будетсильно взрывать мозг. Мой совет — учитеоба :D Потому как понимание хаскеля,как чистого функционального языка, вконечном итоге, позволяет намного лучшеписать на эрланге.

Сколько времени проводишь за написа-нием Perl-кода?

Когда как. Обычно несколько часов в день.

Стоит ли советовать молодым програм-мистам учить сейчас Perl?

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

К тому же, культуры программирования,как таковой, тоже, в широких массах, особоне было. Соответственно, весь тот код, закоторые клеймят Perl, это не вина языка.Если бы в то же время появился, например,питон или руби, сейчас бы слава worn-языка была бы у одного из них. Просто нев то время. Почему стоит учить? Да пото-му, что он достаточно низкоуровневый,чтобы разобраться, как работают файловыедескрипторы, сокеты, счетчик ссылок. Ион достаточно высокоуровневый, чтобыразобраться с концепциями в мире совре-менного программирования. Ну и имееточень большую базу библиотек, да.

Вопросы от читателей

Будут еще хакатоны и конференции?

Будут и много. Скорее всего что-то про-

ведем летом. Есть в планах одесскаяконференция по функциональному про-граммированию.

Какой веб-фреймворк сейчас стоит ис-пользовать и почему?

Мне в последнее время очень нравитсяперловый Catalyst. Почему? Удобный, мно-го умеет, под него написано очень многомодулей. Ну и еще очень немаловаж-ный момент — адекватный мейнтейнер.Минусы — громоздкий.

■ Вячеслав Тихановский

Recommended