Хабрахабр

OpenSource на Clojure

В Run Loop приглашают тех, кто делает классные продукты своими руками. Никита Прокопов (tonsky) — человек и пароход, успел сделать несколько OpenSource проектов, которыми с удовольствием пользуются другие люди.

А потом перейдем к более общим рассуждениям о хороших интерфейсах, здравом смысле, и моделях обучения программистов. В первую очередь поговорим о шрифте FiraCode, о Clojure, и совершенно разных проектах, например, обертке ClojureScript для React.

Помимо этого он пишет на Objective-C под macOS: программа AnyBar подскажет о наступлении какого-либо события в statusbar, ой, menubar вашего компьютера. О госте: Никита Прокопов примечателен тем, что создал FiraCode, внёс заметный вклад в развитие Clojure сообщества и опубликовал в OpenSource такие проекты как Datascript и Rum.

Занимался веб-проектами в основном, увлекаюсь интерфейсами. Ведущие: Роман Бусыгин (разработчик Яндекс.музыки для iOS) и Алексей Милеев (App in the Air).
Никита: Я программист из Новосибирска, но сейчас живу в Москве. Последнее время программирую на Clojure.

Начать хочу с первого и самого интересного для меня — это шрифт FiraCode. Роман: Предлагаю сегодняшнюю беседу построить вокруг твоих Open Source и публичных проектов. Мне всегда было интересно, как сделать свой собственный шрифт, как это происходит, есть ли специальная программа, какими знаниями нужно обладать, чтобы сделать свой шрифт. Им пользуется множество моих коллег и я сам. Расскажи, пожалуйста, об этом.

FiraCode

Никита: Не совсем корректно говорить, что это мой шрифт. Я взял готовый моноширинный шрифт и дорисовал лигатуры, буквы сам я не рисовал. Чтобы сделать сами буквы, нужен огромный опыт и усидчивость. Это очень сложно, там миллион неочевидных тонкостей. Есть любительские шрифты и всегда видно, что они корявые, но не всегда понятно, в чём эта корявость.

Я использовал достаточно популярную программу Glyphs. Я взял хороший шрифт FiraMono от Mozilla и дорисовал туда лигатуры, для этого есть несколько программ. Сюрпризом может стать, что она довольно дорогая, но мне дали лицензию на разработку FiraCode. В шрифтовом дизайне я не эксперт, поэтому посмотрел, что другие используют, и тоже это использовал. Открываешь, там есть буквы, находишь ячейки и рисуешь картинки. Glyphs похож на векторный графический редактор. Всё достаточно просто. Плюс всякие тонкости, связанные со шрифтами, например, что у букв есть размер, точки привязки, переходы от жирного к нежирному.

Изначально его создали, как средство для реализации всяких хитрых штукенций в шрифтах. Интересно то, что внутри шрифта OpenType есть язык программирования, на основе которого сделана эта замена лигатур. Плюс там бывают вариабельности, концевые начертания у букв: если буква стоит в начале и конце слова, к ней дорисовывается какой-нибудь хвостик. Например, лигатур, когда fi, ffi и подобное в обычных шрифтах заменяются на лигатуры. Это всё можно запрограммировать с помощью хитрых паттернов, и также лигатур, которые сделал.

Роман: Сколько примерно заняла времени доработка FiraMono до того вида FiraCode, которым сейчас все пользуются?

Первая версия была не очень сложная. Никита: Посчитать будет сложно. Как всегда, началось всё довольно быстро, но какое-то время ушло на то, чтобы разобраться, что происходит, как это реализовать: примеров особо не было.

Чем они отличались? Роман: То есть было ещё и несколько версий.

Во-первых, я дорисовываю новые лигатуры, убираю старые иногда, если они в чём-то конфликтуют. Никита: Да, и версии обновляются. Там внутри есть очень интересная алгоритмическая задача: есть длинная последовательность символов, из нее нужно вычленить те комбинации, из которых состоят лигатуры, но если они пересекаются, то должны быть другая замена. В какой-то момент там поменялся механизм для отрисовки лигатур. Вот эта штука поменялась, поменялся способ рендеринга лигатур. У меня даже есть программка на Clojure, которая генерирует набор правил, которые потом вставляются в шрифт — довольно сложно. По сути, для пользователя он становится лучше, меньше багов, больше лигатур. Раньше они заменялись на один символ, сейчас заменяются на три.

