7.0 KiB
Архитектура: Истински Clojure върху Nim
Философия
Това не е "Clojure-подобен" език. Това е Clojure диалект, който компилира към Nim. Макросите, структурите от данни и семантиката са съвместими с Clojure (JVM).
Защо истински Clojure?
| Аспект | Clojure-inspired Lisp | Истински Clojure диалект |
|---|---|---|
| Библиотеки | Пишеш всичко на ново | Можеш да пренесеш clojure.core, clojure.string, clojure.set |
| Разработчици | Учат нов език | Всеки Clojure програмист може да го ползва веднага |
| Инструменти | Собствени | Работи с clojure-mode, CIDER, Calva (с някои адаптации) |
| Финтех/Indустрия | Не е опция | Може да се ползва за реални проекти |
Който го е правил успешно
- ClojureScript — компилира Clojure до JS. Използва Google Closure. Пълна съвместимост.
- Babashka — GraalVM native image на Clojure. Бърз старт, скриптове, портативен.
- Ferret — Clojure → C++11 за embedded системи. Работи на 2KB RAM.
- ClojErl — Clojure върху BEAM. Жертва част от performance заради семантиката.
Архитектура на Компилатора
┌─────────────────┐
│ .clj Файл │ ← Стандартен Clojure код
└────────┬────────┘
│
┌────▼────┐
│ Reader │ ← EDN парсер, Clojure symbols, quoted forms
└────┬────┘
│
┌────▼────┐
│ Macro │ ← defmacro, syntax-quote, unquote, unquote-splicing
│ Expansion│ (работи върху Clojure структури)
└────┬────┘
│
┌────▼────┐
│ Analyzer│ ← Специални форми, locals, recur targets,
│ │ closures, type hints
└────┬────┘
│
┌────▼────┐
│ Emitter │ ← Генерира Nim AST (не Nim текст!)
│ (Nim) │ Използва Nim макроси за компилация
└────┬────┘
│
┌────▼────┐
│ Nim │ ← C код, после машинен код
│Compiler │ оптимизация, tree-shaking, LTO
└────┬────┘
│
┌────▼────┐
│ Бинарен │ ← Единичен файл, < 10MB, бърз старт
│ Файл │
└─────────┘
Ключови Решения
1. Компилатор, не Интерпретатор
Изборът е AOT компилатор, подобно на ClojureScript. Няма да пишем интерпретатор в Nim.
- Предимство: Бърз runtime, малък footprint, компилира до C.
- Недостатък: REPL е по-труден (но не невъзможен — виж ClojureScript self-hosted).
- Урок от calcit-runner: Те започнаха с интерпретатор в Nim и се провалиха. Ние директно компилираме.
2. Clojure Макроси на Clojure Структури
;; Това трябва да работи без промяна:
(defmacro unless [condition & body]
`(if (not ~condition)
(do ~@body)))
(unless false
(println "Работи!"))
Макросите се разширяват преди да стигнем до Nim. Nim никога не вижда Clojure макрос.
3. Persistent Структури от Данни в Nim
Clojure's Hash Array Mapped Trie (HAMT) трябва да се имплементира в Nim:
# Nim runtime за Clojure Vector
type
NodeKind = enum nkLeaf, nkInternal
Node[T] = ref object
case kind: NodeKind
of nkLeaf: values: seq[T]
of nkInternal: children: array[32, Node[T]]
PersistentVector[T] = object
root: Node[T]
tail: seq[T]
count: int
conj,assoc,dissoc— O(log₃₂ n)nth,get— O(log₃₂ n)count— O(1)
4. Nim Interop (вместо Java Interop)
Тъй като нямаме JVM, interop-ът е с Nim:
;; Извикване на Nim функция
(nim/strutils.join ", " ["a" "b" "c"])
;; Достъп до Nim тип
(nim/times.DateTime)
;; FFI към C библиотека
(nim/ffi.import "math.h" :functions [["sin" :double [:double]]])
5. Симулация на Clojure Runtime
| Clojure JVM | Нашата Имплементация |
|---|---|
Var |
Глобални Nim променливи с atomic CAS |
Namespace |
Nim модул + хеш таблица със символи |
Atom |
ref тип с compare-and-swap |
Agent |
Channel + Worker thread |
Ref/STM |
Software transactional memory в Nim |
Multimethod |
Диспач таблица + isa? проверка |
Protocol |
Nim concepts + dispatch таблица |
Фази с Индустриален Фокус
Phase 1: Компилатор (не REPL!)
Фокус: AOT компилация на прости Clojure файлове към Nim.
Phase 2: Core Библиотека
Фокус: Пренасяне на clojure.core функции. Тестове с реални Clojure тестове.
Phase 3: Nim Interop + FFI
Фокус: Извикване на Nim/C библиотеки. Това е нашето "убийствено" предимство.
Phase 4: REPL (Self-hosted)
Фокус: REPL като ClojureScript (компилира в паметта).
Phase 5: Инструменти
Фокус: nREPL съвместимост, LSP, пакетен мениджър (Nimble интеграция).
Изисквания за Съвместимост
clojure.corenamespace трябва да е наличенdefn,let,if,do,recur,loop— точна семантика->,->>,cond->— макроси- Persistent Vector, Map, Set — стандартни операции
- Keywords (
:name) и Symbols ('sym) — interned
Разлики от JVM Clojure (приемливи)
| JVM Clojure | Clojure/Nim |
|---|---|
| Java interop | Nim interop |
| JVM threads | Nim threads (no GIL!) |
| Dynamic classloading | AOT компилация |
gen-class |
gen-nim (генериране на Nim типове) |
| Reflection | Compile-time метаданни |
| Big ecosystem | Достъп до Nim + C ecosystem |