16 апреля зарелизился ClojureDart, а это значит, что для любителей Clojure открылась возможность писать мобильные, веб- и десктоп-приложения на Flutter. Зачем использовать для этого Clojure, как бы очевидно это ни было, выходит за границы фокуса статьи.
На текущий момент инструменты еще не отшлифованы, нет репла(!) и автодополнений для dart-интеропа, но пользоваться можно, и некоторые плюшки кложуры уже показали себя (например, nest-макрос, убирающий проблему вложенности, а вот код side-by-side).
В этой статье хочу рассказать, как написать свое первое flutter-приложение на Clojure, какими инструментами удобно пользоваться, где искать ответы на вопросы. Статья для тех, кто имеет хотя бы минимальный опыт работы с Clojure.
И еще, статья будет поддерживаться на гитхабе.
Tools
Ставим Flutter по официальной инструкции и cli для Clojure. Дополнительно не помешает clj-kondo, через который сейчас работают ворнинги для макроса apha/widget.
Вот как это выглядит:
Выбор редактор кода
На данный момент нет поддержки интеропа с дартом, и приходится использовать хаки, о которых подробнее расскажу ниже, но сперва немного о редакторах.
VSCode — быстрый старт
Я не пользователь VSCode, но насколько мне известно, это самый простой способ начать. Устанавливаем Calva, и все заработает само:
Не помешает поставить Flutter extenstion, подробнее тут.
Плюсы:
скорость и простота использования;
уникальный плагин Joyride, позволяющий писать настройки редактора с реплом на Clojure.
Intellij Idea — ожидаем поддержку
Результат, на который можно рассчитывать сейчас:
Есть автодополенения для Clojure (пример с reduce-
выше), но интеропа с дартом нет и отключить эти ворнинги нельзя.
Плюсы:
почти ничего не нужно настраивать и чинить;
поддержка придет в ближайшем будущем, и все заработает без вашего участия.
Минусы:
экстремально медленно открывается проект (до минуты на hello-world на моем air-е, с вимом я жду 125 мс);
нет возможности настроить подсветку и автодополнения (только выключить);
Чтобы настроить поддержку *.cljd, необходимо указать расширение для clojure:
preferences -> editor -> filetypes -> clojure file -> file patterns -> + -> *.cljd
Поддержка Flutter настраивается плагинами для Дарта и Флаттера, подробнее тут.
Поддержка ворнингов clj-kondo работает с плагином clojure-extras.
Vim — кастомизация
Тот же пример с reduce-
Плюсы:
скорость открытия, возможность открывать с десяток(сотню) проектов одновременно за миллисекунды;
расширенная кастомизация;
репл сам интегрируется в работу с выходом репла для ClojureDart;
возможность писать конфиги на лиспе (см. ниже).
Минусы:
нужно научиться пользоваться вимом;
нужно потратить время и настроить все самому;
иногда нужно заниматься починкой после обновления плагинов и самого вима.
Есть несколько опций, как завести вим для ClojureDart.
Nvim с lsp
Ставим плагин и 2 сервера — для Clojure и Flutter (полезные для флаттер-разработки команды + базовый функционал для работы с дартом).
Прописываем настройку для распознавания *.cljd как Clojure:
au! BufRead,BufNewFile *.cljd setfiletype clojure
Я сейчас пользуюсь такой конфигурацией, и за исключением интеропа с дартом в ней есть практически все (рефакторинг, goto definition, find usages, автодополнения, документация, чистка неймспесов) .
Мой конфиг описан на Fennel. Подробный разбор за рамками статьи, но если все же захочется использовать лисп, то я бы посоветовал начать с плагина aniseed и дотфайлов автора. В качестве начального конфига так же можно взять этот проект, либо готовую сборку nyoom.
Vim/nvim с плагином VimIced
Нужно настроить VimIced по инструкции и подключить clj-kondo в качестве линтера (по этой инструкции). Пока еще нет ClojureDart-репла, большей частью функционала будет пользоваться нельзя.
Fireplace
Не проверял этот плагин, но наверняка будет работать сопоставимо с VimIced, если где-то в виме прописать, что cljd -> это кложур.
au! BufRead,BufNewFile *.cljd setfiletype clojure
Emacs
Разработчики ClojureDart пользуются Emacs, в Clojure-mode уже добавили распознавание *.cljd. На мой взгляд, плюсы и минусы сопоставимы с вимом, а остальное — на любителя.
Workflow
Рекомендую начать с hello-world, чтобы убедиться, что все работает.
В процессе работы удобно держать открытыми (под рукой) 3 окна:
Любой файл на дарте (можно создать дарт-файл в директории lib/), чтобы тут же экспериментировать, смотреть сигнатуры, методы, поля и пр.
Терминал, чтобы видеть логи и взаимодействовать с проектом.
Эмулятор (по моему опыту андроид-эмулятор стабильнее и быстрее веб-версии). Самый простой способ настроить его — установить AndroidStudio, и воспользоваться Tools->DeviceManagement.
После запуска "watcher"-а:сlj -M -m cljd.build flutter
Входим в петлю интерактивной разработки: пишем код, сохраняем файл, видим результат, снова пишем код...
Если нужен hot-reload, сохраняем файл.
Если нужен hot-restart, жмем Enter в терминале.
Если все перестает работать, удаляем папку .clojuredart/ и перезапускаем "watcher".
Использование библиотек
Как и в обычным Flutter-проекте, добавляем библиотеки в pubspec.yaml файл. Импортируем строкой, как и в *.dart файлах в формате, как в обычном Clojure:
(:require
[clojure.string :refer [join]]
["package:graphql/client.dart" :as g])
Единственный способ использовать свои dart-классы — выложить их отдельный проектом и добавить в pubspec.yaml. Возможно, в будущем добавят поддержку.
Подход в построении дерева виджетов отличается от Flutter на Dart
Практически всегда имеет смысл создать функцию (как в примерах:2 от авторов), а не объявить класс с deftype (как я сделал тут:4). Для создания же StatefulWidget удобнее всего использовать макрос alpha/widget.
ClojureDart отличается от Clojure
В первую очередь, рекомендую прочесть доку от авторов.
Другое отличие, с которым вы обязательно столкнетесь, это отсутствие библиотек, которые бы переводили данные в старые-добрые кложуровские PersistentHashMap. В примере(3) с GraphQL я написал функцию для перевода дартовой Map в кложуровую PersistentHashMap.
Куда идти с вопросами?
Сообщество сидит в слеке clojurians, канал #clojuredart. Большую часть вопросов уже задали, и можно "загуглить" их в чате. Авторы активно помогают, отвечают на все вопросы. Можно даже залить проект на гитхаб, и попросить совета/помощи в чате. Кто-то обязательно посмотрит.
Полезные ресурсы
Официальная документация по ClojureDart.
Примеры кода от авторов.
Пример приложения c GraphQL и TEA-архитектурой.
Пример написания кастомных виджетов с deftype.
Сообщество в Слеке — clojurians, канал #clojuredart