LFE, Erlang関係で聞いたことはあったけど調べ物していて見かけたので試してみました。
どうせならRaspberry Pi2に入れてみようとしたらちょっと苦労したのでメモ。
LFE(Lisp Flavored Erlang)とは
その名の通り、見た目がLispのErlangです。本家のサイトはこちら。
Elixirが「Erlang VM上で動くRuby風の新しい言語」として設計されたのと異なり、LFEは「関数名やその中身はErlangのまま、見た目は完全にLisp」として設計されています1。
Qiitaでも過去に(lisp (flavored (erlang)))について基本その1やLisp Flavored Erlang (LFE) のインストールなどの記事がありますのでそちらも見てもらったほうがいいと思います。
Raspberry Pi2 (Raspbian) に LFE をインストールする
LFEのインストールはLFE-Quick Start/Getting LFEを見ると真っ先にhomebrewを使うのやり方が出てくるのでこれが良さ気です。
MacOSXではbrew
コマンドをしょっちゅう使っているのに、Linuxに似たようなシステムがあるとは全然知りませんでした。
Linuxbrewのインストール
まずLinux用のbrewをここに従って設定します。
必要なプログラム、ライブラリをapt-get install する。
$ sudo apt-get install build-essential curl git m4 ruby texinfo libbz2-dev libcurl4-openssl-dev libexpat-dev libncurses-dev zlib1g-dev
Linuxbrewをインストールする
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/linuxbrew/go/install)"
以下をインストールしていいか聞いてくるのでRETURN。
==> This script will install:
/home/azureuser/.linuxbrew/bin/brew
/home/azureuser/.linuxbrew/Library/...
/home/azureuser/.linuxbrew/share/man/man1/brew.1
Press RETURN to continue or any other key to abort
==> Downloading and installing Homebrew...
...
環境変数を設定する
下記を.bashrcや.bash_profileなどに追加します。
export PATH=$HOME/.linuxbrew/bin:$PATH
export MANPATH=$HOME/.linuxbrew/share/man:$MANPATH
export INFOPATH=$HOME/.linuxbrew/share/info:$INFOPATH
忘れないように
$ source ~/.bashrc
のように環境変数をセットしておきましょう。
Linuxbrewの初期化
$ brew doctor
以上で設定完了です。
lfeのインストール
上記のGetting LFEのページではこのあと
$ brew install lfe
でオシマイ、となっていましたが… 途中で失敗します2。
$ brew install lfe
==> Installing dependencies for lfe: wxmac, erlang, rebar
...
==> Installing lfe dependency: wxmac
==> Downloading https://downloads.sourceforge.net/project/wxwindows/3.0.2/wxWidgets-3.0.2.tar.bz2
...
checking for architectures to use in universal binary...
checking if C compiler (/usr/bin/gcc-4.9 -mmacosx-version-min=0) works with SDK/version options... configure: error: in `/tmp/wxmac20151025-330-1o2c8rv/wxWidgets-3.0.2':
configure: error: no. Try a different SDK
See `config.log' for more details
READ THIS: https://github.com/Homebrew/linuxbrew/blob/master/share/doc/homebrew/Troubleshooting.md#troubleshooting
wxmacが入らない、と言われてエラーになります3。
回避策はここにありました。wxmac(=wxwidget)はGUI用のライブラリです。とりあえず試しに動かす分には不要なので、先にlfeが依存するErlangをwxwidgetなしでbrewしてしまいます。
$ brew install erlang --without-wxmac
$ brew install lfe
LFEを起動していじってみる
LFEにもREPLがあるので起動していじってみます。
Hello, Worldなど
$ lfe
Erlang/OTP 18 [erts-7.1] [source] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
LFE Shell V7.1 (abort with ^G)
> (io:format "Hello, World~n")
Hello, World
ok
Erlangの同等のコードが
io:format("Hello, World~n").
なので確かに見た目はLisp, 中身はErlangですね。
もうちょっといじってみます。1から100まで足すのは
> (lists:foldl (lambda (n acc) (+ n acc)) 0 (lists:seq 1 100))
5050
こんな感じ。これもErlangのlists:foldl/3
、lists:seq/2
がそのまま出てきています。
FizzBuzz
もういっちょ。よくあるFizzBuzz問題。
(defun fbcheck (x)
(cond ((and (== (rem x 3) 0) (== (rem x 5) 0)) "FizzBuzz")
((== (rem x 3) 0) "Fizz")
((== (rem x 5) 0) "Buzz")
('true (integer_to_list x))))
(defun fizzbuzz (x)
(lists:map (lambda (x) (fbcheck x)) (lists:seq 1 x)))
...
(fizzbuzz 100)
("1"
"2"
"Fizz"
"4"
"Buzz"
"Fizz"
"7"
"8"
"Fizz"
...
Lispとしてどうなのよ?ということでlfeと同じLisp-2(関数と変数の名前が別の名前空間)であるCommon Lispと比較してみます。
(defun fbcheck (x)
(cond ((and (= (mod x 3) 0) (= (mod x 5) 0)) "FizzBuzz")
((= (mod x 3) 0) "Fizz")
((= (mod x 5) 0) "Buzz")
(t (format nil "~a" x))))
(defun fizzbuzz (x)
(mapcar 'fbcheck (loop for i from 1 to x collect i)))
(fizzbuzz 100)
("1" "2" "Fizz" "4" "Buzz" "Fizz" "7" "8" "Fizz" "Buzz"...
かなり似ていますね4。
Elixirと使い勝手を比べてみて
- Elixirにはiexという超強力なREPLがあるが、LFEのREPL(lfe)はErlangのerlと同程度の使い勝手。特にiexのh/1コマンドにはお世話になっているのでああいうのがないと辛い。
- 同様にプロジェクト管理がErlangのrebarを「何とか使えるかな」という状態。
- 資料が少ない。本家サイトのドキュメントもあちこちが工事中。
- 当然ライブラリも少ない。というかはじめからErlangのライブラリを利用することしか考えていないかもしれない。
ということでどうも「Erlangの文法は大嫌いだけどErlang VMの機能は慣れてるLispで使いたい」という強い要望と意思を持った人でないと使っていけないかな…と思いました5。
おまけ:
RaspbianにはCommon Lisp(glisp)はapt-getから入るんですね…
-
Lispは長い歴史上「何かのアプリの設定・操作用のDSL」として使われたことが幾度もあり(最も有名な例はEmacsでしょう)、Erlang VMを使うためのDSLみたいなものと考えれば自然な気がします。元々方言も多いですしね。 ↩
-
肝心な依存モジュールであるErlang自体が入らないのでElixirも同じ問題でbrewでのインストールに失敗します。 ↩
-
もちろん本家MacOSX用のhomebrewでは全く問題なしでした。 ↩
-
ただしやっぱりmapやseqなどをErlangの関数であるlists:map, lists:seqで呼び出さないといけなかったり、map関数で適用される関数をlambda式でくくらないといけなかったりややめんどくさい面が。 ↩
-
しばらくLispいじってなかったんで復習にはなりました。 ↩