この記事は,はてなエンジニア Advent Calendar 2017の12日目の記事です。 前日は id:cohalz さんによる 学生がエンジニアで仕事をして知識を増やすこと - cohаlz.hatеnablоg.сom でした。
概要
最近Common Lispで色々な実験をしています. Common Lispをはじめたばかりのころは,現代的な開発のやり方がよくわからずに苦労しました. 今となっては(まだわからないことだらけですが)ある程度やり方がわかってきたので, 主に,実用的な「じゃあこれからCommon Lispソフトウェアを作るぞ!!」といったときに使えそうなものをチョイスした備忘録を残そうとおもいます。
ASDFなどちょっと端折っている部分もありますが,ご容赦ください.
これを書いた人のレベル
- Common Lispをはじめて1年くらいの社会人2年目エンジニア
処理系マネージャroswellを入れる
Common Lispにはたくさんの処理系がある.これらはインストール方法も異なるし,使い方も異なる.せっかく言語仕様が標準化されているのに,利用方法が異なるのはつらいことだ.できれば統一的に処理系を利用したい.
Roswellはそういった事情で生まれた.RubyのrbenvやPerlのplenvのように,Roswellは適当なバージョンの適当な処理系をインストールし,管理し,呼び出してくれる.
ついでにgo get
のようなアプリケーションインストーラ的な機能もあるから,現代的な開発にうってつけの便利なツールだ.
インストール方法は https://github.com/roswell/roswell/wiki/Installation を手本として説明する.
まず開発用のlibcurl
をインストールしておく.バージョンは3でも4でも動くので,好きな方をインストールしておくこと.
# 例: Ubuntuの場合 $ sudo apt install libcurl4-openssl-dev
OS Xの場合は,このままbrew
を使えば完了する.
$ brew install roswell $ ros setup
Linux/FreeBSDの場合,パッケージマネージャのレポジトリに登録されていることがある(linuxbrewやAUR,XBPS)が,登録されていないときはソースコードをビルドする.
ビルドといっても難しくはない.GitとAutomake,標準的なビルドツールをインストールした状態で(CentOSではzlib-devel
もインストールして),次の手順に従う.
$ git clone -b release https://github.com/roswell/roswell.git $ cd roswell $ sh bootstrap # ↓FreeBSDの場合は LDFLAGS="-L/usr/local/lib/" CFLAGS="-I/usr/local/include/" ./configure $ ./configure $ make $ sudo make install $ ros setup
Roswellのインストールができたら,好みの処理系を入れておく.
$ ros install ccl-bin/1.11 $ ros use ccl-bin/1.11 $ ros run # REPLを開く Welcome to Clozure Common Lisp Version 1.11-r16635 (LinuxX8664)! CCL is developed and maintained by Clozure Associates. For more information about CCL visit http://ccl.clozure.com. To enquire about Clozure's Common Lisp consulting services e-mail info@clozure.com or visit http://www.clozure.com. ? ^D ? ^D ? ^D $
Common Lispプロジェクトを作成する
cl-project
を使えば一発でプロジェクトが作成されます.プロジェクトを作成したい場所でREPLを開きます.
$ cd WHERE_PROJECT_WILL_BE_CREATED_TO $ ros run
(ql:quickload :cl-project) (cl-project:make-project #P"./hogeproject" :author "Windymelt") writing ./hogeproject/hogeproject.asd writing ./hogeproject/hogeproject-test.asd writing ./hogeproject/README.org writing ./hogeproject/README.markdown writing ./hogeproject/.gitignore writing ./hogeproject/src/hogeproject.lisp writing ./hogeproject/t/hogeproject.lisp
$ tree hogeproject -a hogeproject ├── .gitignore ├── README.markdown ├── README.org ├── hogeproject-test.asd ├── hogeproject.asd ├── src │ └── hogeproject.lisp └── t └── hogeproject.lisp 2 directories, 7 files
ソースとテストとREADMEとASDファイル,そして.gitignore
が作成されまし
た.
このまま開発することができます.
プロジェクトを実行する
コードは実行されるためにあるので,実行したいですよね. EmacsのCommon Lisp開発環境であるSLIMEの使い方は別のエントリに任せるとして,ここではCLIからアプリケーションを起動する手順に注目します.
運用段階でよくある状況は,ある関数(main
関数のような)をエントリポイントとして,処理系を実行するといったものです.
これを行うスクリプトを作りましょう.
この場合に行わなければならないことは,
- 処理系の起動
- ライブラリの読み込み
- プロジェクトの読み込みとコンパイル
- 実行
です.けっこうやることが多いですね.
ここでは処理系ごとの差異を埋めるためにroswellを使います*1. Roswellは,上掲のうち,ライブラリの読み込み以外を行うことができます.ライブラリの読み込みは,ASDFやquicklispの仕事です.
プロジェクトを読み込む一般的なスクリプト
プロジェクトにこういうパッケージと関数があるとしましょう.
;;; src/hogeproject.lisp (in-package :cl-user) (defpackage hogeproject (:use :cl)) (in-package :hogeproject) (defun hoge () (format t "hello, hoge!~%"))
まずは大前提として,スクリプトから呼び出される側の関数をexport
します.
Common Lispのパッケージシステムでは,export
していない関数はパッケージの外部から呼び出せません.
ここではhogeproject:hoge
をexport
します.
;;; src/hogeproject.lisp (in-package :cl-user) (defpackage hogeproject (:use :cl) (:export :hoge)) ;; exportしている (in-package :hogeproject) (defun hoge () (format t "hello, hoge!~%"))
これでプロジェクト側の準備が整いました.これからこのプロジェクトを呼び出すスクリプトを作成します.
まずプロジェクトのどこか,たとえばscript/
にroswellスクリプトを作成します.
$ mkdir script $ cd script $ ros init bootstrap Successfully generated: bootstrap.ros
rosファイルが作成されました.これがroswellスクリプトで,これ自体に実行権限を与えるだけで実行できるようになっています*2.
さて,プロジェクトを読み込ませるためには,ASDFがシステムを見付けられる必要があります.roswellには,ASDFがシステムを探す場所をオーバライドする機能があるので,これを使います.
roswellのコマンドラインオプション-S
でこの機能を呼び出し,プロジェクトのASDファイルがある場所を読み込み元として指示します.
rosファイルの冒頭にroswellを呼び出す箇所があるので,ここを修正しましょう.
#!/bin/sh #|-*- mode:lisp -*-|# #| <Put a one-line description here> DN=$(dirname $0) REPO=$(cd $DN/.. && pwd) # rosファイルの親ディレクトリにasdファイルがあるとき exec ros -Q -S ${REPO} -- $0 "$@" # ^^^^^^^^^^ ここを追加した |#
この状態でスクリプトを起動すると,ASDFがプロジェクトのシステムを発見できるようになり,quicklisp
で依存ライブラリを含めたロードができるようになります.
rosファイル中のquicklispにライブラリを読み込ませる箇所に,自分のプロジェクトを追加します.最終的にはこのようになります.
(progn ;;init forms (ros:ensure-asdf) #+quicklisp (ql:quickload '(:hogeproject) :silent t) ; hogeprojectが読み込まれる!! )
最後に,rosファイルでhogeproject:hoge
を呼び出してみましょう.
(defun main (&rest argv) (declare (ignorable argv)) (hogeproject:hoge))
$ script/bootstrap.ros hello, hoge! $
プロジェクトの関数を呼び出すスクリプトの実行ができました.めでたしめでたし. Roswellを一枚噛ませることで,処理系によらないプロジェクトのロードと実行ができるようになりました.
結び
Common Lispにも現代的なエコシステムが生まれつつあります。柔軟な言語を試してみてはいかがでしょう。
See other
require, ASDF, quicklispを正しく使う | κeenのHappy Hacκing Blog
*1:Perlなどと違って,Common Lispには複数の処理系があります.