Про HaTeX 3 и юникод в коде

(репост из tumblr)

Вступление

Давненько я не заглядывал сюда и не писал ничего содержательного. Но тут в очередной раз есть повод поделиться впечатлениями от новых интересных знаний и к тому же для этого удалось выкроить немного времени.

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

Я кстати писал в мае о том как и почему мне нравится HaTeX. Так вот пред-релизное известие о новых концепциях в HaTeX-3 очень меня обрадовало. Главный момент (для меня), в том, что теперь есть специальный алгебраический тип LaTeX, с помощью которого конструируются LaTeX-выражения на Haskell’е. Такой подход даёт возможность контролировать валидность получаемых (true-)LaTeX-выражений на выходе.

На самом деле это тоже не то, что я хотел, когда говорил о типо-безопасном LaTeX’е, но всё же, лучше, чем было раньше. Я вижу это скорее как класс типов, которые можно представить в форме LaTeX’а, но об этом надо ещё подумать (это мне напомнило цитату, по ходу дела).

Немного об изменениях

Ладно, ближе к делу. Когда я пользовался HaTeX’ом до 3й версии, я написал для себя небольшое дополнение - там были команды, которых мне не хватало и удобные сокращения. Так вот перед тем как начать пользоваться новой версией, я естественно занялся переделкой этих дополнений для совместимости с ней. На самом деле менять почти ничего не пришлось - в основном только уродские сигнатуры типа foo :: Monad m => LaTeX m заменились на foo :: LaTeX, что конечно же к лучшему.

Насчёт пропажи (а на самом деле опциональности) монад в новом HaTeX стоит прочитать пост автора о двух стилях. Он говорит что новый способ, изобилующий операторами <> для склейки частей LaTeX-выражений не шибко приятен для глаза (“Yes, all these operators seem ugly”), но мне это наоборот больше нравится “) Просто я и в старой версии не всегда пользовался do-нотацией (по формуле/фразе на строчку?), а частенько писал что-то типа (от балды что-то напишу сейчас)

1
math $ forall >> x >> from >> "S" >> comma >> space >> f^:2.:(x) >> leq <> 0

Что на обычном LaTeX’е означает примерно следующее:

1
$\forall x\in S,\quad f^2(x)\leq 0$

Что может быть выглядит короче, но уж на мой вкус совершенно отвратительно. А в HaTeX3 это запишется так:

1
math $ forall <> x <> from <> "S" <> comma <> space <> f^:2.:(x) <> leq <> 0

Во-первых так по-моему лучше (читабельнее), а во вторых, поскольку я пользуюсь conceal-фишкой в Vim’е, то у меня в нормальном режиме >> заменяется на симпатичную кавычку » (то есть одним символом), а для нового оператора я сделал замену <> на (юникодовский символ “white diamond”).

И это по-моему прекрасно, потому что код выглядит чисто и чудесно и без всяких там “ugly operators”.

Vim conceal

Про использование юникодовских символов стоит сказать отдельно. Я не очень люблю conceal в Vim’е, потому что меня раздражает то, что в нормальном режиме всё сворачивается в один символ, но при перемещении он воспринимается как свёрнутая последовательность символов, то есть я тупо натыкаюсь на такой символ, жму вправо 10 раз, а курсор не реагирует. Естественно это приводит к проблемам при копировании/вырезании и т.п. Не понимаю, почему так сделано.

Поэтому conceal’у я оставляю маленькие визуальные украшательства, такие как замена <> на или

1
(\x -> x == 1) :: Num a => a -> Bool

на

1
(λx  x  1)  Num a  a  Bool

^__^

Юникодовские символы для операторов

В остальном же, я использую юникод для операторов в Haskell’е, благо GHC это поддерживает уже давным давно (можно хоть по-русски функции и типы называть). Я думаю, любой правовер годный редактор позволяет ненапряжно писать, используя юникодовские символы. В Vim’е я делаю это используя функцию IMAP из плагина LaTeX-Suite. Аналогичное можно сделать тысячей других способов и уж точно можно сделать в Emacs/Textmate/Vico. Итак, я пишу в .vimrc cледующую строчку:

call IMAP (";<" , "≤" , "haskell")

И теперь, при наборе кода;, моментально заменяется на - это как ввод иероглифов по их фонетическому эквиваленту (пиньинь вроде называется). ; я использую как префикс, чтобы отличить этот иероглиф (макрос? “/) от обычного ввода символов, поскольку ; не используется в Haskell’е (нет, не используется).

Теперь вернёмся к моим дополнениям HaTeX’а, которые я кстати назвал HaTeXExtension. Не шибко хорошее название, но другого пока нет. Я определяю там много таких юникодных операторов:

1
 a  b = a <> leq <> b

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

1
math $ forall <> x <> from <> "S" <> comma <> space <> f^:2.:(x) <> leq <> 0

Станет:

1
math $ forall <> x  "S" ¸⎕ f2(x)  0

А при переходе в нормальный режим с conceal’ом, так и вообще

1
math $   x  "S" ¸⎕ f2(x)  0

Что выглядит уже не длиннее чем LaTeX’овский вариант, и намного симпатичнее его. Более того, эта запись совсем немного отличается от математической записи (если не брать во внимание “двумерность” математического синтаксиса). Для ясности, дам определения этих операторов:

1
2
a  b = a <> from <> b       -- from это \in 
a  b = a <> leq <> b

(другое имя потому что in - ключевое слово в Haskell)

1
2
3
a ¸ b = a <> comma <> b    -- comma это просто ", "
a  b = a <> space <> b    -- space это \quad
a ¸⎕ b = a ¸ ""  b        -- а это их комбинация

тут ¸ - это такая красивая арабская загогулинка, похожая на запятую, которую возможно тут особо не разглядишь, но которая у меня в Vim’е с правильным шрифтом отлично смотрится “)

Ну и последний оператор - это как бы “применение функции к аргументам”, чтобы писать f.:(x + y) а не f <> "(" <> x + y <> ")" или f <> prn (x+y) (где prn просто окружает аргумент скобочными символами):

1
2
3
(.:) :: LaTeX -> LaTeX -> LaTeX
func .: params = func <> prn params
() = (.:)     -- просто юникодовский синоним

Заключение

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

Но ящитаю, что это наоборот очень правильно и важно - код должен выглядеть чисто и однородно. И на мой взгляд, эти символы, сокращающие код и зачастую приближающие его к естественной/общепринятой математической нотации, намного меньше нарушают его однородность и прозрачность, чем всякие служебные символы типа слеша в LaTeX’е и этих ужасных фигурных скобочек. И вообще, по-моему нужно расширять синтаксис обычных языков программирования подобным образом - юникод уже никого ведь не удивляет, а если ещё и двумерный синтаксис сделать (как во всяких maple’ах)?….

P.S. Завтра постараюсь написать пост про то, как я использую Template Haskell в своём HaTeXExtension чтобы “скрэпать свой бойлерплэйт-код” (: И да, выложу код на гитхаб.

Comments