genericOnを作ろうとして失敗した
Haskell には Data.Function に on という関数が用意されている.
C++ にも on が欲しい - EAGLE 雑記
を見てonという関数を知りました.定義は簡単なのでCleanでも普通に書けます.
on f g = \x y = f (g x) (g y)
まんまですね.
gOnの実装
これを見て,引数の数を一般化した関数が書けないかなと思って挑戦しました.
つまり以下のような感じ.
gOn f g x0 = f (g x0) gOn f g x0 x1 = f (g x0) (g x1) gOn f g x0 x1 x2 = f (g x0) (g x1) (g x2) ... gOn f g x0 x1 ... xN = f (g x0) (g x1) ... (g xN)
今見るとムチャな気がしてくるわけですが…,genericsを使ってやってみました.
generic gOn a :: (a->b) (?->?) a -> b
いきなり型の定義でつまずいた \(^o^)/
一応説明しておくと下のような意味です.
generic gOn a :: // 一般化した型aを含む (a->b) // (一般化した)aを受け取ってbにする関数が第一引数 (?->?) // 引数それぞれに適用する関数 -> b // 最終的には第一引数の関数が適用された結果になる
gApplyの実装
どうやらgOnは難しそうだったので, とりあえず可変長引数を取るapplyを書いてみることにしました.
generic gApply a :: (a->b) a -> b gApply{|Int|} f arg = f arg gApply{|Char|} f arg = f arg gApply{|Real|} f arg = f arg gApply{|UNIT|} f arg = f arg gApply{|String|} f arg = f arg gApply{|PAIR|} fa fr f (PAIR x y) = f (PAIR x y) gApply{|EITHER|} fa fr f (LEFT x) = f (LEFT x) gApply{|EITHER|} fa fr f (RIGHT x) = f (RIGHT x) gApply{|OBJECT|} fa f arg = f arg gApply{|(->)|} fa fr f arg = f arg gApply{|CONS of {gcd_name, gcd_arity, gcd_fields}|} fa f (CONS x) = f (CONS x) derive gApply [], (,), (,,), (,,), (,,,) Start = ( gApply{|*|} (\x=x*2) 1 , gApply{|*|} (\x y z=x+y+z+2) 1 2 3 , gApply{|*|} (\x y = x+y+3.5) 0.1 0.2 , gApply{|*|} (map \x=x*2) [1..10] , gApply{|*|} (\(x,y)=(y,x)) (1,2) , (gApply{|*|} (o) (\x=x*2) (\x=x+2)) 5 ) > (2,8,3,8,[2,4,6,8,10,12,14,16,18,20],14)
ちょっと見た目が冗長だけど,それっぽく出来てますね.
ここまで実装した感覚からすると,「個々の引数に言及するのはgenericsじゃ無理っぽいなー」という感じ.
ただこれ(gApply)は動いちゃっていいのだろうか?
自分で書いといていまいち納得できない.
kindを指定したgeneric関数は普通の関数かと思ってたけど,上を見るとどうやら違うみたいだし….
結論
generics自体がよく分かりません ><