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自体がよく分かりません ><