Направление движения в Perl: не туда
Гляжу я на тикеты, которые люди пишут на некоторые модули Perl, и жуть берет. Жуть берет причем не по поводу того что пишут, а по поводу того что на эти тикеты реагируют и что-то исправляют.
Не буду приводить тут ссылок, но давайте рассмотрим пример:
no utf8;
my $str;
$str = 'привет, ';
use utf8;
$str .= 'медвед';
printf "utf8: %s\n", utf8::is_utf8($str) ? 'yes' : 'no';
printf "%s\n", $str;
Из примера видно что флаг utf8
означает
“в данной строке имеются юникодные символы”.
Если попробовать напечатать полученную смесь блоба и строки, то в итоге конечно на экране будет каша. Если задуматься на тему как можно вообще работать с подобной смесью: простого способа привести строку к полностью юникодной или к чистому блобу - нет.
Задайтесь вопросом: как в реальной жизни, в реальном приложении может получиться подобная смесь? Придумать реальный юзкейз, где может встретиться не по ошибке подобная смесь строки и блоба ни у кого из знакомых мне людей не получилось.
Самый близкий вариант: некий драйвер, который на входе имеет Perl-объекты, а на выходе пакует в поток октетов в каком-то бинарном протоколе. Но даже в этом случае подобная программа манипулирует не смесью блобов и строк, а блобом в котором закодированы и строки тоже. При упаковке и распаковке строки будут приводиться к блобам и наоборот.
Теперь что происходит в реальной жизни. В реальной жизни, некий человек (причем явление это относительно массовое), страдающий каким-то избыточным перфекционизмом пишет тикет на один из базовых модулей Perl.
Дескать если вот так сконкатенировать, потом байтик вырезать/вставить при
помощи substr
или скажем chop
, то происходит некое несоответствие поведения
документации. А майнтенер базового модуля вместо того чтобы закрыть тикет с
определением “в реальной жизни подобного встретится не может”,
берет да и “исправляет” ошибку, делая так, что на стандартных юзкейзах
функция ведет себя совершенно новым способом, отличным от того что был на
протяжении более чем 15 лет.
Как вам подобный расклад? А потом Вы или кто-то другой залезаете в проект и натыкаетесь на то, что в модуле работы с БД юникод сломали, в веб-фреймворке - юникод сломали, там, сям… Копаешься и раскапываешь, что в каждом случае причиной оказывается примерно такой тикет и такой перфекционист. Причем убивает еще то обстоятельство, что юникод вдруг ломает модуль, от которого такого вообще странно ожидать, например модуль предназначенный для запуска тестов.
Не знаю кого как, а меня эта ситуация крайне удручает. Не понимаю как с этим можно бороться. Пытался я разговаривать с авторами того же Mojo на тему как можно исправить подобный косяк и всегда сталкивался с тем что
- во первых они по причине того что не занимаются работой над не ASCII приложениями, среднестатистически не понимают вообще юникодных проблем (при этом часто Вам с большим апломбом напишут дескать “Вы не понимаете как работает юникод в перле, поэтому заткнитесь”)
- а во вторых почему-то считают подобный вышеприведенному кейз чем-то значимым, достаточной причиной к тому чтобы, например, зафорсить енкодинг Вашего текста в блоб.
Что делать?
Как бороться с перфекционистами пишущими баги, которые потом недалекие не latin1 говорящие граждане “исправляют” я не знаю.
А вот по Perl в целом считаю что нужно бы чтобы с очередной версии Perl бы начал вести себя следующим образом:
- конкатенация
utf8
иblob
на выходе должна давать полнуюutf8
-строку - если это невозможно (
blob
содержит невалидный символ юникода), то должно выбрасываться исключение - можно сделать для “обратной совместимости” (правда пока я так и не нашел ни
одного примера из реальной жизни!) прагму при применении которой конкатенация
blob
иutf8
в случае неудачи на выходе выдастblob
.
Если изменить поведение базовых модулей таким образом, то по моему мнению, спустя какое-то время работа с текстом снова станет простой и понятной.
Легенды “нужно прикрутить/прикрутили поддержку юникода к Test::More” канут в лету. Модуль занимающийся статистикой “прошел тест или нет и сколько таких” не должен вообще думать о юникоде.
PS: Сейчас
Если говорить о разработчиках, пишущих приложения работающие с юникодом и национальными символами.
Чтобы иметь возможность скажем обрабатывать регекспы с национальными символами,
прагма use utf8
просто необходима, например:
use utf8;
use open qw(:std :utf8);
use Test::More;
# ...
like $str => qr{приве\w, медвед}, 'тест пройден';
Без директивы use utf8
валидного сравнения не получится.
Идем далее. Если мы хотим напечатать что-то, то для того
чтобы избежать варнингов то вторая прагма из разряда “маст хэв”
это - use open ':std', ':utf8';
.
Некоторые люди, нактнувшись на проблему применения этой прагмы в модулях (не скриптах!) начинают считать ее вредной. Переубедить (например авторов Mojo) в обратном практически невозможно.
Однако именно это сочетание двух прагм (open
в скриптах и utf8
повсюду)
позволяет писать код который практически не содержит в себе команд
encode
/decode
.