Аннотация
Программирование на Perl — это выбор по всему пути. Perl является мультипарадигмальным языком программирования, что означает, что Perl может поддерживать разработку программного обеспечения с использованием различных парадигм программирования, например, функциональное программирование, объектно-ориентированное программирование и многое другое.
Языки программирования — это культура, а культура следует за философией. Философия — это то, как каждая культура обосновывает свои решения, ценности, убеждения и мировоззрение.
Философия Perl
Философия Perl — это TIMTOWTDI, то есть «есть более чем один способ сделать это». Можно даже сказать, что Perl занимает позицию «не занимать позицию», и эта позиция применима и к теме стандартной библиотеки.
Для ясности, то, что некоторые называют стандартной библиотекой Perl — это не более чем набор функций, прагм и пакетов, поставляемых вместе с ядром. Эта коллекция представляет собой смесь функционального и объектно-ориентированного стилей и намеренно облегчена.
Возможно, стоит упомянуть, что команда Raku также приняла эту позицию (и традицию).
Что если бы у Perl была стандартная библиотека?
Не так быстро. Допустим, мы согласны предоставить надежную стандартную библиотеку, но какую парадигму должна использовать эта библиотека? Должна ли эта библиотека быть написана на C, чтобы максимизировать производительность, или она должна быть написана на самом Perl? Должна ли библиотека быть читабельной и служить примером того, как работать на Perl, или просто быть средством достижения цели? Решения, решения.
CPAN вместо стандартной библиотеки
Perl и его CPAN в подавляющем большинстве объектно-ориентированы, и здесь девиз TIMTOWTDI проявляется в полной мере. Это и дар, и проклятие, одновременно источник величайших достоинств Perl и его величайших недостатков. Хотя, даже перед лицом такого изобилия выбора было несколько дистрибутивов CPAN, которые стали де-факто стандартами для сообщества. Тем не менее, парадокс выбора реален и парализует, и очень трудно найти и/или создать единство среди всего многообразия.
Устойчивая объектная ориентация в Perl трудна, потому что концепция и механизмы были прикручены к языку как нечто само собой разумеющееся, и потому что она необязательна, поэтому приходится колебаться между этой парадигмой и другими, т.е. некоторые вещи являются объектами, большинство вещей — нет, поэтому при использовании Perl вам приходится постоянно подтверждать свою объектную ориентацию.
TL;DR
Стандартная библиотека OO значительно упростила бы написание Perl таким образом, чтобы избежать необходимости придумывать похожие или одинаковые решения для общих вычислительных задач. Это сделает код более легким для чтения и написания и устранит усталость от принятия решений, особенно ту, которая приходит с препятствием «искать библиотеку» или «написать эту библиотеку самому», что является именно тем типом ситуации, которой пользуется толпа NIH (не придуманная здесь). Это сделает последующие приложения более производительными и с меньшим количеством зависимостей. Итак, TIMTOWTDI — это дар или проклятие? Является ли конвенция над конфигурацией смирительной рубашкой? Является ли девиз Python «батареи в комплекте» лучшим или раздутым?
Что входит в стандартную библиотеку
Обычно стандартная библиотека содержит процедуры для доступа к файловой системе и выполнения операций ввода-вывода; процедуры, работающие с типами данных языка; процедуры, позволяющие манипулировать памятью; процедуры, обеспечивающие параллелизм/параллелизм; процедуры для обработки математики, операций с датой и временем; процедуры для обработки ошибок и утверждений. Часто, чтобы обеспечить оптимальную производительность, эти процедуры пишутся в очень низкоуровневом коде, например, на языке C.
Что делает хорошую стандартную библиотеку
Я считаю, что хорошая стандартная библиотека должна обладать всеми стандартными качествами, но также должна быть хорошо организована, не быть «собачьим кормом» и быть написана так, чтобы ее можно было читать, что поможет обеспечить идиоматичность производных программ. Другими словами, быть предписывающей, чего Perl старается не делать.
Цель хорошей стандартной библиотеки должна заключаться в том, чтобы помочь уменьшить количество решений, которые приходится принимать инженеру, без ущерба для гибкости или принципов «не повторяйся» (DRY).
Стандартная библиотека должна отвечать на вызов и реагировать на то, что люди строят (здесь и сейчас), или на то, что они пытаются построить. Чего у них нет, что они хотели бы иметь; что они хотят перенять у других языков и фреймворков, которые успешны в том, в чем мы хотим видеть наше программное обеспечение. Чтобы облегчить эргономику разработчика.
Представляем Venus
Представляем Venus, попытку создать идиоматическую неосновную объектно-ориентированную стандартную библиотеку для Perl 5, без многолетних гаданий, законотворчества комитетов, междоусобиц и промедления.
Venus имеет простую модульную архитектуру, надежную библиотеку классов, методов и признаков (ролей), поддерживает автобоксинг чистого Perl, продвинутую обработку исключений, обратно совместимые функции ключевых слов «true» и «false», простую интроспекцию пакетов, разбор опций командной строки и многое другое.
Девиз и этика проекта — «быть комплиментом, а не дубиной». Эта этика является своего рода руководящим принципом, который управляет принятием решений. Система Venus строго комплиментарна, все функции являются опциональными и выбираются по желанию (даже автобоксинг; особенно автобоксинг). Эта система может быть легко расширена без «обезьяньего» патча (т.е. с помощью плагинов), а полезные модели поведения могут быть повторно использованы вне системы с помощью черт (ролей).
Некоторые особенности
- Поддерживает Perl 5.18.0+
- Булевы, совместимые с обратным развитием
- Совместимые стандарты
- Обработка исключений
- Быстрая объектная ориентация
- Отражение пакетов
- Автобоксинг Perl-Perl
- Подключаемая стандартная библиотека
- Надежная документация
- Утилитарные классы
- Классы значений
- Нулевые зависимости
Руководящие принципы
- Стандартная библиотека должна быть стандартом
- Библиотека должна иметь нулевую зависимость
- Библиотека должна быть предписывающей и в то же время гибкой
- Библиотека должна обслуживать две основные категории потребностей: значения и утилиты.
- Библиотека должна поддерживать обертывание всех основных собственных типов данных
- Методы класса значений должны использовать идиоматические алгоритмы чистого языка Perl
- Библиотека должна использовать роли и интерфейсы для максимальной переносимости поведения.
- Библиотека должна раскрывать и использовать сырьевые материалы ядра Perl.
- Классы библиотеки могут быть легко расширены (т.е. подклассифицированы)
- Библиотека должна позволять подключаемые модули и препятствовать «обезьяньему» патчу
- Библиотека должна предоставлять механизмы для обработки ошибок (отбрасывание, перехват и т.д.)
- Библиотека должна обеспечивать поддержку DMMT для JSON и YAML.
- Библиотека должна быть последовательной в названиях и соглашениях для повышения предсказуемости
- Библиотека должна облегчить кризис идентичности мультипарадигмы
- Библиотека должна предоставлять надежную документацию с максимальным тестовым покрытием.
Мировое турне
Все пакеты Venus являются классами, и обычно их можно отнести к одной из четырех категорий: основные классы, классы значений, классы полезностей или абстрактные модели поведения (роли).
ОСНОВНЫЕ КЛАССЫ
Venus — Основной модуль, экспортирующий несколько полезных функций, которые входят в стандартную поставку большинства других языков.
package main;
use Venus qw(
catch
error
raise
);
# the "catch" function for trapping exceptions
my ($error, $result) = catch {
error;
};
# the "true" and "false" keyword functions
if ($result and $result eq false) {
true;
}
# the "raise" function for raising custom exceptions
if (false) {
raise 'MyApp::Error';
}
# and much more!
true ne false;
Venus::Class — модуль создания классов, который использует архитектуру Mars и поддерживает интеграцию суперклассов, миксинов, ролей и интерфейсов.
package Person;
use Venus::Class 'attr';
attr 'fname';
attr 'lname';
package User;
use Venus::Class 'attr', 'base';
base 'Person';
attr 'email';
package main;
my $user = User->new(
fname => 'Elliot',
lname => 'Alderson',
);
# bless({fname => 'Elliot', lname => 'Alderson'}, 'User')
Venus::Mixin — модуль построения миксинов, который использует архитектуру Mars и по сути является модулем построения экспортеров, поддерживающим динамический экспорт.
package Access;
use Venus::Mixin;
sub login {
# ...
}
sub logout {
# ...
}
sub EXPORT {
['login', 'logout']
}
package User;
use Venus::Class 'attr', 'mixin';
mixin 'Access';
attr 'email';
attr 'password';
package main;
my $user = User->new(
fname => 'Elliot',
lname => 'Alderson',
);
# bless({fname => 'Elliot', lname => 'Alderson'}, 'User')
# $user->login;
# undef
# $user->logout;
# undef
Venus::Role — модуль построения ролей (или трейтов), который использует архитектуру Mars и поддерживает интеграцию суперклассов, миксинов, ролей и интерфейсов.
package Authenticable;
use Venus::Role 'error';
sub AUDIT {
my ($self, $from) = @_;
error "${from} missing the email attribute" if !$from->can('email');
error "${from} missing the password attribute" if !$from->can('password');
}
package User;
use Venus::Class 'attr', 'with';
with 'Authenticable';
attr 'email';
package main;
# Exception! "User missing the password attribute"
ВАЛЮТНЫЕ КЛАССЫ
Venus::Array — Класс Array предоставляет методы для манипулирования ссылками на массивы.
package main;
use Venus::Array;
my $array = Venus::Array->new([1..4]);
# bless({'value' => [1, 2, 3, 4]}, 'Venus::Array')
$array->count;
# 4
# $array->all(sub{ $_ > 0 });
# $array->any(sub{ $_ > 0 });
# $array->each(sub{ $_ > 0 });
# $array->grep(sub{ $_ > 0 });
# $array->map(sub{ $_ > 0 });
# $array->none(sub{ $_ < 0 });
# $array->one(sub{ $_ == 0 });
# $array->random;
Venus::Boolean — Класс Boolean предоставляет методы для представления и работы с булевыми значениями.
package main;
use Venus::Boolean;
my $boolean = Venus::Boolean->new(1);
# bless({'value' => 1}, 'Venus::Boolean')
$boolean->negate;
# 0
Venus::Code — Класс Code предоставляет методы для работы с кодовыми ссылками.
package main;
use Venus::Code;
my $code = Venus::Code->new(sub {1});
# bless({'value' => sub {...}}, 'Venus::Code')
$code->call;
# 1
Venus::Float — Класс Float предоставляет методы для работы с числами с плавающей точкой.
package main;
use Venus::Float;
my $float = Venus::Float->new(1.23);
# bless({'value' => '1.23'}, 'Venus::Float')
$float->int;
# 1
Venus::Hash — Класс Hash предоставляет методы для работы с хэш-ссылками.
use Venus::Hash;
my $hash = Venus::Hash->new({1..8});
# bless({'value' => {'1' => 2, '3' => 4, '5' => 6, '7' => 8}}, 'Venus::Hash')
$hash->count;
# 4
# $hash->all(sub{ $_ > 0 });
# $hash->any(sub{ $_ > 0 });
# $hash->each(sub{ $_ > 0 });
# $hash->grep(sub{ $_ > 0 });
# $hash->map(sub{ $_ > 0 });
# $hash->none(sub{ $_ < 0 });
# $hash->one(sub{ $_ == 0 });
# $hash->random;
Venus::Number — Класс Number предоставляет методы для работы с числовыми значениями.
package main;
use Venus::Number;
my $number = Venus::Number->new(1_000);
# bless({'value' => 1000}, 'Venus::Number')
$number->abs;
# 1000
Venus::Regexp — Класс Regexp предоставляет методы для работы со ссылками регулярных выражений.
package main;
use Venus::Regexp;
my $regexp = Venus::Regexp->new(
qr/(?<greet>w+) (?<username>w+)/u,
);
# bless({'value' => qr/(?<greet>w+) (?<username>w+)/u}, 'Venus::Regexp')
$regexp->search('hello venus')->captures;
# ['hello', 'venus']
Venus::Scalar — Класс Scalar предоставляет методы для представления скалярных ссылок.
package main;
use Venus::Scalar;
my $scalar = Venus::Scalar->new;
# bless({'value' => ''}, 'Venus::Scalar')
${$scalar}
# ""
Venus::String — Класс String предоставляет методы для манипулирования строковыми значениями.
package main;
use Venus::String;
my $string = Venus::String->new('hello world');
# bless({'value' => 'hello world'}, 'Venus::String')
$string->camelcase;
# "helloWorld"
Venus::Undef — Класс Undef предоставляет методы для представления неопределенных значений.
package main;
use Venus::Undef;
my $undef = Venus::Undef->new;
# bless({'value' => undef}, 'Venus::Undef')
$undef->defined;
# 0
ВСПОМОГАТЕЛЬНЫЕ КЛАССЫ
Venus::Args — Класс Args предоставляет методы для доступа и манипулирования значениями @ARGS
.
package main;
use Venus::Args;
my $args = Venus::Args->new(
named => { flag => 0, command => 1 }, # optional
value => ['--help', 'execute'],
);
# bless({....}, 'Venus::Args')
$args->flag;
# "--help"
# $args->get(0); # $ARGV[0]
# $args->get(1); # $ARGV[1]
# $args->action; # $ARGV[1]
# $args->exists(0); # exists $ARGV[0]
# $args->exists('flag'); # exists $ARGV[0]
# $args->get('flag'); # $ARGV[0]
Venus::Box — класс Box является прокси-классом и предоставляет механизм для автобоксинга возвращаемых значений проксируемых объектов.
package main;
use Venus::Box;
my $box = Venus::Box->new(
value => {},
);
# bless({'value' => bless({'value' => {}}, 'Venus::Hash')}, 'Venus::Box')
$box->keys->count->unbox;
# 0
Venus::Data — Класс Data предоставляет методы для доступа и манипулирования данными POD (и секциями данных) в базовом файле или маркере __DATA__
.
package main;
use Venus::Data;
my $data = Venus::Data->new;
# bless({...}, 'Venus::Data')
$data->value($data->space->format('label', 't/%s.t'));
# /path/to/t/Venus_Data.t
$data->find(undef, 'name');
# {
# 'data' => ['Venus::Data'],
# 'index' => 1,
# 'list' => undef,
# 'name' => 'name'
# }
Venus::Date — Класс Date предоставляет методы для манипулирования значениями даты и времени.
package main;
use Venus::Date;
my $date = Venus::Date->new(570672000);
# bless({...}, 'Venus::Date')
$date->string;
# '1988-02-01T00:00:00Z'
Venus::Error — Класс Error предоставляет методы для создания и отбрасывания исключений.
package main;
use Venus::Error;
my $error = Venus::Error->new;
# bless({...}, 'Venus::Error')
$error->throw;
# Exception!
Venus::Json — класс Json предоставляет методы для кодирования и декодирования данных JSON.
package main;
use Venus::Json;
my $json = Venus::Json->new(
value => { name => ['Ready', 'Robot'], version => 0.12, stable => !!1, }
);
# bless({...}, 'Venus::Json')
$json->encode;
# {"name": ["Ready", "Robot"], "stable": true, "version": 0.12}
Venus::Match — класс Match предоставляет объектно-ориентированный механизм переключения (или диспетчерскую таблицу).
package main;
use Venus::Match;
my $match = Venus::Match->new(5);
# bless({...}, 'Venus::Match')
$match->when(sub{$_ < 5})->then(sub{"< 5"});
$match->when(sub{$_ > 5})->then(sub{"> 5"});
$match->none(sub{"?"});
my $result = $match->result;
# "?"
Venus::Name — Класс Name предоставляет методы для разбора и форматирования строк пространства имен пакета.
package main;
use Venus::Name;
my $name = Venus::Name->new('Foo/Bar');
# bless({'value' => 'Foo/Bar'}, 'Venus::Name')
$name->package;
# "Foo::Bar"
Venus::Opts — Класс Opts предоставляет методы для доступа и манипулирования значениями @ARGS
, которые передаются в качестве опций командной строки.
package main;
use Venus::Opts;
my $opts = Venus::Opts->new(
value => ['--resource', 'users', '--help'],
specs => ['resource|r=s', 'help|h'],
named => { method => 'resource' } # optional
);
# bless({...}, 'Venus::Opts')
$opts->method;
# "users"
# $opts->method; # $resource
# $opts->get('resource'); # $resource
# $opts->help; # $help
# $opts->get('help'); # $help
Venus::Path — Класс Path предоставляет методы для работы с путями.
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/planets');
# bless({'value' => 't/data/planets'}, 'Venus::Path')
my $planets = $path->files;
# [
# bless({...}, 'Venus::Path'),
# bless({...}, 'Venus::Path'),
# ...,
# ]
# my $mercury = $path->child('mercury');
# my $content = $mercury->read;
Venus::Process — Класс Process предоставляет методы для форкинга и управления процессами.
package main;
use Venus::Process;
my $parent = Venus::Process->new;
# bless({'value' => 2179356}, 'Venus::Process')
my $process = $parent->fork;
if ($process) {
# do something in child process ...
$process->exit;
}
else {
# do something in parent process ...
$parent->wait(-1);
}
# $parent->exit;
Venus::Random — класс Random предоставляет объектно-ориентированный интерфейс для генератора псевдослучайных чисел Perl (или PRNG), который производит детерминированную последовательность битов, приближенную к истинной случайности.
package main;
use Venus::Random;
my $random = Venus::Random->new(42);
# bless({'value' => 42}, 'Venus::Random')
my $bit = $random->bit;
# 1
my $number = $random->range(10, 50);
# 24
Venus::Replace — класс Replace предоставляет методы для работы с данными замены регулярных выражений.
package main;
use Venus::Replace;
my $replace = Venus::Replace->new(
string => 'hello world',
regexp => '(world)',
substr => 'universe',
);
# bless({...}, 'Venus::Replace')
$replace->captures;
# "world"
Venus::Search — Класс Search предоставляет методы для работы с данными поиска по regexp.
package main;
use Venus::Search;
my $search = Venus::Search->new(
string => 'hello world',
regexp => '(hello)',
);
# bless({...}, 'Venus::Search')
$search->captures;
# "hello"
Venus::Space — Класс Space предоставляет методы для разбора, манипулирования и работы с пространствами имен пакетов.
package main;
use Venus::Space;
my $space = Venus::Space->new('foo/bar');
# bless({'value' => 'Foo::Bar'}, 'Venus::Space')
$space->package;
# Foo::Bar
Venus::Template — Класс Template предоставляет минималистичную функциональность рендеринга шаблонов.
package main;
use Venus::Template;
my $template = Venus::Template->new(
'From: "{{name}}"<{{ email }}>',
);
# bless({...}, 'Venus::Template')
$template->render({
name => 'awncorp',
email => 'awncorp@cpan.org',
});
# 'From: "awncorp"<awncorp@cpan.org>'
Venus::Throw — Класс Throw предоставляет механизм для генерации и поднятия ошибок (объекты исключений).
package main;
use Venus::Throw;
my $throw = Venus::Throw->new;
# bless({'parent' => 'Venus::Error'}, 'Venus::Throw')
$throw->error;
# Exception!
Venus::Try — класс Try предоставляет объектно-ориентированный интерфейс для выполнения сложных операций try/catch.
package main;
use Venus::Try;
my $try = Venus::Try->new;
# bless({...}, 'Venus::Try')
$try->call(sub {
my (@args) = @_;
# try something
return 2 * 3 * 4;
});
$try->catch('Example::Error', sub {
my ($caught) = @_;
# caught an error (exception)
return;
});
$try->default(sub {
my ($caught) = @_;
# catch the uncaught
return;
});
$try->finally(sub {
my (@args) = @_;
# always run after try/catch
return;
});
my @args;
my $result = $try->result(@args);
# 24
Venus::Type — Класс Type предоставляет методы для приведения исходных типов данных к объектам и обратно.
package main;
use Venus::Type;
my $type = Venus::Type->new([]);
# bless({'value' => []}, 'Venus::Type')
my $object = $type->deduce;
# bless({'value' => []}, 'Venus::Array')
my $code = $type->code;
# "ARRAY"
Venus::Vars — Класс Vars предоставляет методы для доступа к элементам %ENV
.
package main;
use Venus::Vars;
my $vars = Venus::Vars->new(
value => { USER => 'awncorp', HOME => '/home/awncorp', },
named => { iam => 'USER', root => 'HOME', },
);
# bless({....}, 'Venus::Vars')
$vars->iam;
# "awncorp"
# $vars->root; # $ENV{HOME}
# $vars->home; # $ENV{HOME}
# $vars->get('home'); # $ENV{HOME}
# $vars->get('HOME'); # $ENV{HOME}
# $vars->iam; # $ENV{USER}
# $vars->user; # $ENV{USER}
# $vars->get('user'); # $ENV{USER}
# $vars->get('USER'); # $ENV{USER}
Venus::Yaml — Класс Yaml предоставляет методы для кодирования и декодирования данных YAML.
package main;
use Venus::Yaml;
my $yaml = Venus::Yaml->new(
value => { name => ['Ready', 'Robot'], version => 0.12, stable => !!1, }
);
# bless({...}, 'Venus::Yaml')
$yaml->encode;
# "---nname:n- Readyn- Robotnstable: truenversion: 0.12n"
Почему стоит попробовать Venus
- Вы занимаетесь современным ОО на Perl 5
- Вам интересен современный идиоматический Perl
- Вы ищете архитектурный стандарт
- Вы неравнодушны к условностям, а не к конфигурации
- Вам нужна мощность и производительность
Вдохновение
- https://github.com/ruby/ruby
- https://github.com/crystal-lang/crystal
- https://github.com/elixir-lang/elixir
- https://github.com/python/cpython
Источники
Venus: Стандартная библиотека OO для Perl 5 — Github
Чтобы выучить новый язык, прочитайте его стандартную библиотеку — Hacker News
Является ли стандартная библиотека любого крупного языка программирования обузой? — Языки программирования
Конец цитаты
«Большинство программного обеспечения сегодня очень похоже на египетскую пирамиду с миллионами кирпичей, нагроможденных друг на друга, без структурной целостности, а просто сделанных грубой силой и тысячами рабов». — Алан Кей