Life Goes On

まあまあだけど楽しんでる方です

カリー化、部分適用、その他の話題

前回のエントリに対して、m-hiyama さんから、色々と指摘をいただきました
以下に訂正を、と思ったら、先に bonotake さんに書かれてしまった
そもそも問題はというと、「ラムダ抽象=カリー化」という前提を僕が理解していなかったのが、いけなかったのだと思います。
型をきちんと考えてみると、g :: (Int, Int, Int) -> Int、g^ :: (Int, Int) -> (Int -> Int)、g^^ :: Int -> (Int -> Int -> Int)、g^^^ :: Int -> Int -> Int -> Int ですね。
それを踏まえて前回のコードを修正すると、↓のようになります。
ただ、いずれにしても小さいラムダ式と大きいラムダ式を区別していないという時点で、この考え方はNGです。g'' (g^^ = < a | λb.λx.a*x+b >) と g''' (g^^^ = < | λa.λb.λx.a*x+b >) が区別できなくなってしまいます。
「大きいラムダ式(関数)に引数を与える」という行為と、「小さいラムダ式(関数コード)と引数を実行エンジンが評価する」という行為の意味の違いが、セミナーでじっくり説明されていたことなので、それをごちゃ混ぜにしてしまうのは、ちょっと頂けない。
[追記] 解決策を考えてみました

g :: (Int, Int, Int) -> Int
g (a,b,x) = a * x + b

cookleCurry :: ((a, b, c) -> d) -> (a, b) -> c -> d
cookleCurry f (a,b) c = f (a,b,c)

g' :: (Int, Int) -> (Int -> Int)
g' = cookleCurry g

bonCurry :: ((a, b) -> c) -> a -> b -> c
bonCurry = curry

g'' :: Int -> (Int -> Int -> Int)
g'' = bonCurry g'

aCurry :: (a -> b) -> a -> b
aCurry = id

g''' :: Int -> Int -> Int -> Int
g''' = aCurry g''
*Main> g (3,5,7)
26
*Main> (g' (3,5)) 7
26
*Main> (g'' 3) 5 7
26
*Main> g''' 3 5 7
26

もう一つ、カリー化したときの g^^^ の絵の複雑さと、point-free スタイルで記述したときのコードの複雑さって対応してるんじゃないか、ということを前回書きましたが、これも間違い。
そもそも point-free スタイル*1で記述して複雑になるのは、引数の順序と演算の順序が一致していないからであって、カリー化とは関係ありません。
たとえば g a b x = a*x+b は g = (. (+)) . flip (.) . (*) = B(B(B(+))(CB))(*) ですが、f x a b = x*a+b とすれば、f = ( (+) .) . (*) = B(B(+))(*)で*2、そんなにややこしくない‥‥ややこしくないと思う‥‥ややこしくないんじゃないかな。
それはともかく、色々間違っていたところを、まずは訂正させてください。
さらに間違いを増殖させていたら、ごめんなさい。

*1:そんなにロクデモナイ用語でしょうか

*2:B や C という記法はこの辺に従ってます。Combinator Birds