Направление движения в 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 бы начал вести себя следующим образом:

  1. конкатенация utf8 и blob на выходе должна давать полную utf8-строку
  2. если это невозможно (blob содержит невалидный символ юникода), то должно выбрасываться исключение
  3. можно сделать для “обратной совместимости” (правда пока я так и не нашел ни одного примера из реальной жизни!) прагму при применении которой конкатенация 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.