c4se記:さっちゃんですよ☆

.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆)

.。oO(此のblogは、主に音樂考察Programming に分類されますよ。ヾ(〃l _ l)ノ゙♬♪♡)

音樂は SoundCloud に公開中です。

考察は現在は主に Scrapbox で公表中です。

Programming は GitHub で開發中です。

普段使ひの shell script を Clojure (Babashka) で書き換へた

Babashka は 10 ms 程度で起動する Clojure の runtime だ。Clojure を GraalVM で native code に compile し、shell script 使ひに便利な Clojure の標準 library と Java の class とを殘して削り、便利な library を幾つか加えたものである。66 MB に迄削ってある。

github.com

Haskell is good for scripting language ヾ(〃l _ l)ノ゙ 以來、日用 script はなるべく Haskell で書いてきた。これらを Babashka で書き直した。

github.com

Clojure の標準 library は私としては覺え易いし、Babashka は加えて JSONcurl が library として有り便利だ。

Clojure で日本の祝日を引く library を作った

Clojure で日本の祝日を引く library を作った。

github.com

既に同様の library が Java に在る (holiday-jp/holiday_jp-java) が、今囘のものは JVM で動く Clojure だけではなく、Erlang VM (BEAM) 上で動く Clojerl、.NET で動く Clojure CLRJavaScript に transpile される ClojureScript に對應した。これが Java 版には無い特徴だ。祝日の data は holiday-jp/holiday_jp を使ってゐる。

holiday-jp/between で二つの日附の間に在る祝日を引ける。

(is (= [{:date #inst "2019-11-23T00:00:00.000-00:00"
         :week "土"
         :week_en "Saturday"
         :name "勤労感謝の日"
         :name_en "Labor Thanksgiving Day"}
        {:date #inst "2020-01-01T00:00:00.000-00:00"
         :week "水"
         :week_en "Wednesday"
         :name "元日"
         :name_en "New Year's Day"}]
       (holiday-jp/between #inst"2019-11-22" #inst"2020-01-02")))

他に holiday-jp/holiday?holiday-jp/on を實裝した。

複數の Clojure 實裝に對應するには、Clojure に reader conditionals と云ふ仕組みが有りこれを使ふ。

clojure.org

何故作ったのか、一つは個人的な練習の爲であるが、一つは Elixir で作ってゐた application を Clojerl に移植してゐ、それに使ふ爲だ。library を作ると責務が明らかに成る爲、test を明解に書けて好い (Library を作ると云ふ開発手法)

昔に Elixir で同等の library を作った (今でもメンテしてる!)。

github.com

Clojure のも暫く使へたら holiday-jp org へ移そうかと思ってゐる。

Clojerl の Slack client を書いた

Erlang の好い感じの Slack client が無かったので書いた、のだが Clojerl で書いたので Erlang からは使へない。

github.com

README に書いた通り gen_event bahaviour を書けば Slack と會話出來る。

(ns example-bot.handler1)

(erlang.core/behaviours gen_event)

(defn init [{:keys [name] :as args}] #erl[:ok args])

(defn handle_call [_ state] #erl[:ok :ok state])

(def handle_event
  (fn* ([#erl[:receive-msg #as(msg #erl{"channel" channel "text" text "type" "message"})]
              #as(state #erl{:name name})]
         ; Handle msg here.
         #erl[:ok state])
       ([_ state]
         #erl[:ok state])))

gun を使って WebSocket で繋いでゐる。であるから WebSocket が通る通信路が要る。

github.com

Elixir には好い Slack client が在るので其れを使ひませう。

github.com

Clojerl で Erlang/OTP behaviour

behaviour (英國英語) は Java の interface に似て、module に指定の函數が實裝してゐなければ警告又は error を出す機能だ。Clojerl にも、無くても困らないが有ると嬉しい。

document には当然 (なんで当然?) 無い。GitHub repository の examples にも無い。Clojerl の test code に在った。

github.com

(ns example.server)

(erlang.core/behaviours gen_server)

; implementations here

これだけである。

Clojerl で Erlang の pattern match を行ふ。guard を使ふ

Clojerl は Erlang VM (BEAM) で動く Clojure である。

Erlang/Elixir では pattern match を頻繁に使ふ。一つは條件分岐する爲。慣れてゐる Elixir で書こう。

defmodule Example do
  def example1(:a, v), do: v + 1
  def example1(_, v), do: v

  def example2(v) do
    case v do
      {:a, v} -> v + 1
      {_, v} -> v
    end
  end
end

example1 函數では第一引數が :a であれば加算しさうでなければ何もしない。example2 函數の樣に data の内部構造にも match 出來る。pattern match で條件分岐するのは主に tuple に對して效果が大きい。tuple は位置と意味を混淆 (complect) すると云ふ事で Clojure には無く、全くその通りなのだが Erlang では頻用するから pattern match をしたい。

Simple Made Easy

これなら迂遠ではあっても Clojure でも書ける。しかし次のは書けない。

defmodule Example do
  def example({:x, v, 42}, [%{x: v, y: y} | rest]), do: {y, rest}
end

一つ目の tuple の中の v と、二つ目の list の中の map の中の v とが同じ値でなければこれには match しない。この樣に Erlang の pattern match は非線形である。また變數だけでなく値も書ける。既に束縛 (a.k.a. 代入) 済みの變數を patter の中に書いた場合には再束縛はされず、その値の data にしか match しない。これを Clojure で書くのは不可能ではないものの、pattern match library を一通り書かなければならない。

pattern match は他にも返り値を檢査するのに使ふ。

{:ok, pid} = GenServer.start_link(Example, nil)

この場合 pid 變數に代入するのだが、:ignore{:error, reason} が返って來たら error として crush させたいのだ。これも Clojure で書くのは面倒だ。

pattern match は擴張出來ず再利用も出來ないと云ふ事で Clojure には無く、multi method や分配束縛を使ふのだが、Erlang/OTP には behaviour や recieve 等 pattern match を前提とした機能が頻發するので pattern match を書きたい。また先の缺點には Scala の unapply や Egison 等の例外が在る。

Why no pattern matching? clojure.org clojure.org

さて Clojerl で pattern match する方法だが document には無い。この document には何が有るんだ? 果たして GitHub repository の examples に在った。

github.com

fn*let* 特殊形式を使ふ。返り値の檢査は let* で行ふ。

(let* [#erl[:ok pid] (gen_server/start_link example nil)]
      pid)

束縛せず檢査だけするなら以下で好い。

(let* [#erl[:ok _] (gen_server/start_link example nil)])

fn* は無名函數を作るもので、callback として渡す事も在るが、主に def と組み合はせて使ふ。

(def example
  (fn* ([:a v]
        (+ v 1))
       ([_ v]
        v)))

case や recieve もそれぞれ case*recieve* 特殊形式として書ける。

(case* v
       #erl[:a v] (+ v 1)
       #erl[_ v] v)

(recieve*
  #erl[:ex1 v 42 #erl{:x v :y y}] v
  #erl[:ex2 message] message
  _ #erl[:error "Unknown message"]
  (after 1000 #erl[:error "Timeout"]))

函數定義で使ふ pattern match と似た機能に Erlang には guard が在る。引數が條件に當て嵌まる時だけ本體を實行させる。これも Clojerl の document には無く examples に在った。

github.com

fn* 特殊形式の機能である。

(fn* ([v] {:when (erlang/is_pid v)}
      :pid)
     ([v] {:when (erlang/is_integer v)}
      :integer))