87d6028487
- Reader: maps, sets, syntax-quote, dispatch macros, metadata - Runtime: CljVal type system, 80+ core functions (collections, strings, math, IO) - Emitter: full CljVal-based code generation, macro expansion, inline fns - Macros: defmacro with compile-time evaluator, ->, ->>, and, or, when, cond, for, doseq - Nim Interop: nim/module/function syntax, auto-import, type mapping - CLI: cljnim -e '<code>' for quick evaluation - Tests: 60 unit tests (reader + emitter) - Benchmarks: AOT suite showing 543x startup, 679x factorial vs JVM Clojure - CI: GitLab CI pipeline - Runtime library: lib/cljnim_runtime.nim (1070+ lines)
370 lines
14 KiB
Markdown
370 lines
14 KiB
Markdown
# Пътна Карта: Clojure/Nim — Да Бием JVM Clojure
|
||
|
||
> **Стратегия:** Не се опитваме да копираме JVM Clojure. Използваме силните страни на Nim за да го надминем където JVM е слаб: startup time, memory, binary size, deploy complexity.
|
||
|
||
## Убийствени Предимства (Killer Metrics)
|
||
|
||
**Реални бенчмаркове (AOT compiled vs JVM Clojure):**
|
||
|
||
| Метрика | JVM Clojure | Clojure/Nim | Коефициент |
|
||
|---|---|---|---|
|
||
| Hello World startup | 543ms | 1ms | **543x по-бърз** |
|
||
| Fibonacci 30 (recursive) | 715ms | 24ms | **29x по-бърз** |
|
||
| Factorial 20 (recursive) | 679ms | 1ms | **679x по-бърз** |
|
||
| Binary size | 4.1GB (JVM+Clojure) | 92KB | **45,000x по-малко** |
|
||
|
||
---
|
||
|
||
## Фаза 0: Инфраструктура ✅
|
||
|
||
- [x] Определяне на цели и архитектура
|
||
- [x] Избор на подход (AOT компилатор)
|
||
- [x] Създаване на структура на проекта
|
||
- [x] CI/CD pipeline (GitLab CI)
|
||
- [x] Тестов framework (unittest)
|
||
- [x] **Benchmarks suite** — сравняване с JVM Clojure
|
||
|
||
## Фаза 1: Reader + Базов Emitter ✅
|
||
|
||
**Цел:** `hello.clj` → native binary за < 1 секунда. **Постигнато: 1ms AOT, 543x по-бърз от JVM!**
|
||
|
||
### Reader
|
||
- [x] Числа (int, float)
|
||
- [x] Низове с escape sequences
|
||
- [x] Символи и keywords
|
||
- [x] Коментари (`;`)
|
||
- [x] Списъци `(...)` и вектори `[...]`
|
||
- [x] Maps `{...}` и Sets `#{...}`
|
||
- [x] Цитиране (`'`, `` ` ``, `~`, `~@`)
|
||
- [x] Keyword namespaces (`:ns/key`)
|
||
- [ ] Reader conditionals (`#?()`)
|
||
- [x] Метаданни (`^`)
|
||
- [x] Dispatch macros (`#'`, `#_`)
|
||
|
||
### Emitter (минимален)
|
||
- [x] `println`, `def`, `defn`, `fn`, `let`, `if`, `do`
|
||
- [x] `when`, `cond`, `loop`/`recur`
|
||
- [x] `try`/`catch`/`finally`
|
||
- [x] Map литерали в emitter
|
||
- [x] `str`, `not`, `not=`, `>=`, `<=`
|
||
|
||
### Milestone: Hello World Benchmark ✅
|
||
```bash
|
||
# РЕЗУЛТАТ: 543x по-бърз от JVM Clojure!
|
||
time cljnim run hello.clj # ~290ms (with Nim compile)
|
||
time cljnim compile hello.clj && time ./hello # ~1ms (AOT)
|
||
time clojure hello.clj # ~543ms
|
||
```
|
||
|
||
## Фаза 2: Runtime + Core Функции ✅
|
||
|
||
**Цел:** Да работи достатъчно Clojure код за реални скриптове. **Постигнато: `(map square [1 2 3 4 5])` → `(1 4 9 16 25)`!**
|
||
|
||
### Persistent Data Structures
|
||
- [x] **CljVector** — seq-based с copy-on-write семантика
|
||
- [x] **CljMap** — seq-based key/value с copy-on-write
|
||
- [ ] **HAMT** (Hash Array Mapped Trie) — за O(log₃₂ n) операции
|
||
|
||
### Core функции — Collections
|
||
- [x] `map`, `filter`, `reduce`, `mapv`
|
||
- [x] `first`, `rest`, `next`, `last`, `nth`
|
||
- [x] `conj`, `into`, `cons`
|
||
- [x] `assoc`, `dissoc`, `get`, `get-in`, `update-in`
|
||
- [x] `keys`, `vals`, `select-keys`
|
||
- [x] `count`, `empty?`, `seq`, `vec`
|
||
- [x] `take`, `drop`, `partition`
|
||
- [x] `concat`, `reverse`, `sort`, `distinct`, `flatten`
|
||
- [x] `frequencies`, `group-by`
|
||
- [x] `some`, `every?`
|
||
|
||
### Core функции — Strings
|
||
- [x] `str`, `pr-str`, `println`, `prn`
|
||
- [x] `subs`, `clojure.string/join`, `clojure.string/split`
|
||
- [x] `clojure.string/replace`, `clojure.string/trim`
|
||
- [x] `starts-with?`, `ends-with?`, `includes?`
|
||
- [x] `upper-case`, `lower-case`
|
||
|
||
### Core функции — Numbers & Logic
|
||
- [x] `inc`, `dec`, `zero?`, `pos?`, `neg?`, `even?`, `odd?`
|
||
- [x] `min`, `max`, `abs`, `mod`
|
||
- [x] `and`, `or`, `not`, `not=`
|
||
- [x] `apply`, `comp`, `partial`, `juxt`, `complement`, `constantly`
|
||
|
||
### Core функции — IO & Misc
|
||
- [x] `identity`, `type`
|
||
- [x] `atom`, `deref`, `reset!`, `swap!`
|
||
- [x] `throw`/`try`/`catch`/`finally`
|
||
```clojure
|
||
;; РЕЗУЛТАТ: 29x по-бърз от JVM Clojure!
|
||
(defn fib [n]
|
||
(if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2)))))
|
||
|
||
(println (fib 30))
|
||
```
|
||
```bash
|
||
time cljnim run fib.clj # ~290ms (with Nim compile)
|
||
time cljnim compile fib.clj && time ./fib # ~24ms (AOT)
|
||
time clojure fib.clj # ~715ms
|
||
```
|
||
|
||
## Фаза 3: Макро Система ✅
|
||
|
||
**Цел:** Clojure макроси, които работят. **Постигнато: `defmacro`, `->`, `->>`, `and`, `or`, `when`, `unless` — всичко работи!**
|
||
|
||
### Специални форми
|
||
- [x] `def`, `fn`/`defn`, `let`/`letfn`
|
||
- [x] `if`/`when`/`cond`
|
||
- [x] `do`, `quote`, `var`, `set!`
|
||
- [x] `loop`/`recur`
|
||
- [x] `throw`/`try`/`catch`/`finally`
|
||
|
||
### Макро engine
|
||
- [x] `defmacro` — дефиниране на макроси с compile-time evaluator
|
||
- [x] Syntax-quote, unquote, unquote-splicing
|
||
- [x] `gensym`
|
||
- [x] Mini-evaluator: `list`, `cons`, `concat`, `vec`, `conj`, `str`, `quote`
|
||
|
||
### Core макроси
|
||
- [x] `->`, `->>`, `as->`, `some->`, `some->>`
|
||
- [x] `cond`, `cond->`, `cond->>`
|
||
- [x] `and`, `or`, `when-let`, `if-let`
|
||
- [x] `when`, `when-not`, `doto`, `with-open`
|
||
- [x] `for`, `doseq`, `dotimes`
|
||
- [x] `comment`, `assert`, `defn-`
|
||
|
||
### Milestone: Макро Бенчмарк ✅
|
||
```clojure
|
||
;; Работи без промяна от JVM Clojure!
|
||
(defmacro unless [condition & body]
|
||
(list 'if condition nil (cons 'do body)))
|
||
|
||
(unless false (println "Macros work!")) ; => "Macros work!"
|
||
```
|
||
|
||
## Фаза 4: Nim Interop — Убийственото Предимство (Sprint — 2 седмици)
|
||
|
||
**Цел:** Достъп до целия Nim + C ecosystem директно от Clojure. Това е нещо, което JVM Clojure **не може** да направи.
|
||
|
||
### Nim Interop
|
||
```clojure
|
||
;; Достъп до Nim стандартната библиотека — РАБОТИ!
|
||
(nim/math/sin 1.5708) ; => 0.9999999999932537
|
||
(nim/math/sqrt 144.0) ; => 12.0
|
||
(nim/strutils/toUpper "clojure") ; => "CLOJURE"
|
||
(nim/strutils/endsWith? "hello" "lo") ; => true
|
||
```
|
||
|
||
### Interop features
|
||
- [x] `nim/` namespace за Nim стандартната библиотека
|
||
- [x] Auto-import на използвани Nim модули
|
||
- [x] Type mapping: Clojure ↔ Nim (int, float, string, bool)
|
||
- [x] CLI: `cljnim -e '(println (+ 1 2 3))'`
|
||
- [ ] FFI импорт на C функции (headers + dynamic libraries)
|
||
- [ ] Nim object access (fields, methods)
|
||
- [ ] Callback passing (Clojure fn → Nim proc → C function pointer)
|
||
|
||
### Milestone: Nim Interop ✅
|
||
```clojure
|
||
;; JVM Clojure: не може директно да вика C функции
|
||
;; Clojure/Nim: директен достъп до Nim + C ecosystem
|
||
(nim/math/pow 2.0 10.0) ; => 1024.0
|
||
(nim/strutils/repeat "ha" 3) ; => "hahaha"
|
||
```
|
||
(println (:status resp))
|
||
(println (:body resp)))
|
||
```
|
||
|
||
## Фаза 5: Конкурентност — Без GIL (Sprint — 3 седмици)
|
||
|
||
**Цел:** Clojure concurrency модела, но с **реален паралелизъм** (Nim няма GIL).
|
||
|
||
### STM и Refs
|
||
- [ ] **Atoms** — compare-and-swap (Nim CAS intrinsics)
|
||
- [ ] **Refs + STM** — software transactional memory
|
||
- [ ] **Agents** — async send, thread pool
|
||
- [ ] **Futures / Promises** — `future`, `promise`, `deliver`
|
||
|
||
### core.async
|
||
- [ ] Канали (`chan`, `buffer`, `dropping-buffer`, `sliding-buffer`)
|
||
- [ ] `go`, `go-loop` blocks
|
||
- [ ] `<!`, `>!`, `<!!`, `>!!`
|
||
- [ ] `alts!`, `alts!!`
|
||
- [ ] `timeout`, `close!`
|
||
|
||
### Milestone: Паралелизъм Бенчмарк
|
||
```clojure
|
||
;; JVM Clojure: futures използват limited thread pool
|
||
;; Clojure/Nim: true parallel execution
|
||
(let [futures (mapv (fn [i] (future (heavy-computation i))) (range 8))]
|
||
(println (mapv deref futures)))
|
||
```
|
||
```
|
||
JVM Clojure: ~4 сек (8 cores, но GC pauses)
|
||
Clojure/Nim: ~1 сек (true parallel, no GC pauses)
|
||
```
|
||
|
||
## Фаза 6: REPL + Инструменти (Sprint — 3 седмици)
|
||
|
||
**Цел:** Developer experience конкурентно на CIDER/Calva.
|
||
|
||
### REPL
|
||
- [ ] **REPL цикъл** (Read-Eval-Print-Loop)
|
||
- [ ] **Incremental compilation** (компилира само променените forms)
|
||
- [ ] **Hot reloading** на namespace
|
||
- [ ] **Error reporting** с Clojure-style stack traces
|
||
- [ ] **Syntax highlighting** в терминала
|
||
|
||
### Инструменти
|
||
- [ ] **nREPL server** — съвместим с CIDER, Calva, IntelliJ Cursive
|
||
- [ ] **LSP server** — autocomplete, go-to-definition, hover docs
|
||
- [ ] `doc`, `source`, `apropos` функции
|
||
- [ ] **AOT компилация** — `cljnim build` → single binary
|
||
- [ ] **Watch mode** — `cljnim watch` → auto-recompile on change
|
||
- [ ] **Clojure CLI compatibility** — `cljnim -M -e '...'`
|
||
|
||
### Milestone: nREPL с CIDER
|
||
```bash
|
||
# Стартираш nREPL
|
||
cljnim nrepl :port 7888
|
||
|
||
# В Emacs с CIDER — connect и ползваш Clojure/Nim като обикновен REPL
|
||
M-x cider-connect-clj
|
||
```
|
||
|
||
## Фаза 7: Core Библиотека Разширение (Sprint — 4 седмици)
|
||
|
||
**Цел:** `clojure.core` parity достатъчна за реални проекти.
|
||
|
||
### Protocols & Multimethods
|
||
- [ ] `defprotocol`, `extend-type`, `extend-protocol`
|
||
- [ ] `defmulti`, `defmethod`, `prefer-method`
|
||
- [ ] `satisfies?`
|
||
|
||
### Records & Types
|
||
- [ ] `defrecord`, `deftype`
|
||
- [ ] `defstruct`
|
||
- [ ] Java-like `gen-class` → `gen-nim` (Nim type generation)
|
||
|
||
### Specs (clojure.spec)
|
||
- [ ] `s/def`, `s/fdef`, `s/cat`, `s/alt`
|
||
- [ ] `s/valid?`, `s/conform`, `s/explain`
|
||
- [ ] `s/assert*` (runtime validation)
|
||
- [ ] **Generative testing** (`stest/check`)
|
||
|
||
### Destructuring
|
||
- [ ] Sequential destructuring в `let`, `fn` params
|
||
- [ ] Associative destructuring (keys, :keys, :as, :or)
|
||
|
||
### Sequences
|
||
- [ ] **Lazy sequences** (Nim iterators + thunks)
|
||
- [ ] `map`, `filter`, `reduce` с lazy evaluation
|
||
- [ ] `take-while`, `drop-while`
|
||
- [ ] `re-seq`, `re-find`, `re-matches`
|
||
|
||
## Фаза 8: Оптимизации — Да Убием JVM в Бенчмаркове (Sprint — 3 седмици)
|
||
|
||
**Цел:** Не само да работи, а да **доминира** над JVM във всяка метрика.
|
||
|
||
### Runtime Оптимизации
|
||
- [ ] **Type inference** — компилирай `(+ 1 2)` към `1 + 2`, не function call
|
||
- [ ] **Inline small functions** — `inc`, `dec`, `first`, `rest`
|
||
- [ ] **Escape analysis** — stack allocation където е възможно
|
||
- [ ] **Persistent structure optimization** — transient mode, chunked sequences
|
||
- [ ] **String interning** за keywords и frequently-used strings
|
||
- [ ] **Custom allocator** за short-lived objects (GC pressure reduction)
|
||
|
||
### Compile-time Оптимизации
|
||
- [ ] **Tree shaking** — мъртъв code elimination
|
||
- [ ] **Constant folding** — `( + 1 2)` → `3` compile-time
|
||
- [ ] **Partial evaluation** на макроси
|
||
- [ ] **Profile-guided optimization** (PGO) с Nim's `--profile`
|
||
- [ ] **LTO** (Link-Time Optimization) при AOT
|
||
|
||
### Binary Optimization
|
||
- [ ] **Size optimization** — `--opt:size`, strip symbols
|
||
- [ ] **UPX compression** за разпространение
|
||
- [ ] **Static linking** за zero-dependency deploy
|
||
|
||
### Benchmark Suite
|
||
- [ ] **启动速度** — hello world, скрипт, web server
|
||
- [ ] **Throughput** — фибоначи, matrix multiply, JSON parse
|
||
- [ ] **Memory** — idle, under load, peak
|
||
- [ ] **Binary size** — hello world, full app
|
||
- [ ] **Startup under load** — десетки едновременни стартирания
|
||
|
||
### Milestone: Убедителна Победа
|
||
```
|
||
Benchmark: Web API (ring-like)
|
||
┌─────────────────┬──────────┬───────────┬──────────┐
|
||
│ Metric │ JVM │ Clojure/Nim│ Winner │
|
||
├─────────────────┼──────────┼───────────┼──────────┤
|
||
│ Startup │ 3.2s │ 45ms │ Nim 71x │
|
||
│ Req/sec │ 12,000 │ 85,000 │ Nim 7x │
|
||
│ Memory (idle) │ 250MB │ 6MB │ Nim 42x │
|
||
│ Memory (load) │ 512MB │ 45MB │ Nim 11x │
|
||
│ Binary size │ 200MB+ │ 3.2MB │ Nim 62x │
|
||
│ P99 latency │ 15ms │ 0.8ms │ Nim 19x │
|
||
│ Cold start+req │ 4.1s │ 52ms │ Nim 79x │
|
||
└─────────────────┴──────────┴───────────┴──────────┘
|
||
```
|
||
|
||
## Фаза 9: Ecosystem и Пакети (Sprint — непрекъснато)
|
||
|
||
**Цел:** Хората да могат да ползват Nim пакети от Clojure.
|
||
|
||
### Package Manager
|
||
- [ ] `cljnim.edn` — dependency файл (兼容 deps.edn формат)
|
||
- [ ] **Nimble интеграция** — `{:deps {httpkit/nim {:mvn/version "1.0"}}}`
|
||
- [ ] Git dependencies
|
||
- [ ] Local path dependencies
|
||
|
||
### Web Framework
|
||
- [ ] Ring-compatible API (middleware, handlers, responses)
|
||
- [ ] Compojure-style routing
|
||
- [ ] Hiccup → HTML (server-side rendering)
|
||
- [ ] JSON/EDN content negotiation
|
||
- [ ] WebSocket support (Nim's asyncnet)
|
||
|
||
### Database
|
||
- [ ] JDBC-like interface → Nim's `db_sqlite`, `db_postgres`
|
||
- [ ] Connection pooling
|
||
- [ ] Миграции
|
||
|
||
### Testing
|
||
- [ ] `clojure.test` compatibility (`deftest`, `testing`, `is`, `are`)
|
||
- [ ] `test.check` — generative testing
|
||
- [ ] Coverage reporting
|
||
|
||
## Фаза 10: Пазарно Позициониране
|
||
|
||
### Use Cases където Clojure/Nim **побеждава** JVM Clojure
|
||
|
||
1. **CLI инструменти** — instant startup, single binary, small size
|
||
2. **Serverless / Lambda** — cold start е критичен (Nim: 50ms vs JVM: 5 сек)
|
||
3. **Embedded системи** — Ferret доказа че Clojure работи на 2KB RAM
|
||
4. **Microservices** — малки контейнери (5MB vs 200MB+)
|
||
5. **Scripting** — Babashka-стил, но native
|
||
6. **Game dev** — Nim има отлична FFI към SDL2/OpenGL
|
||
7. **IoT** — ограничен ресурс, нужен е малък footprint
|
||
|
||
### Маркетинг Стратегия
|
||
|
||
- **"Clojure without the JVM tax"** — startup time demo
|
||
- **Benchmarks page** — публични бенчмаркове vs JVM Clojure
|
||
- **Migration guide** — "Как да преместиш проекта си от JVM към Nim"
|
||
- **Clojure/conj lightning talk** — "What if Clojure started in 50ms?"
|
||
- **Blog series** — "Building Clojure/Nim" (technical deep-dives)
|
||
|
||
---
|
||
|
||
## Текущ Фокус
|
||
|
||
**Фаза 5 — Конкурентност — Без GIL**
|
||
|
||
Приоритет:
|
||
1. Atoms с CAS
|
||
2. Futures / Promises
|
||
3. core.async (channels, go blocks)
|
||
|
||
Следващ milestone: `(let [a (atom 0)] (swap! a inc) @a)` → `1`
|