Направление движения в 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.