何でこの document が無いんだ。無かったから書いた。英語にしたいが氣力が無い。
Clojure Advent Calendar 2020 が空いてゐたので參加しやう。
やってみる: Getting started
先づ rebar3_clojerl で project を作ろう。First, create a project by rebar3_clojerl.
rebar3 を install する。
$ sudo wget -O /usr/local/bin/rebar3 https://s3.amazonaws.com/rebar3/rebar3 $ sudo chmod +x /usr/local/bin/rebar3 $ rebar3 local install
Clojerl project の雛形を作る爲に global な rebar.config に rebar3_clojerl を追加する。
$ cat ~/.config/rebar3/rebar.config {plugins, [rebar3_clojerl]}.
project を作る。
$ rebar3 new clojerl_app example ===> Writing example/src/example/core.clje ===> Writing example/src/example/app.clje ===> Writing example/src/example/sup.clje ===> Writing example/src/example.app.src ===> Writing example/test/example/core_test.clje ===> Writing example/rebar.config ===> Writing example/.gitignore ===> Writing example/LICENSE ===> Writing example/README.md $ cd example
REPL を起動してみよう。雛形の rebar3_clojerl の ver. が古いので上げる。
diff --git a/rebar.config b/rebar.config index f7132a2..62590c3 100644 --- a/rebar.config +++ b/rebar.config @@ -1,4 +1,4 @@ {erl_opts, [debug_info]}. {deps, [{clojerl, "0.7.0"}]}. -{plugins, [{rebar3_clojerl, "0.8.3"}]}. +{plugins, [{rebar3_clojerl, "0.8.4"}]}.
そして起動する。
$ rebar3 clojerl repl ===> Fetching rebar3_clojerl v0.8.4 ===> Analyzing applications... ===> Compiling rebar3_clojerl ===> Verifying dependencies... ===> Fetching clojerl v0.7.0 ===> Analyzing applications... ===> Compiling clojerl ===> Analyzing applications... ===> Compiling example ===> Getting log of git repo failed in /Users/ne-sachirou/dev/example. Falling back to version 0.0.0 ===> Clojerl Compiling clojerl ===> Compiling clojure/core.clje... ===> Compiling clojure/pprint.clje... ===> Compiling clojure/core/reducers.clje... ===> Compiling clojure/core/server.clje... ===> Compiling clojure/data.clje... ===> Compiling clojure/zip.clje... ===> Compiling clojure/repl.clje... ===> Compiling clojure/test.clje... ===> Compiling clojure/xml.clje... ===> Compiling erlang/core.clje... ===> Clojerl Compiling example ===> Compiling example/core.clje... ===> Compiling example/sup.clje... ===> Compiling example/app.clje... =INFO REPORT==== 24-Dec-2020::00:17:04.695885 === application: clojerl exited: stopped type: permanent Clojure 0.7.0 clje.user=>
Clojure の函數を呼んでみよう。
clje.user=> (str "momonga" "MOMONGA") "momongaMOMONGA" clje.user=> (-> "momonga" (str "MOMONGA")) "momongaMOMONGA" clje.user=> (let [m1 "momonga" m2 "MOMONGA"] (str m1 m2)) "momongaMOMONGA" clje.user=> (let [[m1 m2] ["momonga" "MOMONGA"]] (str m1 m2)) "momongaMOMONGA" clje.user=> (let [{m1 :m1 m2 :m2} {:m1 "momonga" :m2 "MOMONGA"}] (str m1 m2)) "momongaMOMONGA" clje.user=> (let [{:keys [m1 m2]} {:m1 "momonga" :m2 "MOMONGA"}] (str m1 m2)) "momongaMOMONGA"
Clojure の標準 library は入ってゐないが、基本的な函數は使へる。
いよいよ Erlang の函數を呼んでみよう。
clje.user=> (math/log2 65536) 16.0 clje.user=> (base64/encode "momonga") "bW9tb25nYQ=="
OTP を Clojerl の標準 library として使へる訣だ。
Erlang の supervisor を使ってみよう。inets module を使って簡單な HTTP server を立てる。inets application が起動してゐる必要があるから src/example.app.src
に書く。
diff --git a/src/example.app.src b/src/example.app.src index 1b8fb83..678771e 100644 --- a/src/example.app.src +++ b/src/example.app.src @@ -6,6 +6,7 @@ , [ stdlib , kernel , clojerl + , inets ] } , {mod, {'example.app', []}}
4000 番 port で待ち受け、priv/public
を公開する HTTP server は Erlang ではこう書く。
PublicDir = filename:join(code:priv_dir(:example), "public"), {ok, _} = inets:start( httpd, [ {port, 4000}, {server_name, "example"}, {server_root, PublicDir}, {document_root, PublicDir}, {bind_address,"localhost"} ] ).
そのまま Clojerl に直すとこうだ。
(let [public-dir (binary/bin_to_list (filename/join (code/priv_dir :example) "public"))] (inets/start :httpd #erl(#erl[:port 4000] #erl[:server_name #erl"example"] #erl[:server_root public-dir] #erl[:document_root public-dir] #erl[:bind_address #erl"localhost"])))
これを supervisor にぶら下げよう。src/example/core.clje
に實裝を書き、src/example/sup.clje
に有る supervisor にぶら下げる。
diff --git a/priv/public/index.html b/priv/public/index.html new file mode 100644 index 0000000..2e52729 --- /dev/null +++ b/priv/public/index.html @@ -0,0 +1,3 @@ +<!DOCTYPE html> +<title>Clojerl</title> +<p>Clojerl</p> diff --git a/src/example/core.clje b/src/example/core.clje index a0c7d74..9dc114f 100644 --- a/src/example/core.clje +++ b/src/example/core.clje @@ -1 +1,17 @@ (ns example.core) + +(defn start-link [] + (gen_server/start_link #erl[:local :example.core] + :example.core + #erl() + #erl())) + +(defn init [_] + (let [public-dir (binary/bin_to_list (filename/join (code/priv_dir :example) "public"))] + (inets/start :httpd + #erl(#erl[:port 4000] + #erl[:server_name #erl"example"] + #erl[:server_root public-dir] + #erl[:document_root public-dir] + #erl[:bind_address #erl"localhost"]))) + #erl[:ok nil]) diff --git a/src/example/sup.clje b/src/example/sup.clje index 4c5f198..f8e58fd 100644 --- a/src/example/sup.clje +++ b/src/example/sup.clje @@ -4,7 +4,11 @@ :intensity 1 :period 5}) -(def child-specs #erl()) +(def child-specs #erl(#erl{:id :example.core + :start #erl[:example.core + :start-link + #erl()] + :restart :permanent})) (defn start-link [] (supervisor/start_link #erl[:local :example.sup]
REPL を再起動し、application を呼び出さう。
clje.user=> (example.app/start :normal nil)
http://localhost:4000/index.html
を見ると先の index.html
の中身が見える筈だ。
Hex.pm の library も使へる。
Recon を入れて VM の樣子を覗いてみよう。Recon は Erlang 實行環境の樣子を安全に知る事のできる debug tool だ。Erlang VM を運用するなら是非入れておくといい。
diff --git a/rebar.config b/rebar.config index 90fa363..e476f18 100644 --- a/rebar.config +++ b/rebar.config @@ -1,5 +1,6 @@ {erl_opts, [debug_info]}. -{deps, [{clojerl, "0.7.0"}]}. +{deps, [{clojerl, "0.7.0"}, + {recon, "2.5.1"}]}. {plugins, [{rebar3_clojerl, "0.8.4"}]}.
install する。
$ rebar3 upgrade ===> Analyzing applications... ===> Verifying dependencies... ===> Fetching recon v2.5.1 ===> No upgrade needed for clojerl ===> No upgrade needed for recon
REPL を立ち上げて呼んでみよう。今囘は VM の樣子を覗いてみる。普通に呼ぶと Erlang の term が返ってくる。
clje.user=> (recon/node_stats_list 1 1000) #erl(#erl[#erl(#erl[:process_count 81] #erl[:run_queue 0] #erl[:memory_total 63948120] #erl[:memory_procs 20665656] #erl[:memory_atoms 622772] #erl[:memory_bin 1882056] #erl[:memory_ets 6677800]) #erl(#erl[:bytes_in 0] #erl[:bytes_out 0] #erl[:gc_count 7] #erl[:gc_words_reclaimed 110416] #erl[:reductions 60219810] #erl[:scheduler_usage #erl(#erl[1 0.008229647869447678] #erl[2 0.0016643060189602482] #erl[3 0.0010973392240004188] #erl[4 0.0014953839650194726] #erl[5 0.0013679809490133251] #erl[6 0.0015487337521235827] #erl[7 0.0010311711991118494] #erl[8 0.0015200885780010886] #erl[9 0.0012850169590631723] #erl[10 0.00136601901759231] #erl[11 0.0014677412982497728] #erl[12 0.0011447580649143999] #erl[13 0.0013334281548910145] #erl[14 0.0013403316357774714] #erl[15 0.0013136567958034098] #erl[16 0.0012386041504103987] #erl[17 0.0] #erl[18 0.0] #erl[19 0.0] #erl[20 0.0] #erl[21 0.0] #erl[22 0.0] #erl[23 0.0] #erl[24 0.0] #erl[25 0.0] #erl[26 0.0] #erl[27 0.0] #erl[28 0.0] #erl[29 0.0] #erl[30 0.0] #erl[31 0.0] #erl[32 0.0])])])
erl->clj
函數で Clojure の data に變換できる。
clje.user=> (erl->clj (recon/node_stats_list 1 1000)) ([([:process_count 81] [:run_queue 0] [:memory_total 63989136] [:memory_procs 20700568] [:memory_atoms 622926] [:memory_bin 1885320] [:memory_ets 6679160]) ([:bytes_in 0] [:bytes_out 0] [:gc_count 1] [:gc_words_reclaimed 27412] [:reductions 117717] [:scheduler_usage ([1 3.095390962856307e-5] [2 7.938116639390832e-4] [3 2.2965689260245194e-5] [4 2.7958342070315232e-5] [5 2.895673863248307e-5] [6 2.795823040377676e-5] [7 3.0954095077000806e-5] [8 3.794331269083989e-5] [9 2.9955276771779733e-5] [10 3.1952295223231713e-5] [11 3.794331269083989e-5] [12 3.594629623342726e-5] [13 3.095378599750572e-5] [14 3.09535696455317e-5] [15 3.095369327486081e-5] [16 3.295070574420667e-5] [17 0.0] [18 0.0] [19 0.0] [20 0.0] [21 0.0] [22 0.0] [23 0.0] [24 0.0] [25 0.0] [26 0.0] [27 0.0] [28 0.0] [29 0.0] [30 0.0] [31 0.0] [32 0.0])])])
pretty print するには OTP の io module を使ふ。
clje.user=> (io/fwrite "~120p~n" #erl((recon/node_stats_list 1 1000))) [{[{process_count,81}, {run_queue,0}, {memory_total,64171600}, {memory_procs,20845064}, {memory_atoms,623317}, {memory_bin,1903656}, {memory_ets,6685032}], [{bytes_in,0}, {bytes_out,0}, {gc_count,1}, {gc_words_reclaimed,25919}, {reductions,120052}, {scheduler_usage,[{1,5.4930677485013915e-5}, {2,9.128479858418676e-4}, {3,2.2971010584642182e-5}, {4,3.495588567228158e-5}, {5,3.2958406491008346e-5}, {6,2.696596894718865e-5}, {7,4.694076075992098e-5}, {8,2.896344812846188e-5}, {9,5.0935719122467445e-5}, {10,3.096058717252937e-5}, {11,2.5967151553285093e-5}, {12,5.1934354975311204e-5}, {13,6.491736020046481e-5}, {14,2.596697001414201e-5}, {15,2.896336134789491e-5}, {16,2.9962337341961154e-5}, {17,0.0}, {18,0.0}, {19,0.0}, {20,0.0}, {21,0.0}, {22,0.0}, {23,0.0}, {24,0.0}, {25,0.0}, {26,0.0}, {27,0.0}, {28,0.0}, {29,0.0}, {30,0.0}, {31,0.0}, {32,0.0}]}]}] :ok
VM の樣子がよく解る。
Erlang との遣り取り
data 型
Erlang の函數に渡せる data は Clojerl では以下の樣に書く。
Erlang | Clojerl | Elixir | |
---|---|---|---|
整數 | -42 |
-42 |
-42 |
浮動小數点數 | -42.29e3 |
-42.29e3 |
-42.29e3 |
atom | atom or 'atom' |
:atom |
:atom |
charlist | "charlist" |
#erl"charlist" |
'charlist' |
binary | <<"binary">> |
"binary" |
"binary" or <<"binary">> |
list | [Item, Item] |
#erl(item item) |
[item, item] |
tuple | {Element, Element} |
#erl[element element] |
{element, element} |
map | #{Key => Value} |
#erl{key value} |
%{key => value} |
list は Clojure の list '(item item)
に由來し、tuple は vector [item item]
に、map は map {key value}
に #erl
reader macro を附けたものである。
clj->erl
函數と erl->clj
函數で變換してもいい。
函數を data にするには arity を指定しなければならない。
clje.user=> math/log.1 #<math/log> clje.user=> example.app/start.2 #<example.app/start>
data にした函數は高階函數に渡せる。
clje.user=> (lists/map (fn [x] (math/sin x)) #erl(1 2)) #erl(0.8414709848078965 0.9092974268256817) clje.user=> (lists/map #(math/sin %) #erl(1 2)) #erl(0.8414709848078965 0.9092974268256817) clje.user=> (lists/map math/sin.1 #erl(1 2)) #erl(0.8414709848078965 0.9092974268256817)
函數の呼び出し
函數の呼び出しは自然である。a.b
module の f
函數は、
(a.b/f arg1 arg2)
で呼べる。Clojerl で定義した函數も Erlang で定義した函數も同じである。
project 管理
rebar3 を使ふといい。rebar3_clojerl を入れておけば便利な command を使へる。
$ rebar3 new --help app (built-in): Complete OTP Application structure. clojerl_app (plugin): Complete Clojerl application structure clojerl_escript (plugin): Complete Clojerl escriptized application structure clojerl_lib (plugin): Complete Clojerl library (no processes) structure clojerl_release (plugin): Clojerl release structure for executable programs cmake (built-in): Standalone Makefile for building C/C++ in c_src escript (built-in): Complete escriptized application structure lib (built-in): Complete OTP Library application (no processes) structure plugin (built-in): Rebar3 plugin project structure release (built-in): OTP Release structure for executable programs umbrella (built-in): OTP structure for executable programs (alias of 'release' template) $ rebar3 help clojerl clojerl <task>: compile Compile clojerl project escriptize Generate escript archive. release Build a release for the Clojerl project. repl Start a clojerl repl run Run the project's -main function. test Test clojerl project