Concurrent Clean : Generic

さて、昨日はGenericを紹介するだけだったのですが、もう少しその使い方について触れてみようと思います。

gDeepSeq{|c|} ...

という形で宣言するのですが、この'c'のところには、型または型構成子を取ることができます。

::A a = A a

generic gSize a :: a -> Int
gSize{|Int|}    a     = 1
gSize{|A|}   f  (A a) = 1 + f a

sizeA :== gSize{|*|}

Start = [sizeA 1
        ,sizeA (A 1)
        ,sizeA (A (A 1))
        ,sizeA (A (A (A 1)))
        ]

のように書くと、

[1,2,3,4]

のような結果が得られます。ちなみに、この例では、同じことを型クラスを使って次のように書くこともできます。

::A a = A a

class    gSize a                where gSize :: a -> Int
instance gSize Int              where gSize a     = a
instance gSize (A a) | gSize a  where gSize (A a) = 1 + gSize a

sizeA :== gSize

Start = [sizeA 1
        ,sizeA (A 1)
        ,sizeA (A (A 1))
        ,sizeA (A (A (A 1)))
        ]

さて、Genericの便利なところは、このように型や型構成子をいちいち書き下さなくても、もっと汎用的な記述だけを書くことで具体型を派生させることができるところです。

generic gSize a :: a -> Int
gSize{|Int|}          a            = 1
gSize{|OBJECT|} f     (OBJECT a)   = f a
gSize{|CONS|}   f     (CONS a)     = 1 + f a
gSize{|PAIR|}   f1 f2 (PAIR a1 a2) = f1 a1 + f2 a2

sizeA :== gSize{|*|}

まず、上のように汎用的に記述を書き、

::A a = A a
derive gSize A

Start = [sizeA 1
        ,sizeA (A 1)
        ,sizeA (A (A 1))
        ,sizeA (A (A (A 1)))
        ]

続いて、型構成子 A に対して派生してやることで、gSize{|*|}を利用できるようになります。
これは、Genericな関数を適用する際に、型構成子を次のような汎用的な構造に暗黙的に変換してやることで実現しています。

A a :== OBJECT (CONS a)

このような仕組みがあることで、次のような新たな型 B を作った際にも、Bに対して派生してやるだけで対応することができます。

::B b1 b2 = B b1 b2
derive gSize B

Start = [sizeA 1
        ,sizeA (A (A 1))
        ,sizeA (B (A 1) (B 2 (A 3)))
        ]

この際、Bは次のように変換されます。

B b1 b2 :== OBJECT (CONS (PAIR b1 b2))