Clozure CL Part1

Clozure CLのセットアップとEmacsとSLIMEの設定、REPLの使い方について。

Clozure CLのセットアップ

Common Lisp処理系のひとつであるClozure CLをセットアップする。まずはClozure CL本体をダウンロードする。

$ cd ~/
$ curl -L -O https://github.com/Clozure/ccl/releases/download/v1.11.5/ccl-1.11.5-linuxx86.tar.gz
$ tar xzvf ccl-1.11.5-linuxx86.tar.gz

解凍してできたcclディレクトリ以下に、lx86cl、lx86cl64という2つの実行可能なファイルがある。32bit環境の場合はlx86cl、64bit環境の場合はlx86cl64を使う。lx86cl64をそのまま実行しても良いが、パスを通したディレクトリに配置し、cclコマンドで実行させるほうが使い勝手が良いので、ホームディレクトリ直下にbinディレクトリを作成し、その中にcclという名前のシンボリックリンクを作成する。

$ mkdir ~/bin
$ ln -s ~/ccl/lx86cl64 ~/bin/ccl

次に、コマンドを実行できるよう~/binディレクトリにパスを通す。~/.bashrcを編集し、以下の一行を追加する。

export PATH=~/bin:$PATH

最後にsourceコマンドで編集した~/.bashrcの内容を取り込み、cclコマンドが実行可能になったことを確認する。

$ source ~/.bashrc
$ ccl --version
Version 1.11.5/v1.11.5  (LinuxX8664)

Emacsの設定

EmacsからClozureCLを利用するための設定と、EmacsにSLIMEという拡張機能を追加する。SLIMEは、Debianであればaptを使用してインストールできる。

$ sudo apt install slime

次に、Emacsの設定ファイルを開く。

$ vi ~/.emacs.d/init.el

以下の内容を設定ファイルに書き込む。Lispの場所と、slimeを使うためにrequireを記述しておく。

(setq inferior-lisp-program "/home/username/ccl/lx86cl64")
(require 'slime)
(slime-setup)

EmacsとSLIMEを使用した開発

Emacs上で、”M-x slime”でSLIMEを起動すると、バッファに以下のテキストが出力される。

Clozure Common Lisp Port: 45433 Pid 8656
; SLIME 2.23
CL-USER>

このCL-USER>の右にLispの式を記述しENTERキーを押すことで、式を評価できる。以下は、(+ 1 2)を評価した際の結果になる。CL-USER>が再び表示されるので、続けて別の式を入力し評価することもできる。

CL-USER> (+ 1 2)
3
CL-USER>

このように、式を入力後、評価し結果が得られる対話的な環境をREPL(Read Eval Print Loop)と呼ぶ。

今のまま長いプログラムを記述するのは難しいため、ファイルを作成する。C-x 3でEmacsの画面を分割し、片方のバッファで新規のファイルを作成する。拡張子はlispにする。片方のバッファはファイル、もう片方はLispのREPLが表示されている状態にしておく。

ファイル側のバッファに早速コードを書く。内容は先程と同様に、1と2を足す式を書く。

(+ 1 2)

この式の末尾にカーソルを移動させ、C-x C-eと入力すると、REPL側のバッファの下端に、=> 3 (2bits, #x3, #o3, #b11)という内容が表示される。最初の3という値は、(+ 1 2)を評価した結果となる。

これは、C-x C-eというコマンドが、その直前にある式をREPLに送信後評価するという処理を行うためだ。

もうひとつ別のコードで試してみる。以下は、hello,worldという文字列を返すのコードになる。defunは、関数を定義するための記述で、以下のコードはhelloという関数名の関数を定義している。

(defun hello ()
  "hello, world")

このコードの末尾にEmacsのカーソルを移動させ、C-x C-eを入力し、REPL側の下端を見ると、=> HELLOという出力を確認できる。関数は実行しない限り結果は表示されず、関数定義の式を評価した結果として、関数のシンボルが返るようになっている。

hello関数の評価によって、REPL上ではhello関数を呼び出せるようになっている。確認するために、REPL上で関数を呼び出す。hello関数は、”hello,world”という文字列を返すものであるため、出力は以下のようになる。

CL-USER> (hello)
"hello,world"
CL-USER>

ここまでで、

  • REPL上での式の評価
  • ファイルに記述した式をREPLに送信し評価
  • REPL上で定義した関数の呼び出し

を行った。最後に、実行中のプログラム内の関数の修正を行う方法について説明する。まず、ファイル側のバッファの関数を以下のように修正する。

(defun hello ()
  (print "hello,world"))

printを使用し、文字列を出力するように変更した。次にこれを、C-x C-eでREPLに送信する。REPL側では、

CL-USER> (loop (hello) (sleep 5))

と入力し評価する。これは5秒毎にhello関数を呼び出すため、5秒毎に”hello,world”という文字が出力され続ける状態となる。これを、プログラムを停止させることなく”hello,lisp”という出力に変更するためには、ファイル側のコードを修正し、REPLに転送すれば良い。

(defun hello ()
  (print "hello,lisp"))

上記のように文字列の部分を修正し、カーソルを末尾に移動させてC-x C-eで転送する。すると、出力される文字列が”hello,lisp”になる。今までの内容が正常に動作していれば、REPLの出力は以下のようになっているはずだ。

CL-USER> (loop (hello) (sleep 5))

"hello,world"
"hello,world"
"hello,world"
"hello,world"
"hello,lisp"
"hello,lisp"
"hello,lisp"
"hello,lisp"
"hello,lisp"

最後に、このループし続けるプログラムを停止させるためには、C-c C-cを入力する。すると、REPLバッファに以下の内容が表示される。

Interrupt from Emacs
   [Condition of type SIMPLE-ERROR]

Restarts:
 0: [CONTINUE] Continue from break.
 1: [RETRY] Retry SLIME REPL evaluation request.
 2: [*ABORT] Return to SLIME's top level.
 3: [ABORT-BREAK] Reset this thread
 4: [ABORT] Kill this thread

Backtrace:
  0: (CCL::%NANOSLEEP 3 0)
  1: (#<Anonymous Function #x30200101AD8F>)
  2: (CCL::CHEAP-EVAL (LOOP (HELLO) ..))
 --more--

ここで、Restarts内の2番目にABORTとあるため、2を入力するとループが停止する。意図しない処理によって無限ループなどに陥った際は、C-c C-cで処理を中断し停止できる。