rontolisp 入門

rontolisp

2026-10-24

話の流れ

  1. rontolisp とは
  2. インストールと最初の実行
  3. 言語
  4. バックエンド
  5. 連携とライブラリ
  6. 次の一歩
2

話の流れ

  1. rontolisp とは
  2. インストールと最初の実行
  3. 言語
  4. バックエンド
  5. 連携とライブラリ
  6. 次の一歩
3

rontolisp とは

Java で実装した Common Lisp 処理系

一つのソースを、三つの実行モードで動かす。

  • インタプリタ REPL とファイル実行。ツリーウォークで評価する
  • JVM コンパイラ .class や .jar を出力する
  • WASM コンパイラ wasm-GC を使った .wasm を出力する

コアライブラリは外部依存を持たない。JVM と WASM のバイトコードは、ASM などのライブラリを使わずに生成する。

4

rontolisp とは

まず動かしてみる

このデッキのコードは、ブラウザ上の rontolisp で実行できる。クリックして編集し、Run で実行する。

(defun fact (n)
  (if (= n 0) 1 (* n (fact (- n 1)))))
(print (fact 10))

Run で 3628800 が出る

5

rontolisp とは

Common Lisp、Scheme、Clojure を読む

Common Lisp

;; sq.lisp
(defun sq (x) (* x x))
(print (sq 12))

Scheme

;; sq.scm
(define (sq x) (* x x))
(display (sq 12))

Clojure

;; sq.clj
(defn sq [x] (* x x))
(println (sq 12))

どの言語のソースも同じコアフォームに変換するので、インタプリタ、JVM、WASM、ネイティブのどれでも動く。

6

話の流れ

  1. rontolisp とは
  2. インストールと最初の実行
  3. 言語
  4. バックエンド
  5. 連携とライブラリ
  6. 次の一歩
7

インストールと最初の実行

インストール

リリースページのビルド済みバイナリを PATH に置く。JVM は要らない。

$ URL=https://github.com/making/rontolisp/releases/download/0.1.0-SNAPSHOT
$ wget $URL/rontolisp-darwin-arm64
$ chmod +x rontolisp-darwin-arm64
$ sudo mv rontolisp-darwin-arm64 /usr/local/bin/rontolisp
$ rontolisp --version
  • Linux の x86-64 と ARM64 向けもある
  • Java 25 以降があれば、実行可能 JAR でも動く
8

インストールと最初の実行

REPL

引数なしで起動すると REPL になる。定義は入力をまたいで残る。

$ rontolisp
CL-USER> (+ 1 2)
3
CL-USER> (defun fact (n) (if (= n 0) 1 (* n (fact (- n 1)))))
FACT
CL-USER> (fact 10)
3628800
CL-USER> (quit)

プロンプトには現在のパッケージ名が出る。括弧が閉じるまで複数行を読む。

9

インストールと最初の実行

ファイルを実行する

;; program.lisp
(defun square (x) (* x x))
(print (square 5))
(print (square 12))
$ rontolisp program.lisp

25
144
$ rontolisp -e "(print (+ 1 2))"

3
  • REPL と違い、評価した値を表示しない
  • エラーで終わると、終了コードが 0 以外になる
10

インストールと最初の実行

コマンドの一覧

コマンド動作
rontolispREPL
rontolisp app.lispファイルをインタプリタで実行
rontolisp -e "(...)"引数のプログラムを実行
rontolisp app.lisp -o OUTコンパイル。OUT の拡張子で出力を選ぶ
rontolisp format src/ソースを正規のレイアウトにインデントし直す
rontolisp test tests/main.lisprove のテストを実行し、結果を終了コードで返す
11

話の流れ

  1. rontolisp とは
  2. インストールと最初の実行
  3. 言語
  4. バックエンド
  5. 連携とライブラリ
  6. 次の一歩
12

言語

Common Lisp の主な機能