Я никогда не думал, что в шрифтах могут быть баги. Роман: Интересно. Правила, по которым отрисовщик решает, когда вставлять лигатуры, а когда нет, описываются на специальном языке программирования или просто декларативно?

Никита: Декларативно, а потом внутри транслируется достаточно эффективно в таблицу замены.

Набор для первой версии лигатур

Роман: Скажи, когда ты придумывал набор лигатур, ты смотрел на какие-то конкретные языки программирования или есть более-менее общий перечень того, что нужно людям в повседневной жизни?

Я увидел, что есть шрифт Hasklig, сделанный специально для Haskell. Никита: Эту идею я придумал не сам. Дальше я взял просто всё, что мне пришло в голову. Но Haskell мне не был нужен, поэтому я подумал что надо сделать такой же шрифт, но, во-первых, основанный на том шрифте, который мне нравятся, во-вторых, для любого языка. Пишешь как в С, а оно заменяется на стрелочку. Сначала очевидные вещи: <=, >=, ->, <-. По-моему, с этого всё началось.

Clojure

Алексей: В самом начале ты упомянул, что сейчас пишешь на Clojure. Расскажи, как ты пришел к Clojure, с чего всё начиналось, как ты пришел именно к этому языку?

Они прикольные и универсальные на тему, как у программиста работает голова, грубо говоря, как проектировать системы. Никита: Я посмотрел несколько лекций Рича Хикки (рекомендую, например, эту и эту). Потом заинтересовался тем, что же он сделал. Лекции открыли для меня огромное количество новой важной концентрированной информации, и я зафанател от Рича Хикки. Пошел читать, разобрался и всё — понеслось. Оказалось, что он сделал язык Clojure.

Я видел его выступления: они и длинные, и что самое важное, интересные. Роман: Меня тоже можно записать в поклонники Рича Хикки. То есть он умудряется держать внимание слушателей на протяжении долгого времени.

Алексей: Что именно тебе нравится в Clojure?

Никита: На последней конференции Рич Хикки сделал такую ремарку:

Clojure — это язык для старых, уставших программистов.

К нему не приходят в начале карьеры, а он становится интересен уже после 10-15 лет. Во-первых, у тебя абсолютная свобода. Всё, что тебе нужно, ты делаешь сам или берешь из библиотек. В самом языке зашито минимум вещей. Всё остальное ты конструируешь, делаешь ровно так, как тебе нужно. Всё самодельное и можно поменять. Это опасно, когда ты новичок, но полезно, если ты уже эксперт и знаешь то, что тебе нужно.

То есть минимальный разрыв между сложностью того, что ты пытаешься выразить и сложностью того, как это записано. В этом языке максимально четко и компактно выражается мысль. В Clojure, если вещь тривиальная, то она и записана, скорее всего, тривиально, в одну-две строчки. В Java, например, ты можешь делать какую-то совершенно тривиальную вещь, но она растянется на 10 строчек, и ты устанешь.

Когда я услышал, что Clojure не накладывает на тебя никаких ограничений, мне почему-то сразу вспомнился С, который тоже как угодно стоял на ушах. Роман: Я понял, что ты ответил и на мой вопрос. Но потом я услышал ответ, что всё-таки это не просто синтаксический сахар, но и удобный, компактный язык, который позволяет выражать свои мысли меньшим количеством кода.

Никита: Да, он очень высокоуровневый, а С — низкоуровневый.

Или в OpenSource, где нужно тщательно отслеживать каждое изменение? Алексей: Мне интересно, разве такая свобода языка не мешает работать в больших проектах с большим количеством людей. Разве это не мешает? Насколько я слышал, в Clojure разрешены вещи вплоть до изменения синтаксиса языка.

В итоге приходят к какой-то общей практике, что этим мы пользуемся, а этим нет. Мне это напоминает ситуацию со Scala, когда язык умеет много всего, и в каждом большом проекте все обкладываются какими-то своими граблями. Нет ли такой проблемы в Clojure? В итоге Scala в одном проекте и Scala в другом — это две разные Scala.

