乱暴に継続渡しっぽいなにか

教科書的に「計算の途中の状態を渡して…」とかいうとすごく理解しづらいものに思えるんだけど、下みたいなgroovyコード(javascriptの場合も)を書くとなんとなく「継続ってなんなの」って聞かれたときに説明しやすいのかなと思ったのでメモ的に書いておくことにします。乱暴極まりないかんじで。

フツーなかんじ

def add = {x,y-> return x+y}
def mul = {x,y-> return x*y}
println mul(3, add(1,2))

ようは「1と2を足し算して3を掛けたい」みたいなことをしたいときに、コード上は自然言語で表現されるものと順序が変わってしまう。これを自然言語に近づけるために(コードの実行順序を自然言語にならうようにするために)、コードのかたちを変えてしまうってのに「継続渡し」って概念がつかえるのよって話。

継続渡しなかんじ

def これらをたし算 = {x,y,c-> c(x+y)}
def これらをかけ算 = {x,y,c-> c(x*y)}
println これらをたし算(1,2){x-> これらをかけ算(x,3){return it}}

ほらね。実行順序が自然言語で表現される順序になったでしょ。

特にクロージャっぽいものが準備されてるような言語だと、そのままクロージャを引数にとって計算途中のオブジェクト(変数が束縛されてる計算途中状態のなにか)を渡せるので理解がしやすい。前置記法の言語だとそれを前置記法でつかえるようにしないといけないので、前置記法に馴染みがないプログラマが見るとすごーく複雑に感じるんじゃないのかなと思った次第。

つかgroovyわかるひとにしてみればごくごく当然というか意識しないでもブリブリつかってまっせという話ですよね。この考え方でwebアプリのフレームワークもつくることも可能だと思う。実際にgaucheフレームワークの概念もそういうことだし。ユースケースに沿って継続渡しなクロージャを書いてって挙動決めてくみたいな。テストもしやすそうだし、なによりおもろそうw


と実験するのに珍幹線でコード書いてたら、groovyってこういうことできるんですね。

def c = {x-> {y-> {z-> (x+y)*z}}}
println c(1)(2)(3)

curry呼ばなくてもいけまっせという話。これっていつのバージョンからこういうことできるようになったんでしょうか…? ちなみに

def c = {x-> {y-> {z-> z(y(x))}}}

とかしておいて次のような書きかたはダメ。

println c(1){it+2}{it*3} // => not work

これならいける。

println c(1)({it+2})({it*3})

自分の仕事はあくまでもパーカッション叩いてデスカルガを演奏することなのであまり詳しく書けないのですが、「継続渡し」という概念をつかうとjavascriptでいろいろできるindesignとかいうソフトがあるらしく、縦や横に伸びたり縮んだりするレイアウトをクールに書けるらしいです。