erlangの機能をlispの書式で使う

erlangの機能をlispの書式で使えるLFE(Lisp Flavoured Erlang)というプロジェクトが在るようです。

=> http://forum.trapexit.org/viewtopic.php?p=40268#40268

erlangの書式に慣れられなかった人もこれで安心です。

lfeの"doc/user-guide.txt"を見ながら、少し遊んでみようと思います。

erlangのversionはR12B-5、lfeのversionは0.3を使いました。

lfeを展開し、makeします。

bash-3.2$ unzip lfe_v0.3.zip 
Archive:  lfe_v0.3.zip
  inflating: lfe/COPYRIGHT           
  inflating: lfe/doc/release_notes.txt  
  inflating: lfe/doc/user_guide.txt  
  inflating: lfe/ebin/lfe_boot.beam  
  inflating: lfe/ebin/lfe_codegen.beam  
  inflating: lfe/ebin/lfe_comp.beam  
  inflating: lfe/ebin/lfe_eval.beam  
  inflating: lfe/ebin/lfe_gen.beam   
  inflating: lfe/ebin/lfe_io.beam    
  inflating: lfe/ebin/lfe_lib.beam   
  inflating: lfe/ebin/lfe_lint.beam  
  inflating: lfe/ebin/lfe_macro.beam  
  inflating: lfe/ebin/lfe_parse.beam  
  inflating: lfe/ebin/lfe_scan.beam  
  inflating: lfe/ebin/lfe_shell.beam  
  inflating: lfe/ebin/user_drv.beam  
  inflating: lfe/examples/gps1.lfe   
  inflating: lfe/examples/lfe_eval.lfe  
  inflating: lfe/Makefile            
  inflating: lfe/README              
  inflating: lfe/src/ChangeLog       
  inflating: lfe/src/lfe-mode.el     
  inflating: lfe/src/lfe-mode.elc    
  inflating: lfe/src/lfe_boot.erl    
  inflating: lfe/src/lfe_codegen.erl  
  inflating: lfe/src/lfe_comp.erl    
  inflating: lfe/src/lfe_eval.erl    
  inflating: lfe/src/lfe_gen.erl     
  inflating: lfe/src/lfe_io.erl      
  inflating: lfe/src/lfe_lib.erl     
  inflating: lfe/src/lfe_lint.erl    
  inflating: lfe/src/lfe_macro.erl   
  inflating: lfe/src/lfe_parse.erl   
  inflating: lfe/src/lfe_scan.erl    
  inflating: lfe/src/lfe_scan.xrl    
  inflating: lfe/src/lfe_shell.erl   
  inflating: lfe/src/user_drv.erl    
  inflating: lfe/test/recfs.lfe      
  inflating: lfe/test/test_bin.lfe   
  inflating: lfe/test/test_case.lfe  
  inflating: lfe/test/test_flet.lfe  
  inflating: lfe/test/test_guard.lfe  
  inflating: lfe/test/test_inc.lfe   
  inflating: lfe/test/test_lc.lfe    
  inflating: lfe/test/test_lc_e.erl  
  inflating: lfe/test/test_let.lfe   
  inflating: lfe/test/test_macro.lfe  
  inflating: lfe/test/test_pat.lfe   
  inflating: lfe/test/test_pat_e.erl  
  inflating: lfe/test/test_rec_defs.lfe  
  inflating: lfe/test/test_slurp.lfe  
  inflating: lfe/test/test_sr.lfe    
  inflating: lfe/test/test_try.lfe   