機能主なフォーム
マクロdefmacro、バッククォート、macroexpand
CLOSdefclass、defgeneric、defmethod
コンディションとリスタートhandler-case、restart-case
多値values、multiple-value-bind
繰り返しloop、dolist、dotimes
数値多倍長整数、有理数、浮動小数点数
13

言語

ANSI Common Lisp テストスイート

19,769 件のテストのうち 16,529 件 が通過している (83.6%)。

  • 計測の対象はインタプリタ
  • CI が毎日スイート全体を実行し、通過数が変わるたびに結果を記録する
  • 対応する機能を増やし、通過数を引き続き上げていく

ansi-test/results/interpreter.md (2026-10-23 時点、スイートのリビジョン ca06bd9)

14

言語

プログラムもリスト

コードは S 式で書く。S 式はリストなので、マクロはリストを組み立ててコードを作る。

(defmacro twice (&body body) `(progn ,@body ,@body))
(print (macroexpand-1 '(twice (princ "hi "))))
(twice (princ "hi "))
15

言語

CLOS とコンディション

(defclass circle () ((r :initarg :r :reader r)))
(defclass square () ((side :initarg :side :reader side)))
(defgeneric area (shape))
(defmethod area ((c circle)) (* 3 (r c) (r c)))
(defmethod area ((s square)) (* (side s) (side s)))
(print (mapcar #'area (list (make-instance 'circle :r 2)
                            (make-instance 'square :side 3))))
(handler-case (/ 1 0)
  (division-by-zero () :infinity))
16

言語

Scheme のフロントエンド

Scheme のコードでは、(scheme base) などの R7RS-small のライブラリを import して使う。

(import (scheme base) (scheme write))
(define (count-up n)
  (let loop ((i 0) (acc '()))
    (if (= i n)
        (reverse acc)
        (loop (+ i 1) (cons (* i i) acc)))))
(display (count-up 5))
17

言語

Clojure のフロントエンド

Clojure のコードでは、->> などのスレッディングマクロや、シーケンスを扱うコア関数を使える。

(defn fact [n]
  (if (< n 2) 1 (* n (fact (- n 1)))))
(println (fact 20))
(println (->> (range 10) (filter odd?) (map #(* % %)) (reduce +)))
(println (frequencies "mississippi"))
18

言語

言語はファイルごとに決まる

拡張子言語
.lisp .asdCommon Lisp
.scmScheme
.clj .cljcClojure
それ以外--source-language で指定する

一つのプログラムに、複数の言語のファイルを混ぜられる。

19

話の流れ

  1. rontolisp とは
  2. インストールと最初の実行
  3. 言語
  4. バックエンド
  5. 連携とライブラリ
  6. 次の一歩
20

バックエンド

一つのパイプラインから三つの実行モードへ

  1. ソース (CL、Scheme、Clojure)
  2. コアフォームに変換
  3. マクロ展開
  4. インタプリタ、.class、.wasm
  • 言語の違いは最初の変換で吸収する
  • バックエンドが受け取るのは、どの言語でも同じコアフォーム
21

バックエンド

出力の拡張子でバックエンドを選ぶ

$ rontolisp hello.lisp                                # インタプリタ
$ rontolisp hello.lisp -o Hello.class && java Hello   # JVM
$ rontolisp hello.lisp -o hello.jar && java -jar hello.jar
$ rontolisp hello.lisp -o hello.wasm && wasmtime run hello.wasm
$ rontolisp hello.lisp -o hello-c.wasm --component && wasmtime run hello-c.wasm
$ rontolisp hello.lisp --native -o hello && ./hello   # ネイティブ

どの形でも (print (+ 1 2)) は 3 を出力する。

22

バックエンド

JVM と WASM

JVM

  • .jar は java -jar で動く
  • Java のライブラリにもできる

WASM

  • 既定は WASI Preview 1
  • --component で WASI 0.3
  • --no-wasi でインポートなし
  • --no-gc で wasm-GC のないエンジン向け
23

バックエンド

ネイティブ実行ファイル

--native は、WASM 出力を wasmtime でマシンコードにプリコンパイルし、小さなランナーの後ろに付ける。

  • 実行に wasmtime も JVM も要らない
  • 起動は約 10 ms
  • rontolisp:fetch、Clack や Ring の Web アプリ、TCP ソケットも動く。通信はランナーが行う
  • --native-target で Linux の x86_64 と aarch64、macOS の aarch64 向けを作れる

doc/ja/compiling/native.md

24

バックエンド

インタプリタとコンパイル結果の速さ

fib ベンチマークの実行時間 (短いほど速い)

インタプリタ

5,442 ms

JVM

60 ms

WASM

218 ms

ループ中心のコードは、コンパイルで 20〜200 倍速くなる

bench-report/results/benchmarks.md (2026-09-04, Linux x86_64, AMD EPYC 9V74)

25

バックエンド

インタプリタは試行錯誤に使う

  • すぐに起動し、コンパイル後と同じ意味で実行する
  • I/O が中心のスクリプトなら、速さの差は表に出にくい
  • CPU 負荷の高い処理は、コンパイルしてから動かす
$ rontolisp program.lisp -o program.jar
$ java -jar program.jar
26

バックエンド

ブラウザで動かす

  • プレイグラウンド rontolisp 自体を GraalVM の Web Image で WebAssembly にしたもの。REPL と、.class や .wasm へのコンパイルがブラウザの中で完結する
  • ドキュメント リファレンスの Lisp の例は Run で実行できる
  • このデッキ 同じブラウザ版がコードを実行している
  • 自分のプログラム WASM にコンパイルしてページから呼ぶ (examples/browser/ のマインスイーパーや WebGL)
27

話の流れ

  1. rontolisp とは
  2. インストールと最初の実行
  3. 言語
  4. バックエンド
  5. 連携とライブラリ
  6. 次の一歩
28

連携とライブラリ

Java 連携

rontolisp 独自の java パッケージで、任意の Java API をリフレクション経由で呼べる。

(java:call (java:new "java.lang.StringBuilder" "ab") "length")  ; => 2
(java:static "java.lang.Math" "max" 3 7)                        ; => 7
(java:field "java.lang.Integer" "MAX_VALUE")                    ; => 2147483647

JVM 上のインタプリタと、コンパイルした .class で使える。

29

連携とライブラリ

ASDF で複数ファイルのプロジェクトを読む

;; my-app.asd
(defsystem :my-app
  :depends-on (:base)
  :serial t
  :components ((:file "package")
               (:file "main")))
  • ASDF の defsystem に合わせた組み込み実装
  • .asd はプレーンなデータとして解析する
  • 依存するシステムから順に読み込む
  • --system-path で探す場所を足す
  • インタプリタ、JVM、WASM のどれでも同じように動く
30

連携とライブラリ

実在のライブラリをそのまま読む

$ rontolisp
CL-USER> (ql:quickload "split-sequence")
(split-sequence)
CL-USER> (split-sequence:split-sequence #\, "a,b,c")
("a" "b" "c")
31

連携とライブラリ

既存のライブラリと API

上流のままは、元のライブラリのソースをそのまま読む
パッケージ用途rontolisp での実装
uiop環境変数、ファイル、文字列UIOP の API を実装
roveテスト上流のまま
clackWeb アプリケーション上流のまま
cffiC ライブラリの呼び出し上流のまま
objcObjective-C の呼び出しLispWorks の API を実装
32

連携とライブラリ

rontolisp 独自のパッケージ

次のページから、二つの表のパッケージを一つずつ紹介する
名前用途
javaJava API の呼び出し
rontolisp:fetchHTTP リクエスト
rontolisp testrove のテストを実行するコマンド
linalg、vec、torch行列演算とニューラルネットワーク
appkit、metalmacOS の GUI と GPU 描画
33

連携とライブラリ

uiop で環境やファイルを扱う

uiop は、環境変数、パス名、ファイル、文字列の操作を、多くのライブラリが使う共通の名前で提供する。rontolisp はその API に合わせて組み込みで実装している。

(print (uiop:split-string "red,green,blue" :separator ","))
(print (uiop:strcat "ronto" "lisp"))
(print (uiop:subpathname "/usr/local/" "bin/rontolisp"))
(print (uiop:emptyp ""))
34

連携とライブラリ

rove でテストを書く

上流の rove で書いたテストを、rontolisp 独自の rontolisp test が実行し、結果を終了コードで返す。

;; tests/main.lisp
(defpackage #:my-app/tests
  (:use #:cl #:rove))
(in-package #:my-app/tests)

(defun add (a b) (+ a b))

(deftest add-test
  (ok (= (add 1 2) 3))
  (ng (= (add 1 2) 4)))
$ rontolisp test tests/main.lisp
add-test
  ✓ Expect (= (ADD 1 2) 3) to be true.
  ✓ Expect (= (ADD 1 2) 4) to be false.
Summary:
  All 1 test passed.
$ echo $?
0
35

連携とライブラリ

fetch で HTTP リクエストを送る

rontolisp 独自の rontolisp:fetch は JavaScript の fetch にならった API で、rontolisp:await で応答を待つ。

(let* ((res (rontolisp:await
             (rontolisp:fetch "https://httpbin.ik.am/get?lang=lisp")))
       (body (rontolisp:await (rontolisp:read-all (getf res :body))))
       (json (rontolisp:json-parse body)))
  (print (getf res :status))
  (print (gethash "lang" (gethash "args" json))))
36

連携とライブラリ

Clack で Web アプリを書く

上流の Clack をそのまま読み込む。アプリケーションは、リクエストの plist を受け取ってレスポンスのリストを返す関数。

(ql:quickload "clack")  ; app.lisp
(clack:clackup
 (lambda (env)
   (list 200 '(:content-type "text/plain")
         (list (format nil "Hello, ~A~%" (getf env :path-info)))))
 :server :rontolisp :port 5000 :use-thread nil)
$ rontolisp app.lisp &
$ curl http://127.0.0.1:5000/rontolisp
Hello, /rontolisp
37

連携とライブラリ

Web アプリを一つのソースで各環境へ

Clack のアプリケーションを、ソースを変えずにビルドだけで各環境に合わせる。

ビルド動かす場所
rontolisp app.lispインタプリタ
-o app.jarJVM、Google App Engine
-o app.warServlet コンテナ
--native -o appネイティブ実行ファイル
-o app.wasm --componentwasmtime serve など
-o worker.wasm --no-wasiCloudflare Workers

examples/net/httpbin-clack.lisp、examples/cloudflare-workers/、examples/gae/

38

連携とライブラリ

linalg と vec で行列を計算する

どちらも rontolisp 独自のパッケージで、linalg は numpy 風の行列演算を、vec は浮動小数点ベクトルの演算カーネルを提供する。

(print (linalg:matmul #2A((1 2) (3 4)) #2A((5 6) (7 8))))
(print (linalg:solve #2A((4 0) (2 4)) #(8 8)))
(print (linalg:sum #2A((1 2 3) (4 5 6)) :axis 0))
(print (vec:matvec #d((1.0 2.0) (3.0 4.0)) #d(5.0 6.0)))
(print (vec:dot #d(1.0 2.0 3.0) #d(4.0 5.0 6.0)))
39

連携とライブラリ

行列演算を SIMD、BLAS、GPU で速くする

linalg と vec の行列演算を、フラグでハードウェアに振り向ける。

フラグ計算する場所
--simdCPU のベクトル命令
--blasOS の BLAS (Accelerate、OpenBLAS など)
--gpuNVIDIA の GPU (CUDA)、Mac の GPU (Metal)
  • BLAS や GPU のないマシンでも、同じプログラムが同じ結果を出す
  • 128x128 の行列積 1 回が、インタプリタの 1,848 ms から --simd で 0.62 ms、--blas で 0.034 ms になる

doc/ja/guides/blas-acceleration.md、gpu-acceleration.md (計測は Apple M4 Max)

40

連携とライブラリ

torch でニューラルネットワークを学習する

rontolisp 独自の torch は、linalg の上に載る PyTorch 風のパッケージ。2 層のネットワークに XOR を学習させる。

(linalg:seed 3)
(defparameter *mlp*
  (torch:sequential (torch:linear 2 8) #'torch:relu (torch:linear 8 1)))
(defparameter *xs* (torch:tensor '((0.0 0.0) (0.0 1.0) (1.0 0.0) (1.0 1.0))))
(defparameter *ys* (torch:tensor '((0.0) (1.0) (1.0) (0.0))))
(defparameter *sgd* (torch:sgd *mlp* :lr 0.2))
(dotimes (i 200)
  (let ((loss (torch:mse-loss (torch:forward *mlp* *xs*) *ys*)))
    (torch:zero-grad *sgd*)
    (torch:backward loss)
    (torch:step *sgd*)))
(print (torch:data (torch:forward *mlp* *xs*)))
41

連携とライブラリ

cffi で C ライブラリを呼ぶ

上流の CFFI をそのまま読み込み、defcfun で C の関数を Lisp の関数として定義する。

CL-USER> (ql:quickload :cffi)
CL-USER> (cffi:defcfun "strlen" :long (s :string))
CL-USER> (strlen "hello, world")
12

rontolisp コマンドと、コンパイルした .class や .jar で使える。

42

連携とライブラリ

Mac アプリを作る

  • objc で AppKit を呼ぶ。LispWorks の API を実装している
  • rontolisp 独自の appkit でウィンドウやボタンを組み立てる
  • インタプリタ、JVM、ネイティブ実行ファイルで動く
  • 追加のインストールは要らない
rontolisp で書いた macOS のマインスイーパー

examples/browser/minesweeper/minesweeper-macos.lisp

43

連携とライブラリ

appkit のコード

ウィンドウにラベルを置き、クリックで表示を変える。マインスイーパーも、同じ関数で画面を組み立てている。

(defvar *win*
  (appkit:window "rontolisp" :width 240 :height 160 :dark t))
(defvar *face*
  (appkit:label *win* "🙂" :x 94 :y 54 :width 52 :height 52
                :size 26 :align :center))
(appkit:on-click *face*
  (lambda (button) (appkit:set-text *face* "😮")))
(appkit:wait *win*)
44

連携とライブラリ

Metal で GPU に描く

rontolisp 独自の metal パッケージで Metal を呼び、約 13,000 個のボクセルからなる庭園をアニメーションで描く。

Metal で描いたボクセルの庭園と五重塔

examples/macos/metal-pagoda-garden.lisp

45

連携とライブラリ

Metal の描画コード

ウィンドウに Metal を取り付け、シェーダからパイプラインを作り、フレームごとに描画コマンドを送る。

(defvar *metal*
  (metal:attach *window* :clear '(0.02 0.03 0.07 1.0) :depth t))
(defvar *library* (metal:library *metal* *shaders*))
(defvar *solid* (metal:pipeline *metal* *library* "vox_vertex" "vox_fragment"))
(defvar *static-buffer* (metal:buffer *metal* *static*))
;; every frame
(objc:invoke encoder "setRenderPipelineState:" *solid*)
(objc:invoke encoder "setVertexBuffer:offset:atIndex:" *static-buffer* 0 0)
(objc:invoke encoder "drawPrimitives:vertexStart:vertexCount:"
             metal:+triangle+ 0 (* 36 *static-count*))

examples/macos/metal-pagoda-garden.lisp (抜粋、一部省略)

46

連携とライブラリ

Clojure から使う

Clojure のプログラムでは右の列を使う。テストはどちらも rontolisp test で実行する
用途Common LispClojure
Java の呼び出しjava パッケージJava interop
依存ライブラリASDF、Quicklispdeps.edn
テストroveclojure.test
HTTP リクエストrontolisp:fetchrontolisp.http-client
Web アプリClackRing
47

連携とライブラリ

Clojure の Java interop

Clojure の書き方で Java を呼ぶ。fn は、そのまま Comparator として渡せる。

(ns demo
  (:import (java.util ArrayList Collections)))

(let [words (ArrayList. ["kiwi" "fig" "banana"])]
  (Collections/sort words (fn [a b] (compare (count a) (count b))))
  (println (vec words)))                ; [fig kiwi banana]
(println (Integer/parseInt "42") (.toUpperCase "hi") (Math/max 3 7))
;; 42 HI 7

JVM 上のインタプリタと、コンパイルした .class で使える。

48

連携とライブラリ

deps.edn で Clojure のライブラリを使う

deps.edn に書いた data.json などを、Maven から取得して読む。

;; deps.edn
{:paths ["src"]
 :deps {org.clojure/data.json {:mvn/version "2.5.1"}}}
;; src/app/main.clj
(ns app.main
  (:require [clojure.data.json :as json]))
(defn -main [& args]
  (println (json/write-str {:lang "clojure" :args args})))
$ rontolisp -M -m app.main x y
{"lang":"clojure","args":["x","y"]}
49

連携とライブラリ

clojure.test でテストを書く

clojure.test で書いたテストは、deps.edn のあるディレクトリで rontolisp test を実行すると、結果を終了コードで返す。

(ns demo-test
  (:require [clojure.test :refer [deftest is testing run-tests]]))

(defn add [a b] (+ a b))

(deftest add-test
  (testing "adding two integers"
    (is (= 3 (add 1 2)))
    (is (not= 4 (add 1 2)))))

(run-tests)
50

連携とライブラリ

rontolisp.http-client で HTTP リクエストを送る

rontolisp 独自の rontolisp.http-client は、babashka.http-client と同じ API で、内部では rontolisp:fetch を使う。

(ns example
  (:require [rontolisp.http-client :as http]))

(let [r (http/get "https://httpbin.ik.am/get"
                  {:query-params {"lang" "clojure"}})]
  (println (:status r) (get-in r [:headers "content-type"]))
  (println (re-find #"\"lang\": ?\"clojure\"" (:body r))))
51

連携とライブラリ

Ring で Web アプリを書く

rontolisp 独自の ring.adapter.rontolisp が Ring のハンドラを動かす。デプロイ先は Clack と同じ。

(ns app
  (:require [ring.adapter.rontolisp :refer [run-server]]
            [ring.util.response :as response]))
(defn handler [{:keys [uri]}]
  (-> (response/response (str "Hello, " uri "\n"))
      (response/content-type "text/plain")))
(run-server handler {:port 3000})
$ curl http://127.0.0.1:3000/clojure
Hello, /clojure
52

話の流れ

  1. rontolisp とは
  2. インストールと最初の実行
  3. 言語
  4. バックエンド
  5. 連携とライブラリ
  6. 次の一歩
53

次の一歩

examples ディレクトリ

ディレクトリ内容
console/アルゴリズムとコンソール入出力
scheme/ clojure/Scheme と Clojure のプログラム
ml/ llm/数値計算、機械学習、Llama 2 の推論
net/ db/ソケット、HTTP サーバ、PostgreSQL
jvm/ macos/Java 連携、Swing、Cocoa、Metal
browser/ページで動かす WASM
asdf/実在のライブラリの読み込み
54

次の一歩

次に読むもの

55

インタプリタで試し、同じソースを JVM、WASM、ネイティブで動かす。

56