-
Эта удивительная Clojure: что на ней разрабатывают, чем она отличается от других языков и подходит ли для входа в программирование
Эта статья была написана для одного издания, но по ряду причин ее не опубликовали. Размещаю здесь, чтобы материал не пропал. В подготовке статьи участвовали:
- Павел Пеганов и Иван Гришаев, программисты;
- Маша Даровская, редактор.
Мы расспросили разработчиков на Clojure из сообщества clojure_ru. Выясняли, как применяют язык, что на нём пишут, легко ли на нём программировать.
Что программируют на Clojure
Павел: сфера применения Clojure в техническом плане — в основном веб и серверные приложения. На успешно работающий Clojure-код можно посмотреть, например, в продуктах Metabase и Penpot, их исходный код открыт.
Но постепенно язык проникает и в другие области. ClojureScript работает в браузерах и других средах для JavaScript, с помощью проекта Esprit его уже запускают на микроконтроллерах, а сейчас развивают ClojureDart, чтобы захватывать мир Flutter. Конечно, не все эксперименты в итоге «взлетят», но такое разнообразие работающих проектов показывает, что применимость языка ограничена скорее настроениями разработчиков, чем самим языком.
Если говорить о предметных областях, то в вакансиях и проектах с Clojure, о которых слышу я, эмпирически кажется, что финтеха больше, чем прочих. Даже компания, поддерживающая Clojure, Cognitect, принадлежит банку Nubank. Но кроме финтеха областей тоже хватает.
Иван: сфера применения Clojure широка, она решает те же задачи, что Java, Python и другие языки. На ней пишут сетевые сервисы, бэкенд веб- и мобильных приложений. Clojure подходит для обработки данных из разных источников — баз данных, очередей, HTTP API — и часто служит их оркестратором.
Существует ClojureScript — компилятор кода на Clojure в JavaScript. С его помощью создают браузерный фронтенд и мобильные приложения на базе React Native.
Код на Clojure можно скомпилировать при помощи GraalVM и native image, получив бинарный файл. С этим подходом пишут утилиты командной строки, интерпретаторы, AWS Lambda и многое другое.
-
Сбер
Постоянно пользуюсь Сбером, чтобы оплатить что-нибудь по QR-коду. Каждый раз удивляюсь анимации вверху экрана. За каким-то хреном кнопка с QR не зафиксирована, а выезжает справа. Как в рекламных полях, где анимацию делают ради анимации: что-то выехало, покачалось, мигнуло. Типа, управление вниманием.
Зачем? Кто просил эту анимацию? На автомате тычу в левый угол, клик приходится на другой элемент, открывается что-то не то. Ясное дело, все тормозит, потому что параллельно загружаются другие виджеты.
Если функция кнопки известна заранее, а также ее текст и оформление, никакой анимации не нужно. Может, балбесу-дизайнеру из Сбера нравится: у него эмулятор на мощной тачке, запущено одно приложение, все плавно. А потребитель видит слайдшоу и промахивается кнопкой.
Дорогие дизайнеры!
Засуньтеуберите ваши анимации подальше. Они не нужны, вы делаете лишнюю и вредную работу. -
The Mask library for Clojure
(This is a copy of the readme file from the repository.)
Mask is a small library to prevent secrets from being logged, printed or leaked in any similar way. Ships tags for Clojure, EDN and Aero.
Why? Because I’ve been in such a situation three times, namely:
- We don’t mask the secrets.
- Someone logs the entire config.
- Secrets have leaked!
- Rotate all the keys, tokens, etc.
- Change the team and face the same.
This library is an attempt to break this vicious circle.
Installation
Leiningen/Boot:
[com.github.igrishaev/mask "0.1.0"]
Clojure CLI/deps.edn:
com.github.igrishaev/mask {:mvn/version "0.1.0"}
Usage
The
mask.core
namespace providesmask
andunmask
functions. Pass a value tomask
to make it safe for logging or printing in REPL:(in-ns 'mask.core) #namespace[mask.core] (def -m (mask "Secret123")) -m << masked >> (str "The password is " -m) "The password is << masked >>"
Masking is idempotent meaning that you can mask the same value multiple times but the result will be one-level masked value:
(-> -m mask mask mask) << masked >>
To release a value from a mask,
unmask
it:(unmask -m) "Secret123"
Unmasking is idempotent a well:
(-> -m unmask unmask unmask) "Secret123"
Note: the library treats
nil
as an error value that cannot be masked. You’ll get an exception:(mask nil) Execution error (IllegalArgumentException) at ... (core.clj:34). Cannot mask a nil value
Masking an empty value signals you’re doing something wrong. Most likely you’ve missed a corresponding key or an environment variable. Thus, the further work makes no sense.
Spec
The
mask.spec
module provides the::mask
spec that checks if a value is really masked. An example from the tests:(let [config {:username "Ivan" :password #mask "secret"}] (is (s/valid? ::config config))) ;; true
Clojure tag
The built-in
#mask
tag wraps any value with a mask:=> {:token #mask "abc123" :password "SecretABC"} {:token << masked >>, :password "SecretABC"}
EDN tag
There is a
reader-edn
function that acts like an EDN reader for the same tag:(let [source (-> "{:foo #mask 42}")] (edn/read-string {:readers {'mask reader-edn}} source)) ;; {:foo << masked >>}
Aero tag
To extend Aero with the
#mask
tag, import themask.aero
namespace:(require 'mask.aero)
Then read a config with the tag:
;; config.edn {:foo #mask #env "SOME_PASSWORD"} ;; code (aero/read-config (io/resource "config.edn")) ;; {:foo << masked >>}
The Aero dependency is not included. You’ve got to provide it by your own.
Ivan Grishaev, 2023
-
Эй
Почему-то большие компании не могут нормально составить письмо на русском. Будут ошибки или нелепые обороты. Сегодня получил письмо от Дискорда, где ко мне обращаются на “эй”:
Сам ты эй! Понятно, это дурацкая калька с английского hey. Почему не нашли русского чувака, чтобы показать ему перевод перед отправкой? В больших фирмах всегда найдется русский, украинец, поляк или венгр. Даже если он кодер, пусть посмотрит. Он скажет, что обращение на эй в русском не только неестественно, но даже грубо.
Жаль, не сохранил скриншоты Гугла и Godaddy. Иной раз такую дичь присылают, что хватаешься за волосы. Сейчас реже, но все равно.
-
Silent Hill 2
Появился трейлер переиздания Silent Hill 2 — одной из главных игр моего детства. Наверняка вы его смотрели, но вот на всякий случай:
Графика выше всяких похвал, сказать нечего. Однако у меня вопрос — что не так с этим трейлером? Не проматывая вниз, постарайтесь подумать.
-
Праздники
Не люблю праздники посреди недели. Скажем, по четвергам я хожу в зал, но завтра он не работает. Что мешает администратору прийти и открыть дверь, непонятно. И вообще — чем этот четверг отличается от других четвергов? Ничем, кроме дурацкого обычая, что в какой-то день вместо работы валяют дурака.
Хотел переставить на среду, но оказывается, что у жены на работе поменяли смены из-за предпраздничного дня. У старших детей укороченные уроки, и нужно ехать за ними раньше обычного.
Вот и думаешь: стоило ли переставлять кучу дел из-за одного праздника? Кому вообще всрались эти 23 Февраля, 8 Марта, Дни единства и все такое? Кто эти люди, что их празднуют? И как?
Выгоду от праздников получают только дети, потому что не надо идти в школу. Взрослые, даже если они бюджетники, должны что-то переставлять и отрабатывать, что еще то мучение и нарушение ритма.
Считаю, любой праздник должен автоматом переноситься на ближайший выходной — празднуй сколько хочешь, а другим не мешай работать. Исключение из этого правило одно — Новый год, его празднуют строго по календарю. Но это и так очевидно.
-
The DynamoDB library for Clojure
(This is a copy of the readme file from the repository.)
This library is a driver for DynamoDB written in pure Clojure. No AWS SDK, lightweight dependencies, GraalVM-friendly.
Benefits
- Free from AWS SDK. Everything is implemented with pure JSON + HTTP.
- Quite narrow dependencies: just HTTP Kit and Cheshire.
- Compatible with Native Image! Thus, easy to use as a binary file in AWS Lambda.
- Clojure-friendly: supports fully qualified keyword attributes and handles properly them in SQL expressions.
- Both encoding & decoding are extendable with protocols & multimethods.
- Raw API access for special cases.
- Specs for better input validation.
- Compatible with Yandex DB.
-
Удалил
Как и многие, я храню всякий хлам в канале Saved Messages в Телеграме: ссылки, идеи, напоминалки. Иногда перебрасываю креды к сервисам, если лень это делать через гист или менеджер паролей.
Только что случилась забавная вещь. Удаляя сообщение из Saved Messages, не заметил, как кликнул по каналу и удалил его. Нажимая на подтверждение, подумал, что диалог какой-то странный, но палец было не остановить. Вжух — и канала не стало со всеми сообщениями.
-
Баш-скрипты
Программист должен помнить не только о вещах, которые нужно делать. Обратное тоже важно: запоминайте, чего делать не нужно. Плохой опыт — тоже опыт. Он поможет не упасть там, где спотыкаются другие.
Одна из таких вещей — скрипты на баше. Я взял за правило: никаких баш-скриптов. Когда хочется написать на нем что-то длиннее пяти строк, закрывайте ноут и идите за молоком. Потом вернитесь и сделайте правильно — то есть без баша.
Шелл-скрипты хрупки и тяжелы в поддержке. Их трудно читать и отлаживать. Они опираются на сторонние утилиты, которые где-то есть, а где-то нет. Поведение зависит от платформы и флагов.
Шелл — это плавное погружение в смоляную яму, из которой не выбраться. Год-другой — и скрипт занимает экраны, никто не знает, как он работает. Лучше не трогать.
Замечу, что сказанное не относится к случаям, когда вызывают несколько утилит, соединяя их каналы, например:
> ps aux | grep java
Это совершенно нормально. Я имею в виду скрипты, где шелл выступает в роли настоящего языка программирования, то есть:
- активно используются переменные;
- циклы, ветвления, переходы;
- объявление и вызов функций;
- импорт других скриптов.
Все вместе это становится таким адом, что лучше уволиться, чем ворошить осиное гнездо (или, в английской идиоме — тараканье).
Кусок скрипта откуда-то из интернета:
no_more_args=1 ; -*) if [ "x$no_more_args" = "x1" ] ; then dirs[$num_dirs]="$1"; let "num_dirs++" else if [[ "x${1:1:1}" != "x-" && "x$1" =~ "x-.*a.*" ]] ; then no_dots=0; fi opts="$opts $1"; fi *) dirs[$num_dirs]="$1"; let "num_dirs++" esac shift
Для меня он выглядит как китайская грамота. Что такое
[ "x$no_more_args" = "x1" ]
? Что такое [[ "x${1:1:1}" != "x-" && "x$1" =~ "x-.*a.*" ]]
? Как это читать и поддерживать?Во времена Unix шелл-скрипты были прорывом, с этим никто не спорит. Однако время не стоит на месте, и созданы более удобные инструменты. Вот несколько примеров.
Если с проектом нужно делать много мелких вещей, подойдет make. Напомню, make — это набор именованных действий. Кроме вызова по отдельности их можно комбинировать, например собрать бек и фронт за раз. В любом проекте я завожу Makefile и помещаю туда все, что придет в голову, например:
- сборку проекта
- прогон тестов и миграций
- очистку временных файлов и папок
- запуск и остановку Докера
- много чего еще
Пример большого Make-файла можно посмотреть в репозитории с книгой.
Далее: если нужны действительно сложные скрипты, возьмите Питон. Даже на голом Питоне писать в разы легче, чем на баше. Для Питона созданы библиотеки с изящным API, чтобы вызывать процессы, двигать файлы и прочее.
Да, Питон требует окружения. Однако если у вас сложные скрипты, он окупается. Установку Питона и его барахла поместите в Makefile.
Для управления машинами лучше писать плейбуки на YAML. Кроме Ansible, полно других утилит для конфигураций.
Любые действия лучше задавать декларативно. Если вы пишете скрипты, скорей всего, вы не знаете о нужной утилите.
Еще одна интересная мысль — генерация шелла из другого языка. Подобно тому, как из TypeScript производят JavaScript, можно произвести шелл из условного Лиспа или Питона. Когда я работал в Exoscale, у нас была библиотека для генерации шелла на Кложе. Я ей не пользовался, но само наличие говорит о том, что в руководстве понимали риски ручного скриптописания.
Припомню только один случай, когда от шелла не отвертишься — это докер-контейнеры. Кроме голой операционки там ничего нет, поэтому сжимаешь булки и пишешь на шелле. Тянуть мегабайты Питона, конечно, будет плохим вариантом.
В общем, чем раньше вы замените шелл-скрипт на что-то профильное, тем лучше.
-
Спасибо, что
Кто читает Ильяхова, знает про “заранее спасибо”. Это когда пытаются манипулировать — свалить задачу, важное дело — и как бы проявляют вежливость.
Сделай до понедельника, заранее спасибо. Напиши за ночь, заранее спасибо. Заполни сто полей, подтверди почту и телефон, заранее спасибо. Словом, все то, после чего думаешь “нет уж, спасибо”.
В определенных кругах “заранее спасибо” стало мемом. Например, обращаешься за помощью и в шутку добавляешь эту фразу. Только важно убедиться, что человек на том конце понимает юмор.
У “заранее спасибо” есть другая форма: “спасибо, что”. Как и в случае с оригиналом, она вызывает одну мысль — я и не собирался. Лучше объясни, что делать, а не благодари.
— Спасибо, что закрыли дверь!
— Поставь доводчик.
— Спасибо, убрали за собой!
— Что убирать? Куда убирать? Как быть с жидкостями?
Оказалось, “спасибо, что” есть и в английском языке. Когда-то давно я переписывался с коллегой, и он сказал “…and thank you for doing bla-bla-bla”. Я слегка удивился: вроде ничего не делал, а коллега благодарит. Начал было объяснять, но потом понял — это просто формулировка. Поручение в замысловатой форме, а thank you для того, чтобы я не обиделся. Типа такая вежливость. Bla-bla-bla я в итоге сделал, но душок от манеры речи остался.
Надо ли говорить, что подобные вещи хоть в русском, хоть в английском языке смотрятся плохо. Общее между ними одно — манипуляция. Просьбу всегда следует выражать явно. Затасканная фраза вместо прикрытия дает фальшь.