関数パターンマッチングのまとめ

オブジェクトリテラルも使えるようになって、もう大きな変化も無さそうなのでまとめます。

基本

シンボルはそのままローカル変数になります。

`x[
  x
].(1) //1

引数の数が合わなければエラーになります。

`x[
  x
].() //error

仮引数を書かなければ、引数ゼロを指定したことになります。

`[
  arg.size.== 0
].() //true

Method.| でオーバーロードします。引数エラー時に次のMethodを試します。

f: `x[
  1
].| `x y[
  2
]
f(0) //1
f(0 0) //2

無視

アスタリスクで引数が無視されます。

`*[
  arg.[0]
].(1) //1

残りの引数を無視する、というのもアスタリスクでできます。

`x *[
  x
].(1 2 3) //1

展開

.@でRange化します。

`x xs.@[
  xs.to_Array
].(1 2 3) //{2 3}

実引数側の.@で引数展開になります。

`x xs.@[
  xs.to_Array
].(1..3.@) //{2 3}

型指定

シンボルに続けて()を書くことで型指定ができます。そのオブジェクトが自身か親で無い場合にエラーになります。

f: `x(String)[
  x
].| `x[
  x.@name
]
f('str') //str
f(1) //Int

複数指定すると、どちらでもいい、という意味になります。

f: `x(Int Double)[
  x
].| `x[
  x.@name
]
f(1) //1
f(1.0) //1.0
f(1u) //Uint

メソッドコール

シンボルがメソッドコールを持つ場合、シンボルをまず定義し、その式全体が評価され、その結果がfalseの場合エラーになります。

f: `x.% 2[
  'odd'
].| `x.% 2.== 0[
  'even'
].| `*[
  'else'
]
f(1) //odd
f(2) //even
f(3.0) //else //Double.% は未定義でエラー

デフォルト引数

.=で実引数が足りない場合のデフォルト値を与えられます。

f: `x.= 0 y.= 1[
  {x y}
]
f()  //{0 1}
f(1) //{1 1}
f(Null 2) //{0 2} //実引数がNullの場合もデフォルト値

リテラル

リテラルを置くとそうでない場合にエラーになります。

f: `1[
  1
].| `x[
  0
]
f(1) //1
f(2) //0
f(1.0) //1 //単純に、その値の.==でtrueになるかどうかなので、型が一致しなくてもエラーにならない。
f('1') //0 //.==自体がエラー

分解(デストラクチャ)

配列リテラルで配列を分解します。

`{x y z}[
  y
].({1 2 3}) //2

分解式内でのパターンマッチングも可能です。

f: `{0 y(Int) z.= 1 *}[
  z
].| `*[
  'error'
]
f({0 0}) //1
f({1 0}) //error
f({0 2.0 3}) //error
f({0 0 0 0}) //0

Pairを作る.=>でPair(Mapのvalue_type)が分解されます。

Map.new(:a.=> 1 :b.=> 2).collect& `x.=> y[
  y
] //{1 2}

オブジェクトリテラルでslotの分解ができます。key: valuevalue部分がパターンマッチング式として処理されます。

`{a: x  b: y}[
  {x y}
].({a: 1 b: 2}) //{1 2}

指定されたkeyが無ければエラーになりますが、指定されてないものがあってもエラーにはなりません。

f: `{a: x  b: y}[
  {x y}
].| `{a: x}[
  {x}
]
f({a: 1}) //{1}
f({a: 1 c: 2}) //{1}

def_safe(:*)により、そのkeyが無くてもよいという指定になり、その値はNullになります。

`{a:* x  b: y}[
  {x y}
].({b: 2 c: 3}) //{Null 2}

Nullなので、valueにデフォルト引数を与えることで、デフォルト値が指定できます。

`{a:* x.= 0  b: y}[
  {x y}
].({b: 2 c: 3}) //{0 2}

その引数自体のデフォルト値を指定することも可能です。*1

f: `{a: x}.= {a: 1}[
  x
]
f() //1
f(1) //aが未定義でerror

親のslotは参照されます。

`{@name: name}[
  name
].(1) //Int

キーワード引数 (非推奨)

他の引数との兼ね合いが難しいので、オブジェクトリテラルのデストラクチャを使うほうがその部分に限定できていいと思います。
Symbol_idリテラルにデフォルト引数を指定した場合にキーワード引数として扱われます。

f: `:key.= 'val'[
  key
]
f(:key 1) //1
f(1) //'val' //キーワード引数に関係ない実引数は無視され、エラーにならない。
f(1 :key 2) //2
f(:key 3 1) //3

f2: `x :key.= 'val'[
  {x key}
]
f2(:key 1) //{:key 1}
f2(1) //{1 'val'}
f2(1 :key 2) //{1 2}
f2(:key 3 1) //{:key 3}

関数型言語によくある例

len: `{x xs.@}[1.+ recur(xs)].|`{}[0]
len({1 2 3}) //3

quicksort: `{}[{}].|`{x xs.@}[
  recur(xs.remove&`y[y.>= x]).+ {x}.+ recur(xs.remove&`y[y.< x])
]
quicksort({2 3 1}) //{1 2 3}

fib: `n.<= 2[
  1
].| `n[
  recur(n.dec).+ recur(n.dec(2))
]
fib(10) //55

*1:Giraffe+ 0.6.29より