В тех, в которых работал, были легкие вариации. Никита: Я в особо больших проектах я не работал. Мне кажется, это потому что в Scala, допустим, есть любители потеоретизировать и порассуждать, как наиболее правильно что-то сделать. На самом деле почему-то не происходит такого прям разброда. В Clojure, наоборот, мало всего — ты не можешь выбрать не то. Как ты сказал, там много всего — ты можешь выбрать не то. На Clojure не любят городить абстракции ради чего-то на будущее. Это очень практичный язык. Делаешь конкретно то, что нужно. Если нужно, например, что-то распечатать, то ты просто максимально практично печатаешь и не паришься о том, откуда к тебе пришел принтер, какой интерфейс, протокол ты реализуешь. Поэтому эта практичность — наверное, общий знаменатель.

Проекты на Clojure

Роман: Насколько я могу видеть, у тебя на GitHub есть несколько проектов на Clojure. Расскажи о них подробнее.

Datascript

Никита: Первый мой относительно успешный OpenSource проект — это Datascript. Это клиентский storage для браузера. Для сайта это, наверное, не очень полезно, а если пишете какое-то интерактивное приложение в браузере, то вам нужно хранить где-то состояния. Datascript — это как раз хранилище для состояний. Его фишки:

  • Он имутабельный. То есть он не уничтожает предыдущие версии, просто создается достаточно эффективно новая копия storage.
  • Он сортированный. Он автоматически поддерживает индексы по атрибутам, по любым entity_id и так далее. Он позволяет быстро найти все, что нужно. всего, чего тебе нужно.
  • Он плоский. Если ты особо не задумывался над тем, как реализовать storage на клиенте, то первое, что ты сделаешь, это структуру вложенных JSON. В Datascript storage плоский, в любой момент можно перейти в любое место и найти то, что нужно.

В общем, это относительно удобный доступ к данным, причем двусторонний: ты можешь от родителя к ребенку перейти, а можешь также от ребенка к родителю.

Datomic — это база данных Clojure. Для сообщества Clojure у него есть еще одно преимущество: он сделан с таким же API, как Datomic. Еще там есть запросы, можно на языке Datalog писать запросы к данным. Поскольку у них одинаковый интерфейс, то, если ты знаешь Datomic, —- ты знаешь Datascript. Это SQL-like, можно нагородить условия и получить результаты из клиентского хранилища. Я эту фичу не нашел особо полезной именно для интерфейса, но есть люди, которые находят полезное в этом.

Задача хранения состояния на клиенте в интерактивном приложении довольно распространенная. Роман: Расскажи, пожалуйста, чем Datascript уникален. Однако ты решил сделать свое решение. Кажется, что она уже должна быть где-то встроена либо в сам язык, либо в какой-то фреймворк. Что тебя побудило?

Это был интерес повторить уже существующую систему. Никита: Мне было интересно, смогу ли я сделать Datomic маленькими средствами. В мире JavaScript, наверное, есть подобные решения. Потом оказалось, что это на самом деле хорошая идея, и очень удобно организовывать и обращаться к state таким образом. Она, насколько я помню, решает проблему и синхронизации и хранения данных. Есть такая штука, как Relay. Она чем-то похожа, но, по-моему, позже появилась.

Rum

Алексей: Есть ещё одна твоя библиотека Rum. Расскажи про нее, пожалуйста.

React — это круто, здорово, и всем нравится. Никита: Rum — это просто обертка React для ClojureScript. Он Java script, а хочется его в Clojurescript использовать. Хочется использовать его b в ClojureScript использовать. Они предлагали свою модель, которая внутри использовала React. В ClojureScript мире было несколько решений, но они все были слишком концептуальные. То есть не чистый, а свою концепцию, которая использует React.

В итоге я пришел к конструкции, в которой Rum — это максимально прозрачный и тривиальный binding to React. Идея Rum возникла из того, что с этими предыдущими биндингами нельзя было использовать Datascript, а мне хотелось его использовать. Он ничего не прячет, можно добраться до нативных компонент React. Мы предлагаем все то же самое, что есть в React, только обернутое в удобный интерфейс для использования ClojureScript. Он агностик, как хочешь, так и организуй приложение и архитектуру.

