2021/11/20 Introduce Clojerl for Alchemist
Introduce ClojerlClojerl.icon for AlchemistElixir.icon
2021/11/20 @ 2021/11/20 tokyo.ex Reboot #tokyoex
ne-sachirou.icon
.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆)
株式會社はてな株式會社はてな.iconで MackerelMackerel.icon を作ってゐます
ClojerlClojerl.icon =
ClojureClojure.icon
on ErlangErlang.icon VM
I 💕 ErlangErlang.icon VM
Free from crush.
supervisor and gen_server is a natural model of heterogeneous parallel processes.
I 💕 ClojureClojure.icon
It's Lisp.
Program is data.
Data is immutable.
Data is explicit. It's a map or a sequence.
Writing tests is very easy.
Libraries (both standard & 3rd. party) are basically stable, because data is stable.
Funny features🌟 including core.async & clojure.spec.
I 💕 ClojureClojure.icon
Runs on stable runtimes.
JVM : ClojureClojure.icon on JVM
Clojure - Java Interop
code:interop.clj
(.exampleMethod (ExampleJavaClass. arg1 arg2) arg3 arg4)
.NET : Clojure.NET
ECMAScript : ClojureScriptClojureScript.icon
ClojureScript - Quick Start
code:interop.cljs
(.querySelectorAll js/document ".class")
Erlang VMErlang.icon : ClojerlClojerl.icon (unofficial)
code:interop.clje
(exam.ple/erl-fun arg1 arg2)
Easy to integrate with other language's environments.
Python : libpython-clj on JVM
clj-python/libpython-clj: Python bindings for Clojure
code:libpython.clj
(require-python 'numpy :as np)
(np/array 1 2] [3 4)
ElixirElixir.icon$ \capClojerlClojerl.icon
ErlangErlang.icon VM
ErlangErlang.icon interoperability
Elixir.icon:module_name.f()
Clojerl.icon(module_name/f)
Functional programming.
threading (pipe operator)
Elixir.iconv |> f |> g
Clojerl.icon(-> v f g)
Data is basically immutable. Variable is reasignable.
$ diff ElixirElixir.icon ClojerlClojerl.icon
code:diff
- Phoenix
- Nerves
+ Lisp
+ Data oriented
+ Clojure ecosystem
data oriented programming
Example : Create a Web server
ne-sachirou/clojerl-example-20211120: Introduce Clojerl for Alchemist
Create a new project
$ rebar3 new clojerl_app example
Add Cowboy
Nine Nines: Cowboy User Guide
code:diff
diff --git a/rebar.config b/rebar.config
index a9410fa..95f5937 100644
--- a/rebar.config
+++ b/rebar.config
@@ -1,4 +1,5 @@
{erl_opts, debug_info}.
-{deps, {clojerl, "0.9.0"}}.
+{deps, [{clojerl, "0.9.0"},
+ {cowboy, "2.9.0"}]}.
{plugins, {rebar3_clojerl, "0.8.8"}}.
diff --git a/src/example.app.src b/src/example.app.src
index 1b8fb83..ddfc3cc 100644
--- a/src/example.app.src
+++ b/src/example.app.src
@@ -6,6 +6,7 @@
, [ stdlib
, kernel
, clojerl
+ , cowboy
]
}
, {mod, {'example.app', []}}
Create a webserver module to start Cowboy
code:clojure
;; src/example/webserver.clje
(ns example.webserver
(:require
cowboy
erlang
erlang.core
example.index-handler))
(erlang.core/behaviours gen_server)
(defn child-spec
[]
{:id :example.webserver
:start :example.webserver :start-link #erl()
:restart :permanent
:shutdown :brutal_kill
:type :worker
:modules '(:example.webserver)})
(defn start-link
[]
(gen_server/start_link #erl:local :example.webserver,
:example.webserver
nil
#erl()))
(defn init
_
(let [dispatch (cowboy_router/compile (clj->erl '(:example.index-handler nil)))]
(let* [#erl:ok listener-pid (cowboy/start_clear :example.listener
#erl(#erl:port 8080)
#erl{:env #erl{:dispatch dispatch}})]
(erlang/link listener-pid)))
#erl:ok nil)
(defn handle_call
_ state
#erl:noreply state)
(defn handle_cast
state
#erl:noreply state)
code:clojure
;; src/example/index_handler.clje
(ns example.index-handler)
(defn init
req state
(let [req (cowboy_req/reply 200
#erl{"content-type" "text/plain"}
"Hello Clojerl"
req)]
#erl:ok req state))
Supervise it.
code:diff
diff --git a/src/example/sup.clje b/src/example/sup.clje
index 4c5f198..1e74a1f 100644
--- a/src/example/sup.clje
+++ b/src/example/sup.clje
@@ -1,10 +1,16 @@
-(ns example.sup)
+(ns example.sup
+ (:require
+ erlang.core
+ example.webserver
+ supervisor))
(def sup-flags #erl{:strategy :one_for_one
:intensity 1
:period 5})
-(def child-specs #erl())
+
+(def child-specs #erl((clj->erl (example.webserver/child-spec))))
+
(defn start-link []
(supervisor/start_link #erl:local :example.sup
Start the application
$ rebar3 clojerl repl --apps example
Check it.
$ curl http://localhost:8080
Hello Clojerl
It's just a normal Cowboy application in ClojureClojure.icon world.
Monitoring the application by OpenTelemetryOpenTelemetry.icon
Add opentelemetryOpenTelemetry.icon
Erlang/Elixir | OpenTelemetry
code:diff
diff --git a/config/sys.config b/config/sys.config
new file mode 100644
index 0000000..18c775e
--- /dev/null
+++ b/config/sys.config
@@ -0,0 +1,7 @@
+[
+ {opentelemetry,
+ [{processors, [{otel_batch_processor,
+ #{exporter => {otel_exporter_stdout, []}}
+ }]
+ }]}
+].
diff --git a/rebar.config b/rebar.config
index 95f5937..922cfa0 100644
--- a/rebar.config
+++ b/rebar.config
@@ -1,5 +1,9 @@
{erl_opts, debug_info}.
{deps, [{clojerl, "0.9.0"},
- {cowboy, "2.9.0"}]}.
+ {cowboy, "2.9.0"},
+ {opentelemetry_api, "~> 1.0.0-rc.3"},
+ {opentelemetry, "~> 1.0.0-rc.3"}]}.
{plugins, {rebar3_clojerl, "0.8.8"}}.
+
+{shell, {config, "config/sys.config"}}.
diff --git a/src/example.app.src b/src/example.app.src
index ac6c9a9..2441fb4 100644
--- a/src/example.app.src
+++ b/src/example.app.src
@@ -5,6 +5,8 @@
, { applications
, [ stdlib
, kernel
+ , opentelemetry_api
+ , opentelemetry
, clojerl
, cowboy
]
Trace requests
code:diff
diff --git a/src/example/index_handler.clje b/src/example/index_handler.clje
index 2b84853..ee49789 100644
--- a/src/example/index_handler.clje
+++ b/src/example/index_handler.clje
@@ -1,10 +1,45 @@
-(ns example.index-handler)
+(ns example.index-handler
+ (:require
+ erlang
+ maps
+ opentelemetry))
+
+
+(defn now-microsecs
+ []
+ (let* [#erlmega-secs secs microsecs (erlang/now)]
+ (->> mega-secs
+ (* 1000)
+ (+ (->> secs
+ (* 1000000)))
+ (+ microsecs))))
+
+
+(defn with-response-time-span
+ callback attributes
+ (let [module (-> *ns* ns-name name)
+ tracer (-> module erlang/binary_to_existing_atom opentelemetry/get_application_tracer)]
+ (otel_tracer/with_span tracer
+ module
+ #erl{}
+ (fn _span-ctx
+ (let* [now-1 (now-microsecs)
+ resp (callback)
+ now-2 (now-microsecs)
+ attributes (-> (merge {"response-time" (- now-2 now-1)} attributes)
+ clj->erl
+ maps/to_list)]
+ (otel_span/set_attributes (otel_tracer/current_span_ctx) attributes)
+ resp)))))
(defn init
req state
- (let [req (cowboy_req/reply 200
- #erl{"content-type" "text/plain"}
- "Hello Clojerl"
- req)]
- #erl:ok req state))
+ (with-response-time-span
+ (fn []
+ (let [req (cowboy_req/reply 200
+ #erl{"content-type" "text/plain"}
+ "Hello Clojerl"
+ req)]
+ #erl:ok req state))
+ {"path" "/"}))
Start the application
$ rebar3 clojerl compile && rebar3 shell --apps example
Check it.
$ ab -n 100 -c 10 'http://127.0.0.1:8080/'
code:erlang
{span,206190385536417520207509724574528174945,12710625632486370212,[],
undefined,<<"clojure.core">>,internal,-576460738214198000,
-576460738204198000,
{<<"path">>,<<"/">>},{<<"response-time">>,16},
[],[],undefined,1,false,
{instrumentation_library,<<"clojerl">>,<<"0.9.0">>}}