Fennel - зачем, где и как
Привет!
Я - Андрей Листопадов (andreyorst), один из разработчиков языка Fennel
Мой вклад в язык:
?.
оператор безопасного лукапа по вложенным таблицам,fcollect
макрос для диапазонного компрехеншена,- более лисповый и настраиваемый pretty-printer,
- расширение API работы с метаданными функций, и
- улучшения для интерактивной работы с REPL
Темы сегодняшнего доклада
- Что такое Fennel и чем он не является
- Какие фичи делают этот язык оправданным
- Особенности компилятора
- Поддержка редакторами
- Где можно применить Fennel
- Альтернативы
Lua? Fennel!
Что такое Fennel
- Fennel - не Lisp*
- Компилятор в Lua, с лисп макросами
- Библиотека, для ембеддинга в Lua проекты
- Lua, как платформа, Lisp, как фронтенд
- Прямой интероп в обе стороны
- Отсутствие своего рантайма
Фичи, на мой взгляд, оправдывающие существование Fennel
- Более регулярный синтаксис (S-expressions)
- Исправление проблем Lua
- local-by-default
- Отдельные формы для числовой итерации и итераторов:
-- Lua ;; fennel for i=1,10 do (for [i 1 10] print(i) (print i)) end for k,v in pairs(t) do (each [k v (pairs t)] print("key:" k, "val:" v) (print "key:" k "val:" v)) end
- Отдельный синтаксис для секвенций и таблиц:
[]
,{}
- Отказ от statement’ов в пользу expression’ов
- Расширение языка дополнительными операторами
- Возможность дальнейшего расширения через макросы
Подробнее о фичах
Таблицы
- Синтаксис таблиц во многом опирается на опыт Clojure
- Секвенции, они же массивы пишутся в квадратных скобках
[1 "2" :foo [:bar (fn baz [] "qux")]]
- Таблицы или хэшмапы в фигурных:
{:foo "bar" :baz {:qux 42}}
- Но это все еще одна и та же структура данных.
- Списки, на самом деле, тоже таблицы.
Выражения
Немного о коде и его структуре
- Fennel использует круглые скобки для обозначения исполняемого кода,
- Единственное исключение - биндинг множественных значений.
- Язык расширяется дополнительными формами, отсутствующими в Lua:
let
,when
,each
,case
и пр.
- Всё является выражениями.
Пример кода на Fennel:
(fn add [a b ...]
(let [sum (+ a b)]
(if ...
(add sum ...)
sum)))
(add 1 2 3 4)
Использование if
как экспрешшена:
(print (if (> (math.random 100) 50) :heads :tails))
Уход от IIFE:
(let [side (if (> (math.random 100) 50) :heads :tails)]
(print side))
Деструктурирование и паттерн матчинг
-
Destructuring:
(local t {:a 1 :b 2}) (let [{:a a :b b} t] (+ a b)) (fn normalize [[a b]] (let [len (math.sqrt (+ (* a a) (* b b)))] [(/ a len) (/ b len)])) (normalize [3 4])
- Вложенное разбиение
(local t [:a :b {:x 1 :y 2}]) (let [[key1 key2 {: x : y}] t] {key1 x key2 y}) ;; {:a 1 :b 2}
- Вложенное разбиение
-
Pattern matching:
(case (request) {:status :ok :data data} (process data) {:status :warn :message msg} (io.stderr:write msg "\n") {:status _} (error (.. "unexpected status: " _)))
Макросы
- lisp-style quote, unquote
(macro inc [var*] (assert-compile (sym? var*) "must pass a symbol" var*) `(do (set ,var* (+ ,var* 1)) ,var*)) (var x 10) (inc x) ;; returns 11 (print x) ;; 11
- Автоматическая проверка на hygiene
(macro no-capture [...] `(let [answer 1337] ,...)) (let [answer 42] (no-capture (print (.. "Answer to the Ultimate Question of Life, " "the Universe, and Everything: " answer))))
- Манипуляции кодом, как таблицами
(macro loop [binding-vec ...] (let [keys [] gensyms [] bindings []] (each [i v (ipairs binding-vec)] (when (= 0 (% i 2)) (let [key (. binding-vec (- i 1)) gs (gensym (tostring i))] (table.insert gensyms gs) (table.insert keys key) (doto bindings (table.insert gs) (table.insert v) (table.insert key) (table.insert gs))))) `(let ,bindings ((fn ,(sym :recur) ,keys ,...) ,(unpack gensyms))))) (loop [x 10] (when (> x 0) (print x) (recur (- x 1))))
Недостатки макро системы fennelа:
- Макросы не могут быть эксптртированы из того же файла, что и функции.
- Не поддерживается вложенный квазиквотинг и анквотинг.
- Нет unquote-splicing (таблицы всё таки).
- Сложности с использованием обычных функций из макросов.
- Сложности с поставкой библиотек, содержащих макросы.
Поддержка редакторами
Редакторы для Fennel
- Emacs + fennel-mode
- Nvim + conjure
- Kakoune
- VSCode + vsc-fennel
- Vis
- все что поддерживает LSP и TreeSitter*
Редакторы, конфигурируемые на fennel
Поддержка инструментами
Благодаря расширяемости require
системы Lua, Fennel поддердивается
практически всем, что предоставляет доступ к package
:
fennel = require("lib.fennel")
table.insert(package.loaders, fennel.searcher)
-- или
require("fennel").install()
Феннел можно использовать с:
Альтернативы
Спасибо
Материалы по теме:
- fennel-lang.org - официальный сайт языка, wiki, reference
- fennel-lang.org/see - онлайн декомпилятор Lua в Fennel
- #fennel на irc.libera.chat и Matrix
- andreyor.st - мой блог о fennel’е, Emacs’е и многом другом