Ты сказал, что это wrapper вокруг React. Роман: По рассказам знакомых верстальщиков, и в целом, поглядывая в сторону React и React Native, я вижу, какую бешеную популярность набирает твой проект. Как это удалось? А ведь React — огромный проект, и у тебя получилось сделать Rum очень маленьким, не изучая тонны исходников React.

Всё существенное, что есть в React, есть и в библиотеке Preact, которая занимает всего 3 КБ. Никита: React — не такой огромный проект на самом деле. У React достаточно маленький API плюс куча хаков под новые браузеры особого смысла изучать хаки нет, они все внутри React.

Роман: G6 — это часть React или нет?

Никита: Официально — нет, это отдельный компонент.

Роман: Ты ее портировал или оставил в стороне?

В ClojureScript код — это данные и всё такое, даже не нужно что-то подобное G6, уже есть свой синтаксис на основе векторов, который принят в Clojure-сообществе. Никита: Нет, не портировал, ее в ClojureScript никак не используешь. С помощью данных мы представляем то же самое, что в G6 делается макросом и предобработкой исходников.

Теперь предлагаю переключиться к следующему проекту. Роман: Здорово! Удивительно, что мой первый проект для MacOS тоже был menubar-приложением, которое показывало уведомления о новых письмах из Яндекс.Почты. Я очень удивился и обрадовался, когда увидел, что ты, Никита, писал проект AnyBar — menubar-приложение, которое показывает разные индикаторы возле часов. Я заглянул в исходник и прямо вернулся в прошлое на 8 лет назад.

Наверняка, им пользуются для каких-то прикладных задач. Проект очень простой и популярный. Расскажи, пожалуйста, как проект появился, как ты сам пользуешься или, возможно, ты знаешь, как кто-то им пользуется?

AnyBar

Никита: Проект появился достаточно случайно. Знаете, программисты любят что-нибудь заскриптовать, так и тут. Clojure-код не нужно компилировать, ClojureScript-код — нужно. Каждый раз, когда ты меняешь исходник, он перекомпилируется. Это занимает какое-то время: холодный старт занимает 30-40 секунд, например, а инкрементальный билд от одной секунды до десяти секунд. Ты поменял исходник, переключился в браузер, и еще не знаешь, уже можно смотреть или еще нужно подождать, потому что исходник не скомпилировался. Чтобы это знать, я придумал индикатор.

Приходится переключаться в терминал и ждать, пока в терминале всё скомпилируется, а потом переключаться в браузер. Работают все в основном на ноутбуках, места особо нет, куда это вывести. Если произошла ошибка, то не нужно обновлять страницу сто раз, все равно ничего не применится. Чтобы в терминал лишний раз не ходить, я сделал себе индикатор в menubar, который показывал статус билда: компилируется, скомпилировался, скомпилировался с ошибкой.

Чтобы поменять цвет индикатора в AnyBar, нужно просто послать UDP-пакет, это максимально простой способ кросс-коммуникации. Я не стал это делать как-то особо специфично, а сделал максимально универсально. Я использовал в основном только для билд статуса. Получается, что это супер гибкая штука, которую тривиально заскриптовать.

Чтобы можно было записаться на прием сразу, когда появляется свободное место. Кто-то, например, недавно писал в Твиттере, что он сделал индикатор статуса свободных мест в посольстве. Он выводился тоже в AnyBar.

Роман: Никита, это штука работает только на Localhost или моя виртуалка в Голландии может пингануть мой ноутбук и что-то высветится в menubar?

Никита: Если ты UDP-пакет сможешь послать из своей виртуалки для ноутбука, то да.

Но у меня вот такой вопрос: не обросло ли всё это дело набором готовых shell скриптов, готовых плагинов, которые можно даже не писать, а просто подключить, чтобы это всё сразу работало? Алексей: Услышав про холодное время сборки 30-40 секунд и дальше hot reload на секунду-две, я конечно, из Android мира могу только позавидовать.

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

