Roswell で環境構築して Common Lisp を書いてみる
なんか Lisp ってよく聞くよなーと思って、ちょっと触ってみる。
目次
- Common Lisp の概要をお勉強する
- Roswell をインストールして環境構築する
- Roswell で SBCL の REPL を試す
- Common Lisp プロジェクトの雛形を作ってみる
- エントリポイントとなる Roswell ファイルを作る
- エントリポイントから
src/main.lisp
を呼び出す - プロジェクトは Roswell のディレクトリ配下に置かないといけない (重要!)
- ビルドするには
- ついでに : シングルファイルでの実行
- ひとまず以上
Common Lisp の概要をお勉強する
Lisp は1958年に登場したプログラミング言語。マクロ機能により構文そのものを拡張できるなど柔軟性があり、様々な方言が存在する。
2022年現在で広く使われている方言は Common Lisp。その他に Clojure、Scheme などが有名なようだ。今回は Common Lisp を扱ってみる。
C 言語におけるコンパイラが gcc
だけではないのと同じように、Common Lisp にも処理系がいくつかある。中でも有名な実装は Steel Bank Common Lisp (SBCL) や CLISP など。
パッケージマネージャもあり、Quicklisp が有名。他には Another System Definition Facility (ASDF) というモノもあるみたい。
- Getting Started | Common Lisp …
lisp-lang.org
では SBCL と Quicklisp のセットアップ手順が解説されている
調べていくと、Roswell というツールが処理系のインストールからパッケージマネージャの役割までフルスタックで担ってくれて Windows 対応もしているそうなので、一番使いやすそうかと思い、今回環境構築に利用する。
- roswell/roswell: intended to be a launcher for a major lisp environment that just works.
- How is Roswell different · roswell/roswell Wiki
- RoswellでCommon Lisp環境をセットアップする - 星にゃーんのブログ
- Lispのパッケージ管理入門.Quicklisp,ASDF,Roswellの違いなど · wshito's diary
Roswell をインストールして環境構築する
Mac の場合、Homebrew からインストールできる。Linux の場合も同じ Linuxbrew でインストールできる。
$ brew install roswell
動作確認してみようと思い $ ros
コマンドを叩いてみたら、いきなりデフォルトの処理系などがインストールされ始めた。
$ ros
Installing sbcl-bin...
No SBCL version specified. Downloading sbcl-bin_uri.tsv to see the available versions...
[##########################################################################]100%
Installing sbcl-bin/2.2.8...
Downloading https://github.com/roswell/sbcl_bin/releases/download/2.2.8/sbcl-2.2.8-x86-64-darwin-binary.tar.bz2
[##########################################################################]100%
Extracting sbcl-bin-2.2.8-x86-64-darwin.tar.bz2 to /Users/Neo/.roswell/src/sbcl-2.2.8-x86-64-darwin/
Building sbcl-bin/2.2.8... Done.
Install Script for sbcl-bin...
Installing Quicklisp... Done 14223
Making core for Roswell...
Common Lisp environment setup Utility.
Usage:
ros [options] Command [arguments...]
or
ros [options] [[--] script-path arguments...]
commands:
run Run repl
install Install a given implementation or a system for roswell environment
update Update installed systems.
build Make executable from script.
use Change default implementation.
init Creates a new ros script, optionally based on a template.
fmt Indent lisp source.
list List Information
template Manage templates
delete Delete installed implementations
config Get and set options
version Show the roswell version information
Use "ros help [command]" for more information about a command.
Additional help topics:
options
Use "ros help [topic]" for more information about the topic.
Roswell を経由してインストールした処理系やライブラリなどは、全て ~/.roswell/
配下に配置されるようなので、アンインストールしたくなったら ~/.roswell/
をまるっと削除すれば良い。
- バージョン確認
$ ros version
roswell 21.10.14.111(NO-GIT-REVISION)
build with Apple clang version 13.0.0 (clang-1300.0.29.3)
libcurl=7.77.0
Quicklisp=2021-02-13
Dist=2022-07-08
lispdir='/usr/local/Cellar/roswell/21.10.14.111/etc/roswell/'
homedir='/Users/Neo/.roswell/'
sbcl-bin-variant=''
- 設定確認
$ ros config
setup.time=3872220259
sbcl-bin.version=2.2.8
default.lisp=sbcl-bin
Possible subcommands:
set
show
- インストールできる処理系の確認
$ ros install
Usage:
To install a new Lisp implementaion:
ros install impl [options]
or a system from the GitHub:
ros install fukamachi/prove/v2.0.0 [repository... ]
or an asdf system from quicklisp:
ros install quicklisp-system [system... ]
or a local script:
ros install ./some/path/to/script.ros [path... ]
or a local system:
ros install ./some/path/to/system.asd [path... ]
For more details on impl specific options, type:
ros help install impl
Candidates impls for installation are:
abcl-bin
allegro
ccl-bin
clasp-bin
clasp
clisp
cmu-bin
ecl
mkcl
sbcl-bin
sbcl-head
sbcl
sbcl-source
# 以下でも同様
$ ros list versions
デフォルトでインストールされた処理系は SBCL v2.2.8 だった。
$ ros run -- --version
SBCL 2.2.8
# 以下でリスト表示
$ ros list installed
Installed implementations:
Installed versions of sbcl-bin:
sbcl-bin/2.2.8
$ ros install
で処理系をインストールして、$ ros use
でグローバルに使用する処理系を変更する、という感じみたい。rbenv や nvm なんかのバージョン管理ツールを使ったことがあれば分かると思う。
ライブラリのインストールも $ ros install
で出来るようで、内部的には前述の Quicklisp が使われている模様。
Roswell で SBCL の REPL を試す
$ ros run
と打つと、デフォルトに指定した処理系の REPL が立ち上がる。ココで動作確認してみる。
$ ros run
# Hello World
* (print "Hello World")
"Hello World"
"Hello World"
# 算術
* (+ 1 2)
3
# 終了する場合 : Ctrl + D で強制終了も覚えておくと良いかも
* (exit)
Common Lisp プロジェクトの雛形を作ってみる
cl-project
というモノを使うと、プロジェクトの雛形 (スケルトン) が作れるらしい。
$ ros run
で REPL を起動し、その後の2行を打つ。
$ ros run
* (ql:quickload :cl-project)
* (cl-project:make-project #P"hello-project" :author "Neos21")
# 終わったら (exit) で REPL を抜ける
* (exit)
# こんな風にファイルが出力されている
$ tree -a ./hello-project
./hello-project
├── .gitignore
├── README.markdown
├── README.org
├── hello-project.asd
├── src
│ └── main.lisp
└── tests
└── main.lisp
2 directories, 6 files
src/main.lisp
というファイルを編集していけば良いようだ。
エントリポイントとなる Roswell ファイルを作る
$ ros init
コマンドを使うと、エントリポイントとなる実行可能ファイルを作れるらしい。
$ cd ./hello-project/
$ ros init hello-project
Successfully generated: hello-project.ros
中身は以下のようになっている。シェルスクリプトとして起動するが、自身を ros
コマンドに渡して Common Lisp のコードを実行させるようだ。
#!/bin/sh
#|-*- mode:lisp -*-|#
#|
exec ros -Q -- $0 "$@"
|#
(progn ;;init forms
(ros:ensure-asdf)
#+quicklisp(ql:quickload '() :silent t)
)
(defpackage :ros.script.hello-project.3872814791
(:use :cl))
(in-package :ros.script.hello-project.3872814791)
(defun main (&rest argv)
(declare (ignorable argv))
; ココにメイン処理を書く
)
;;; vim: set ft=lisp lisp:
; ココにメイン処理を書く
と書いた部分に、
(write-line "Hello World!")
(print "Hello World!")
こんな感じのエコー文を書いてみる。そして次のように実行する。
$ ros ./hello-project/hello-project.ros
# もしくは直接実行しても大丈夫
$ ./hello-project/hello-project.ros
とりあえずエコーはできたが、まだ src/main.lisp
を全く使えていない。
エントリポイントから src/main.lisp
を呼び出す
src/main.lisp
を書き換えていって、「パッケージ」を作る。ココでは hello-project
というプロジェクト名で、echo-hello-world()
という関数をエクスポートしている。
src/main.lisp
(defpackage hello-project
(:use :cl)
(:export :echo-hello-world) ; 関数をエクスポートする
)
(in-package :hello-project)
(defun echo-hello-world ()
(write-line "hello-project/src/main.lisp !")
)
続いてエントリポイントとなる hello-project.ros
ファイルを書き換えて、この hello-project
パッケージの echo-hello-world()
関数を呼び出すようにする。
hello-project.ros
#!/bin/sh
#|-*- mode:lisp -*-|#
#|
exec ros -Q -- $0 "$@"
|#
(progn ;;init forms
(ros:ensure-asdf)
#+quicklisp(ql:quickload '(:hello-project) :silent t)
; ↑元は `'()` だった部分でパッケージをインポートしている
)
(defpackage :ros.script.hello-project.3872814791
(:use :cl))
(in-package :ros.script.hello-project.3872814791)
(defun main (&rest argv)
(declare (ignorable argv))
; ココにメイン処理を書く
; ↓ ココで関数を呼び出している
(hello-project:echo-hello-world)
0 ; 終了ステータス
)
;;; vim: set ft=lisp lisp:
さて、コレで hello-project.ros
を実行すれば、src/main.lisp
が読み込めるはず…と思ったら、どうやってもパッケージが「Not Found」といったエラーが出てしまう。何だこれ…。
プロジェクトは Roswell のディレクトリ配下に置かないといけない (重要!)
色々調べてみると、自作パッケージを解決するには、そのプロジェクトが ~/.roswell/local-projects/
配下に配置されていないといけないようだ。
先程の .ros
ファイルを見てもらうと、quicklisp
の機能を使って自作の hello-project
パッケージをインポートしようとしている。この Quicklisp を Roswell が上手く隠蔽してくれている都合もあり、自作のプロジェクトを ~/.roswell/local-projects/
配下に置いておかないといけないようである。素の Quicklisp がよく分かっていないが、少なくとも Roswell による環境構築で一緒にインストールされる Quicklisp を利用する場合は、こういうことらしい。
Node.js 民が想像しやすい例えをするなら、Quicklisp は「npm のグローバルインストールしたディレクトリ」しか探索できない、という感じだろうか。この辺、モダンな言語だともう少し上手くやってくれるけど、言語やエコシステムの歴史が長い Common Lisp の都合があり、それを Roswell が頑張ってパッケージングしてくれているため、Roswell Way を理解して使わないといけない感じだ。
実際のプロジェクトディレクトリを ~/.roswell/local-projects/
配下に移動させて利用しても良いが、シンボリックリンクを張るという方法でも良い。
# Quicklisp でパッケージが認識できるようにシンボリックリンクを張る
$ ln -s "$(pwd)" "${HOME}/.roswell/local-projects/$(basename "$(pwd)")"
# コレで上手く動作するようになる
$ ros ./hello-project/hello-project.ros
コレを解決するのに時間がかかった…。
- 参考 : Common LispでCaveman2を起動する - kitemw’s diary
- 参考 : Roswell 環境下でのローカル・プロジェクト管理入門 · wshito's diary
- 参考 : Common Lisp Seminar 2019 ログ by fukamachi
ビルドするには
こうしてとりあえず作れた Common Lisp プロジェクトだが、最後にビルド方法を。
エントリポイントとなる .ros
ファイルを指定して $ ros build
を実行すれば、単一のバイナリファイルが出来上がる。このシングルバイナリを持ち運べば、Roswell や Common Lisp の処理系がなくとも動作する。
$ ros build ./hello-project/hello-project.ros
# ↓以下のようにバイナリファイルができる
$ ./hello-project/hello-project
ついでに : シングルファイルでの実行
ついでに発見したこと。.lisp
ファイルに main
関数さえ書いてあれば、$ ros ./single-file.lisp
のように直接実行できる。ただし、$ ros build
はできなかった。
(defun main ()
(print "single-file.lisp !")
)
ひとまず以上
Roswell によって、SBCL 処理系や Quicklisp・ASDF といったパッケージ管理ツール等をまるっとインストールしてもらい、Common Lisp プロジェクトの雛形作成・ビルドなどをしてもらえるようになった。
ココから先は Common Lisp の構文の勉強もそうだし、途中で ~/.roswell/local-projects/
配下にシンボリックリンクを張ったように、Roswell がラップしている Quicklisp や ASDF などの仕組みも理解しておかないといけないだろう。
とりあずドックフーディングの初歩ということで、今回はココまで。