bash-3.2$ cd lfe
bash-3.2$ make
mkdir -p ebin
erlc -I include -o ebin -W0 -Ddebug +debug_info src/*.erl
#erl -I -pa ebin -noshell -eval -noshell -run edoc file src/leex.erl -run init stop
#erl -I -pa ebin -noshell -eval -noshell -run edoc_run application "'Leex'" '"."' '[no_packages]'
#mv src/*.html doc/
bash-3.2$ 

emacserlangモードの設定を行います。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; erlang-mode
(setq load-path (cons "/usr/local/lib/erlang/lib/erts-5.6.5/emacs/"
		      load-path))
(setq erlang-root-dir "/usr/local/lib/erlang")
(setq exec-path (cons "/usr/local/lib/erlang/bin" exec-path))
(require 'erlang-start)

M-x erlang-shellでerlangを起動します。

Erlang (BEAM) emulator version 5.6.5 [source] [smp:2] [async-threads:0] [kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1>

erlang-shellでは"help()."でhelpが表示できます。

6> help().
** shell internal commands **
b()        -- display all variable bindings
e(N)       -- repeat the expression in query <N>
f()        -- forget all variable bindings
f(X)       -- forget the binding of variable X
h()        -- history
history(N) -- set how many previous commands to keep
results(N) -- set how many previous command results to keep
v(N)       -- use the value of query <N>
rd(R,D)    -- define a record
rf()       -- remove all record information
rf(R)      -- remove record information about R
rl()       -- display all record information
rl(R)      -- display record information about R
rp(Term)   -- display Term using the shell's record information
rr(File)   -- read record information from File (wildcards allowed)
rr(F,R)    -- read selected record information from file(s)
rr(F,R,O)  -- read selected record information with options
** commands in module c **
bt(Pid)    -- stack backtrace for a process
c(File)    -- compile and load code in <File>
cd(Dir)    -- change working directory
flush()    -- flush any messages sent to the shell
help()     -- help info
i()        -- information about the system
ni()       -- information about the networked system
i(X,Y,Z)   -- information about pid <X,Y,Z>
l(Module)  -- load or reload module
lc([File]) -- compile a list of Erlang modules
ls()       -- list files in the current directory
ls(Dir)    -- list files in directory <Dir>
m()        -- which modules are loaded
m(Mod)     -- information about module <Mod>
memory()   -- memory allocation information
memory(T)  -- memory allocation information of type <T>
nc(File)   -- compile and load code in <File> on all nodes
nl(Module) -- load module on all nodes
pid(X,Y,Z) -- convert X,Y,Z to a Pid
pwd()      -- print working directory
q()        -- quit - shorthand for init:stop()
regs()     -- information about registered processes
nregs()    -- information about all registered processes
xm(M)      -- cross reference check a module
y(File)    -- generate a Yecc parser
** commands in module i (interpreter interface) **
ih()       -- print help for the i module
true
7> 

lfeのshellをerlang-shellから起動します。

3> cd("src/lfe").
/Users/TakayukiSuzuki/src/lfe
ok
5> ls().
COPYRIGHT          Makefile           README             doc                
ebin               erl_crash.dump     examples           src                
test               
ok
12> cd(ebin).
/Users/TakayukiSuzuki/src/lfe/ebin
ok
13> l(lfe_shell).
{module,lfe_shell} 
23> lfe_shell:server().
LFE Shell V5.6.5 (abort with ^G)
>

shellまで起動できました。
erlangはあまりなじみが無いので、いろいろ手探りです。
とりあえず、lfeのexamplesにあるgpsv1.lfeを動かすことを目標とします。

lfe shellでは、erlang-shellの機能を "(: c Command Arg ...)"で使える様です。

>  (: c cd "../example").
exception error: {bad_form,application}
  in function  lfe_eval:eval_expr/2
  in call from lists:map/2
  in call from lfe_eval:eval_call/2
>

文字列ではないんかー。引数の型が分からない…。
色々試行錯誤した結果、cdの場合、シンボルをクォートしてあげればよいことが分かりました。

> (: c pwd)
/Users/TakayukiSuzuki/src/lfe/ebin
ok
> (: c cd '..)
/Users/TakayukiSuzuki/src/lfe
ok
> (: c cd 'ebin)
/Users/TakayukiSuzuki/src/lfe/ebin
ok
> 

サンプル(gpsv1)をコンパイルする

このサンプルはPAIP由来のgpsの様です。

> (c '../examples/gps1.lfe)
#(module gps1)
> (slurp '../examples/gps1.lfe)
#(ok gps1)

cでコンパイルし、slurpでファイル内で定義したマクロや関数をlfe shellから使える様に出来ます。slurpは同時に1つのファイルのみを有効に出来るようで、別のファイルをslurpするとそれまでにslurpしていたファイル内の定義はlfe shellから見えなくなる様です。

gpsを使ってみます。

> (gps '(son-at-home) '(son-at-school) (school-ops))
false
> (gps '(son-at-home have-money have-phone-book car-needs-battery) '(son-at-school) (school-ops))
executing 'look-up-number'
executing 'telephone-shop'
executing 'tell-shop-problem'
executing 'give-shop-money'
executing 'shop-installs-battery'
executing 'drive-son-to-school'
solved
>

動きました。:を使うと、slurpしなくても良さそうです。

> (: gps1 gps '(son-at-home have-money have-phone-book car-needs-battery) '(son-at-school) (: gps1 school-ops))
executing 'look-up-number'
executing 'telephone-shop'
executing 'tell-shop-problem'
executing 'give-shop-money'
executing 'shop-installs-battery'
executing 'drive-son-to-school'
solved
>