В Run Loop приглашают тех, кто делает классные продукты своими руками. Никита Прокопов (tonsky) — человек и пароход, успел сделать несколько OpenSource проектов, которыми с удовольствием пользуются другие люди.
В первую очередь поговорим о шрифте FiraCode, о Clojure, и совершенно разных проектах, например, обертке ClojureScript для React. А потом перейдем к более общим рассуждениям о хороших интерфейсах, здравом смысле, и моделях обучения программистов.
О госте: Никита Прокопов примечателен тем, что создал FiraCode, внёс заметный вклад в развитие Clojure сообщества и опубликовал в OpenSource такие проекты как Datascript и Rum. Помимо этого он пишет на Objective-C под macOS: программа AnyBar подскажет о наступлении какого-либо события в statusbar, ой, menubar вашего компьютера.
Ведущие: Роман Бусыгин (разработчик Яндекс.музыки для iOS) и Алексей Милеев (App in the Air).
Никита: Я программист из Новосибирска, но сейчас живу в Москве. Занимался веб-проектами в основном, увлекаюсь интерфейсами. Последнее время программирую на Clojure.
Роман: Предлагаю сегодняшнюю беседу построить вокруг твоих Open Source и публичных проектов. Начать хочу с первого и самого интересного для меня — это шрифт FiraCode. Им пользуется множество моих коллег и я сам. Мне всегда было интересно, как сделать свой собственный шрифт, как это происходит, есть ли специальная программа, какими знаниями нужно обладать, чтобы сделать свой шрифт. Расскажи, пожалуйста, об этом.
FiraCode
Никита: Не совсем корректно говорить, что это мой шрифт. Я взял готовый моноширинный шрифт и дорисовал лигатуры, буквы сам я не рисовал. Чтобы сделать сами буквы, нужен огромный опыт и усидчивость. Это очень сложно, там миллион неочевидных тонкостей. Есть любительские шрифты и всегда видно, что они корявые, но не всегда понятно, в чём эта корявость.
Я взял хороший шрифт FiraMono от Mozilla и дорисовал туда лигатуры, для этого есть несколько программ. Я использовал достаточно популярную программу Glyphs. В шрифтовом дизайне я не эксперт, поэтому посмотрел, что другие используют, и тоже это использовал. Сюрпризом может стать, что она довольно дорогая, но мне дали лицензию на разработку FiraCode. Glyphs похож на векторный графический редактор. Открываешь, там есть буквы, находишь ячейки и рисуешь картинки. Плюс всякие тонкости, связанные со шрифтами, например, что у букв есть размер, точки привязки, переходы от жирного к нежирному. Всё достаточно просто.
Интересно то, что внутри шрифта OpenType есть язык программирования, на основе которого сделана эта замена лигатур. Изначально его создали, как средство для реализации всяких хитрых штукенций в шрифтах. Например, лигатур, когда fi, ffi и подобное в обычных шрифтах заменяются на лигатуры. Плюс там бывают вариабельности, концевые начертания у букв: если буква стоит в начале и конце слова, к ней дорисовывается какой-нибудь хвостик. Это всё можно запрограммировать с помощью хитрых паттернов, и также лигатур, которые сделал.
Роман: Сколько примерно заняла времени доработка FiraMono до того вида FiraCode, которым сейчас все пользуются?
Никита: Посчитать будет сложно. Первая версия была не очень сложная. Как всегда, началось всё довольно быстро, но какое-то время ушло на то, чтобы разобраться, что происходит, как это реализовать: примеров особо не было.
Роман: То есть было ещё и несколько версий. Чем они отличались?
Никита: Да, и версии обновляются. Во-первых, я дорисовываю новые лигатуры, убираю старые иногда, если они в чём-то конфликтуют. В какой-то момент там поменялся механизм для отрисовки лигатур. Там внутри есть очень интересная алгоритмическая задача: есть длинная последовательность символов, из нее нужно вычленить те комбинации, из которых состоят лигатуры, но если они пересекаются, то должны быть другая замена. У меня даже есть программка на Clojure, которая генерирует набор правил, которые потом вставляются в шрифт — довольно сложно. Вот эта штука поменялась, поменялся способ рендеринга лигатур. Раньше они заменялись на один символ, сейчас заменяются на три. По сути, для пользователя он становится лучше, меньше багов, больше лигатур.
Роман: Интересно. Я никогда не думал, что в шрифтах могут быть баги. Правила, по которым отрисовщик решает, когда вставлять лигатуры, а когда нет, описываются на специальном языке программирования или просто декларативно?
Никита: Декларативно, а потом внутри транслируется достаточно эффективно в таблицу замены.
Набор для первой версии лигатур
Роман: Скажи, когда ты придумывал набор лигатур, ты смотрел на какие-то конкретные языки программирования или есть более-менее общий перечень того, что нужно людям в повседневной жизни?
Никита: Эту идею я придумал не сам. Я увидел, что есть шрифт Hasklig, сделанный специально для Haskell. Но Haskell мне не был нужен, поэтому я подумал что надо сделать такой же шрифт, но, во-первых, основанный на том шрифте, который мне нравятся, во-вторых, для любого языка. Дальше я взял просто всё, что мне пришло в голову. Сначала очевидные вещи: <=, >=, ->, <-. Пишешь как в С, а оно заменяется на стрелочку. По-моему, с этого всё началось.
Clojure
Алексей: В самом начале ты упомянул, что сейчас пишешь на Clojure. Расскажи, как ты пришел к Clojure, с чего всё начиналось, как ты пришел именно к этому языку?
Никита: Я посмотрел несколько лекций Рича Хикки (рекомендую, например, эту и эту). Они прикольные и универсальные на тему, как у программиста работает голова, грубо говоря, как проектировать системы. Лекции открыли для меня огромное количество новой важной концентрированной информации, и я зафанател от Рича Хикки. Потом заинтересовался тем, что же он сделал. Оказалось, что он сделал язык Clojure. Пошел читать, разобрался и всё — понеслось.
Роман: Меня тоже можно записать в поклонники Рича Хикки. Я видел его выступления: они и длинные, и что самое важное, интересные. То есть он умудряется держать внимание слушателей на протяжении долгого времени.
Алексей: Что именно тебе нравится в Clojure?
Никита: На последней конференции Рич Хикки сделал такую ремарку:
Clojure — это язык для старых, уставших программистов.
К нему не приходят в начале карьеры, а он становится интересен уже после 10-15 лет. Во-первых, у тебя абсолютная свобода. Всё, что тебе нужно, ты делаешь сам или берешь из библиотек. В самом языке зашито минимум вещей. Всё остальное ты конструируешь, делаешь ровно так, как тебе нужно. Всё самодельное и можно поменять. Это опасно, когда ты новичок, но полезно, если ты уже эксперт и знаешь то, что тебе нужно.
В этом языке максимально четко и компактно выражается мысль. То есть минимальный разрыв между сложностью того, что ты пытаешься выразить и сложностью того, как это записано. В Java, например, ты можешь делать какую-то совершенно тривиальную вещь, но она растянется на 10 строчек, и ты устанешь. В Clojure, если вещь тривиальная, то она и записана, скорее всего, тривиально, в одну-две строчки.
Роман: Я понял, что ты ответил и на мой вопрос. Когда я услышал, что Clojure не накладывает на тебя никаких ограничений, мне почему-то сразу вспомнился С, который тоже как угодно стоял на ушах. Но потом я услышал ответ, что всё-таки это не просто синтаксический сахар, но и удобный, компактный язык, который позволяет выражать свои мысли меньшим количеством кода.
Никита: Да, он очень высокоуровневый, а С — низкоуровневый.
Алексей: Мне интересно, разве такая свобода языка не мешает работать в больших проектах с большим количеством людей. Или в OpenSource, где нужно тщательно отслеживать каждое изменение? Насколько я слышал, в Clojure разрешены вещи вплоть до изменения синтаксиса языка. Разве это не мешает?
Мне это напоминает ситуацию со Scala, когда язык умеет много всего, и в каждом большом проекте все обкладываются какими-то своими граблями. В итоге приходят к какой-то общей практике, что этим мы пользуемся, а этим нет. В итоге Scala в одном проекте и Scala в другом — это две разные Scala. Нет ли такой проблемы в Clojure?
Никита: Я в особо больших проектах я не работал. В тех, в которых работал, были легкие вариации. На самом деле почему-то не происходит такого прям разброда. Мне кажется, это потому что в Scala, допустим, есть любители потеоретизировать и порассуждать, как наиболее правильно что-то сделать. Как ты сказал, там много всего — ты можешь выбрать не то. В Clojure, наоборот, мало всего — ты не можешь выбрать не то. Это очень практичный язык. На Clojure не любят городить абстракции ради чего-то на будущее. Если нужно, например, что-то распечатать, то ты просто максимально практично печатаешь и не паришься о том, откуда к тебе пришел принтер, какой интерфейс, протокол ты реализуешь. Делаешь конкретно то, что нужно. Поэтому эта практичность — наверное, общий знаменатель.
Проекты на Clojure
Роман: Насколько я могу видеть, у тебя на GitHub есть несколько проектов на Clojure. Расскажи о них подробнее.
Datascript
Никита: Первый мой относительно успешный OpenSource проект — это Datascript. Это клиентский storage для браузера. Для сайта это, наверное, не очень полезно, а если пишете какое-то интерактивное приложение в браузере, то вам нужно хранить где-то состояния. Datascript — это как раз хранилище для состояний. Его фишки:
- Он имутабельный. То есть он не уничтожает предыдущие версии, просто создается достаточно эффективно новая копия storage.
- Он сортированный. Он автоматически поддерживает индексы по атрибутам, по любым entity_id и так далее. Он позволяет быстро найти все, что нужно. всего, чего тебе нужно.
- Он плоский. Если ты особо не задумывался над тем, как реализовать storage на клиенте, то первое, что ты сделаешь, это структуру вложенных JSON. В Datascript storage плоский, в любой момент можно перейти в любое место и найти то, что нужно.
В общем, это относительно удобный доступ к данным, причем двусторонний: ты можешь от родителя к ребенку перейти, а можешь также от ребенка к родителю.
Для сообщества Clojure у него есть еще одно преимущество: он сделан с таким же API, как Datomic. Datomic — это база данных Clojure. Поскольку у них одинаковый интерфейс, то, если ты знаешь Datomic, —- ты знаешь Datascript. Еще там есть запросы, можно на языке Datalog писать запросы к данным. Я эту фичу не нашел особо полезной именно для интерфейса, но есть люди, которые находят полезное в этом. Это SQL-like, можно нагородить условия и получить результаты из клиентского хранилища.
Роман: Расскажи, пожалуйста, чем Datascript уникален. Задача хранения состояния на клиенте в интерактивном приложении довольно распространенная. Кажется, что она уже должна быть где-то встроена либо в сам язык, либо в какой-то фреймворк. Однако ты решил сделать свое решение. Что тебя побудило?
Никита: Мне было интересно, смогу ли я сделать Datomic маленькими средствами. Это был интерес повторить уже существующую систему. Потом оказалось, что это на самом деле хорошая идея, и очень удобно организовывать и обращаться к state таким образом. В мире JavaScript, наверное, есть подобные решения. Есть такая штука, как Relay. Она, насколько я помню, решает проблему и синхронизации и хранения данных. Она чем-то похожа, но, по-моему, позже появилась.
Rum
Алексей: Есть ещё одна твоя библиотека Rum. Расскажи про нее, пожалуйста.
Никита: Rum — это просто обертка React для ClojureScript. React — это круто, здорово, и всем нравится. Хочется использовать его b в ClojureScript использовать. Он Java script, а хочется его в Clojurescript использовать. В ClojureScript мире было несколько решений, но они все были слишком концептуальные. Они предлагали свою модель, которая внутри использовала React. То есть не чистый, а свою концепцию, которая использует React.
Идея Rum возникла из того, что с этими предыдущими биндингами нельзя было использовать Datascript, а мне хотелось его использовать. В итоге я пришел к конструкции, в которой Rum — это максимально прозрачный и тривиальный binding to React. Мы предлагаем все то же самое, что есть в React, только обернутое в удобный интерфейс для использования ClojureScript. Он ничего не прячет, можно добраться до нативных компонент React. Он агностик, как хочешь, так и организуй приложение и архитектуру.
Роман: По рассказам знакомых верстальщиков, и в целом, поглядывая в сторону React и React Native, я вижу, какую бешеную популярность набирает твой проект. Ты сказал, что это wrapper вокруг React. А ведь React — огромный проект, и у тебя получилось сделать Rum очень маленьким, не изучая тонны исходников React. Как это удалось?
Никита: React — не такой огромный проект на самом деле. Всё существенное, что есть в React, есть и в библиотеке Preact, которая занимает всего 3 КБ. У React достаточно маленький API плюс куча хаков под новые браузеры особого смысла изучать хаки нет, они все внутри React.
Роман: G6 — это часть React или нет?
Никита: Официально — нет, это отдельный компонент.
Роман: Ты ее портировал или оставил в стороне?
Никита: Нет, не портировал, ее в ClojureScript никак не используешь. В ClojureScript код — это данные и всё такое, даже не нужно что-то подобное G6, уже есть свой синтаксис на основе векторов, который принят в Clojure-сообществе. С помощью данных мы представляем то же самое, что в G6 делается макросом и предобработкой исходников.
Роман: Здорово! Теперь предлагаю переключиться к следующему проекту. Я очень удивился и обрадовался, когда увидел, что ты, Никита, писал проект AnyBar — menubar-приложение, которое показывает разные индикаторы возле часов. Удивительно, что мой первый проект для MacOS тоже был menubar-приложением, которое показывало уведомления о новых письмах из Яндекс.Почты. Я заглянул в исходник и прямо вернулся в прошлое на 8 лет назад.
Проект очень простой и популярный. Наверняка, им пользуются для каких-то прикладных задач. Расскажи, пожалуйста, как проект появился, как ты сам пользуешься или, возможно, ты знаешь, как кто-то им пользуется?
AnyBar
Никита: Проект появился достаточно случайно. Знаете, программисты любят что-нибудь заскриптовать, так и тут. Clojure-код не нужно компилировать, ClojureScript-код — нужно. Каждый раз, когда ты меняешь исходник, он перекомпилируется. Это занимает какое-то время: холодный старт занимает 30-40 секунд, например, а инкрементальный билд от одной секунды до десяти секунд. Ты поменял исходник, переключился в браузер, и еще не знаешь, уже можно смотреть или еще нужно подождать, потому что исходник не скомпилировался. Чтобы это знать, я придумал индикатор.
Работают все в основном на ноутбуках, места особо нет, куда это вывести. Приходится переключаться в терминал и ждать, пока в терминале всё скомпилируется, а потом переключаться в браузер. Чтобы в терминал лишний раз не ходить, я сделал себе индикатор в menubar, который показывал статус билда: компилируется, скомпилировался, скомпилировался с ошибкой. Если произошла ошибка, то не нужно обновлять страницу сто раз, все равно ничего не применится.
Я не стал это делать как-то особо специфично, а сделал максимально универсально. Чтобы поменять цвет индикатора в AnyBar, нужно просто послать UDP-пакет, это максимально простой способ кросс-коммуникации. Получается, что это супер гибкая штука, которую тривиально заскриптовать. Я использовал в основном только для билд статуса.
Кто-то, например, недавно писал в Твиттере, что он сделал индикатор статуса свободных мест в посольстве. Чтобы можно было записаться на прием сразу, когда появляется свободное место. Он выводился тоже в AnyBar.
Роман: Никита, это штука работает только на Localhost или моя виртуалка в Голландии может пингануть мой ноутбук и что-то высветится в menubar?
Никита: Если ты UDP-пакет сможешь послать из своей виртуалки для ноутбука, то да.
Алексей: Услышав про холодное время сборки 30-40 секунд и дальше hot reload на секунду-две, я конечно, из Android мира могу только позавидовать. Но у меня вот такой вопрос: не обросло ли всё это дело набором готовых shell скриптов, готовых плагинов, которые можно даже не писать, а просто подключить, чтобы это всё сразу работало?
Никита: Не обросло, потому что я этим не особо и занимался. Я даже удивлен, что кому-то это ещё интересно. Там куча всего: куча идей, куча клонов даже. Люди хотят текст выводить или несколько индикаторов и так далее. Я все это собирался сделать, но руки никак не доходят.
grumpy.website
Алексей: Давай перейдем к следующему твоему проекту. Расскажи, что такое grumpy.website.
Никита: grumpy.website — это такой блог про примеры плохих интерфейсов. Мы собираем какие-нибудь косяки в интерфейсах компьютерных и некомпьютерных, обсуждаем, жалуемся что ли на них.
Алексей: Как давно этот сайт появился?
Никита: Около года назад.
Алексей: Насколько много людей там что-либо постит?
Никита: Это авторский проект, у нас сейчас четыре автора. В основном постим мы, но еще человек пять периодически присылают свои предложения. Это совершенно кастомный движок, написанный на Clojure в прямом эфире моего YouTube-канала. Это заняло эфире 13-14 выпусков, полные описания которых есть в Gist-е. Если интересно как создать веб-приложение на Clojure с нуля, можно посмотреть.
Роман: Этот курс завершился уже или проект еще дорабатывается и ты, по мере того как он дорабатывается, выкладываешь записи?
Никита: Это скорее видеоблог. Он завершился до состояния, в котором сейчас находится grumpy.website — он в реальном времени. То, что сейчас на сайте, то и в блоге.
Дизайн и юзабилити
Роман: Ты, как человек, который поднял проект про глупости проектирования интерфейсов, наверняка разбираешься в дизайне и в юзабилити. Как тебя в эту область занесло? Как ты этим заинтересовался?
Никита: Это случилось довольно рано, еще в начале карьеры. Я занимался веб-проектами, и мне показалось, что, чтобы делать интерфейсы хорошо, нужно разбираться в том, что мы делаем. Было какое-то ощущение, что с компьютерными интерфейсами не всё в порядке.
Я прочитал несколько знаменитых книжек и это всё очень разумно звучало. С одной стороны, для создания хороших интерфейсов нужно наметать глаз. С другой стороны — здравый смысл. Чтобы читать grumpy.website, не нужно быть экспертом по интерфейсу, здравого смысла достаточно, чтобы понять, что это ужас-ужас.
Роман: Я помню, что прочитав книгу «Дизайн привычных вещей» Дональда Нормана, я действительно стал видеть больше. Но до этого я пользовался этими глупостями, этими недоработками и взгляд не цеплялся.
Доклад на AppsConf
Алексей: Никита будет выступать на AppsConf с докладом «Обретение навыков». Никита, расскажи, как такой доклад появился.
Никита: Я посмотрел доклад про модель дрейфуса. Это модель обретения навыков, по которой каждый человек, изучая новую область, проходит через разные стадии: новичок, компетентный, специалист, эксперт, мастер. Я пошел читать, разобрался, что это за уровни, чем отличаются, прикинул как это ложится на программирование и на всё, что я вижу вокруг программирования: курсы, книжки, дискуссии в интернете, устройство языков программирования. То есть именно относительно разных уровней программиста или программирования. Оказалось, что это интересная модель, которая очень много чего объясняет. Стало гораздо четче и понятнее, что часто люди не могут прийти к общему мнению, потому что просто находятся сейчас на разных уровнях и не слышат друг друга.
Пример, чтобы не совсем абстрактно: спор о том, нужно ли программисту знать алгоритмы или не нужно. Одни говорят — нужно, другие — не нужно. Почему они не могут договориться. Стало намного понятней, в том числе, как устроена карьера, как двигаться по карьерной лестнице.
Роман: Никита, спасибо тебе большое за то, что согласился прийти к нам сегодня, было очень приятно с тобой поговорить, узнать и получить ответы на вопросы, которые меня лично интересовали. Например, как сделать или доработать свой шрифт. Оказывается, это вполне реально.
Самая полезная конференция для мобильных разработчиков AppsConf всего через месяц — 8 и 9 октября. Мы уже отобрали все доклады и теперь колдуем над расписанием. Подписывайтесь здесь или в рассылке (там только важные материалы: доклады, расшифровки и видео), чтобы получить информацию раньше всех.
Заглядывайте на YouTube-канал, там мы разнообразили видео выступлений прикольными приглашениями от будущих спикеров и Программного комитета.
Автор: YourDestiny