grumpy.website

Алексей: Давай перейдем к следующему твоему проекту. Расскажи, что такое grumpy.website.

Мы собираем какие-нибудь косяки в интерфейсах компьютерных и некомпьютерных, обсуждаем, жалуемся что ли на них. Никита: grumpy.website — это такой блог про примеры плохих интерфейсов.

Алексей: Как давно этот сайт появился?

Никита: Около года назад.

Алексей: Насколько много людей там что-либо постит?

В основном постим мы, но еще человек пять периодически присылают свои предложения. Никита: Это авторский проект, у нас сейчас четыре автора. Это заняло эфире 13-14 выпусков, полные описания которых есть в Gist-е. Это совершенно кастомный движок, написанный на Clojure в прямом эфире моего YouTube-канала. Если интересно как создать веб-приложение на Clojure с нуля, можно посмотреть.

Роман: Этот курс завершился уже или проект еще дорабатывается и ты, по мере того как он дорабатывается, выкладываешь записи?

Он завершился до состояния, в котором сейчас находится grumpy.website — он в реальном времени. Никита: Это скорее видеоблог. То, что сейчас на сайте, то и в блоге.

Дизайн и юзабилити

Роман: Ты, как человек, который поднял проект про глупости проектирования интерфейсов, наверняка разбираешься в дизайне и в юзабилити. Как тебя в эту область занесло? Как ты этим заинтересовался?

Я занимался веб-проектами, и мне показалось, что, чтобы делать интерфейсы хорошо, нужно разбираться в том, что мы делаем. Никита: Это случилось довольно рано, еще в начале карьеры. Было какое-то ощущение, что с компьютерными интерфейсами не всё в порядке.

С одной стороны, для создания хороших интерфейсов нужно наметать глаз. Я прочитал несколько знаменитых книжек и это всё очень разумно звучало. Чтобы читать grumpy.website, не нужно быть экспертом по интерфейсу, здравого смысла достаточно, чтобы понять, что это ужас-ужас. С другой стороны — здравый смысл.

Но до этого я пользовался этими глупостями, этими недоработками и взгляд не цеплялся. Роман: Я помню, что прочитав книгу «Дизайн привычных вещей» Дональда Нормана, я действительно стал видеть больше.

Доклад на AppsConf

Алексей: Никита будет выступать на AppsConf с докладом «Обретение навыков». Никита, расскажи, как такой доклад появился.

Это модель обретения навыков, по которой каждый человек, изучая новую область, проходит через разные стадии: новичок, компетентный, специалист, эксперт, мастер. Никита: Я посмотрел доклад про модель дрейфуса. То есть именно относительно разных уровней программиста или программирования. Я пошел читать, разобрался, что это за уровни, чем отличаются, прикинул как это ложится на программирование и на всё, что я вижу вокруг программирования: курсы, книжки, дискуссии в интернете, устройство языков программирования. Стало гораздо четче и понятнее, что часто люди не могут прийти к общему мнению, потому что просто находятся сейчас на разных уровнях и не слышат друг друга. Оказалось, что это интересная модель, которая очень много чего объясняет.

Одни говорят — нужно, другие — не нужно. Пример, чтобы не совсем абстрактно: спор о том, нужно ли программисту знать алгоритмы или не нужно. Стало намного понятней, в том числе, как устроена карьера, как двигаться по карьерной лестнице. Почему они не могут договориться.

Например, как сделать или доработать свой шрифт. Роман: Никита, спасибо тебе большое за то, что согласился прийти к нам сегодня, было очень приятно с тобой поговорить, узнать и получить ответы на вопросы, которые меня лично интересовали. Оказывается, это вполне реально.

Мы уже отобрали все доклады и теперь колдуем над расписанием. Самая полезная конференция для мобильных разработчиков AppsConf всего через месяц — 8 и 9 октября. Подписывайтесь здесь или в рассылке (там только важные материалы: доклады, расшифровки и видео), чтобы получить информацию раньше всех.

Заглядывайте на YouTube-канал, там мы разнообразили видео выступлений прикольными приглашениями от будущих спикеров и Программного комитета.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть