何でこの document が無いんだ。無かったから書いた。英語にしたいが氣力が無い。
Clojure Advent Calendar 2020 が空いてゐたので參加しやう。
やってみる: Getting started
先づ rebar3_clojerl で project を作ろう。First, create a project by rebar3_clojerl.
rebar3 を install する。
github.com
$ 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 を追加する。
github.com
$ 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 を運用するなら是非入れておくといい。
ferd.github.io
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 の樣子がよく解る。
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 を使へる。
github.com
$ 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