170 lines
7.0 KiB
Markdown
170 lines
7.0 KiB
Markdown
# Архитектура: Истински 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 Структури
|
||
|
||
```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
|
||
# 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:
|
||
|
||
```clojure
|
||
;; Извикване на 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.core` namespace трябва да е наличен
|
||
- `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 |
|