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

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

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

音樂は SoundCloud に公開中です。

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

Programming は GitHub で開發中です。

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))