改造 GCaml の書き直しもしくは $Caml #1: dispatch 周り

まー要するに何かといいますと、type class の dictionary passing とか、GCaml の flow graph dispatch とか、SML# の polymorphic レコードの index passing とか、結構似ているので、dispatch だけ、それを何に使うか特化せずに書いてみたわけ。

んでもって、且つ、OCaml の optional labeled argument のアイデアを使って、dispatch argument を省略*可能*引数にしてみました。$ という文字から始まるラベル($ とか、 $hogehoge とか) は dispatch argument ($-arg) になります。上で書いた例の dispatch abstraction ($-abst), application ($-app) は完全に implicit でしたが、$Caml では明示的に与えることも出来る訳。($-なんたらは私が勝手に作った用語です。これからも。) まぁ、だから、別に新しいものじゃなくて、いろいろ組み合わせて見た、感じ。
将来的には polytypic な物も書けるんじゃないのかな、でも polytypic って何だっけ、そんな、感じ。

名前

$ は:

  • Dollar
  • Iが入ってる
  • Sも入ってる

ので DISpatch にはぴったりな名前でしょう。GCaml 3.11 とか $Caml とか DISCaml とか、呼んでいる。

$-arg の省略と $-abst

$-arg は省略可能引数ですが、OCaml に元からある ?label な optional argument とは挙動が違います。簡単な違い:

  • 初期値がない
  • 省略されると外の let binding で再バインドされる

$-arg を持つ関数を作ってみる:

# let f $l:d x = d x;;
val f : $l:('a -> 'b) -> 'a -> 'b = <fun>

これを $l に適用せず(というか $l 引数を省略して)使ってみる:

# f 3;;
- : $l:'(int -> 'a) -> 'a = <fun>

これは普通。でも、

# (f 3, 1);; 
- : $l:(int -> 'a) -> 'a * int = <fun>

あれれれ?

# let f ~l:x y = x, y in
  (f 3, 1)
- : (l:'a -> 'a * int) * int = (<fun>, 1)

と違って $l が外に出ているヨ。

$-arg は部分適用できません。部分適用した様に見える式でも、それは適用が省略されたと解釈され、一時変数が適用されます。そしてその変数は外側の let polymorphism (この場合は toplevel)で束縛されます。(ホントはもちょっとややこしいけど、イメージ的に。詳しくは後述):

let f $l:d x = d x in
(f 3, 1)

これは

fun $l:z -> 
  let f $l:x y = x, y in
  (f $l:z 3, 1)

と等価です。なので、$l の付いた arrow 型は内側じゃなくて外側に出てきます。

この自動的な $-app と $-abst のおかげで、省略された $-arg はどんどん外へ外へと abstract されていきます。Dispatch をプログラム変換に使う技術を知っている方にはまさに dispatching 回りのコードを生成しているように見えますよね。

さて、省略された $-arg はどの let でも $-abst される訳ではありません:

let f $l:d x = d x

let h x =
  let g x = f (x,x) in
  g (x,x)

上の h の定義を糞まじめに自動 $-app $-abst していくとこんなコードになります:

let h $l:z x =
  let g $l:z x = f $l:z (x,x) in
  g $l:z (x,x)

$-arg z は f 内部では使われますけど、それ以外は h -> g -> f と何もせずに引き回されるだけです。ちょっと無駄ですね。この無駄は自動的に省けます:

let h $l:z x =
  let g x = f $l:z (x,x) in
  g (x,x)

h から f へと直行です。let polymorphism が省略された $-arg の型を generalize している時だけ、その let に対して $-abst, $-app が自動的に行われる($-dispatch と呼ぶことにしましょうか)ことにしてあります。*1

まぁ、とにかくこんな風に、dispatch 回りを省略可能な引数と関数抽象で実現する。ってのがアイデア。省略可能な引数 ?l はあるのに何でその dual の省略可能な関数抽象てないんかいな、と思いついたのが、きっかけで*2、まぁ無理矢理 dispatch に使ってみたのがはじまり。でも何か屁理屈じゃなくて、使えそう。

*1:この辺りは他にもアイデアがあるので変わるかもしれません。

*2:なんでも dual を考えてみて無かったら研究してみるってよくある手だよね