2013-07-19 7 views
9

GHC Generics'i öğrenmeye çalışıyorum. Birkaç örneği inceledikten sonra, genel bir Functor örneği oluşturmaya çalıştım (GHC'nin bunları benim için otomatik olarak türetebileceğini dikkate almayın). Ancak, Generics ile parametreli veri türleri ile nasıl çalışılacağımı bilmediğimi fark ettim, gördüğüm tüm örnekler * türündeydi. Bu mümkün mü ve eğer evet ise, nasıl? (Ayrıca, SYB gibi diğer benzer çerçevelerle de ilgileniyorum.)GHC.Generics (veya diğer benzer çerçeveler) kullanarak genel Functor örnekleri nasıl oluşturulur?

cevap

8

GHC Generics kullanarak çok sayıda örnek işlevi aramak için en iyi yer generic-deriving package'dur. Orada Functor sınıfının genel bir tanımı var. Kopyalama Generics.Deriving.Functor den (biraz basitleştirilmiş):

class GFunctor' f where 
    gmap' :: (a -> b) -> f a -> f b 

instance GFunctor' U1 where 
    gmap' _ U1 = U1 

instance GFunctor' Par1 where 
    gmap' f (Par1 a) = Par1 (f a) 

instance GFunctor' (K1 i c) where 
    gmap' _ (K1 a) = K1 a 

instance (GFunctor f) => GFunctor' (Rec1 f) where 
    gmap' f (Rec1 a) = Rec1 (gmap f a) 

instance (GFunctor' f) => GFunctor' (M1 i c f) where 
    gmap' f (M1 a) = M1 (gmap' f a) 

instance (GFunctor' f, GFunctor' g) => GFunctor' (f :+: g) where 
    gmap' f (L1 a) = L1 (gmap' f a) 
    gmap' f (R1 a) = R1 (gmap' f a) 

instance (GFunctor' f, GFunctor' g) => GFunctor' (f :*: g) where 
    gmap' f (a :*: b) = gmap' f a :*: gmap' f b 

instance (GFunctor f, GFunctor' g) => GFunctor' (f :.: g) where 
    gmap' f (Comp1 x) = Comp1 (gmap (gmap' f) x) 


class GFunctor f where 
    gmap :: (a -> b) -> f a -> f b 
    default gmap :: (Generic1 f, GFunctor' (Rep1 f)) 
       => (a -> b) -> f a -> f b 
    gmap = gmapdefault 

gmapdefault :: (Generic1 f, GFunctor' (Rep1 f)) 
      => (a -> b) -> f a -> f b 
gmapdefault f = to1 . gmap' f . from1 

Eğer Generic1 yerine Generic türetmek zorunda bir veri türü bu kullanın. Generic1 gösteriminin temel farkı, parametre konumlarını kodlayan Par1 veri türünü kullanmasıdır.

3

* -> * türünde veri türleri için Generic1 sınıfı vardır. Onunla çalışmak çoğunlukla * türündeki veri tipleriyle aynıdır, ayrıca parametre için Par1 da vardır. Örneğin, unfoldable package numaramda kullandım.

+0

GHC, "Generic1" örneklerini otomatik olarak türetiyor mu? –

+1

@ PetrPudlák Tam otomatik olarak değil. Ancak, "DeriveGeneric" dil uzantısıyla, "türetme Jenerik" inin yanı sıra "Generic1" türetme işlevini de kullanabilirsiniz (sonuncusu yalnızca en az bir parametreli veri türleri için çalışır; son parametre ise '' 'tür). – kosmikus

+0

@kosmikus Teşekkürler. Ne yazık ki amacım için daha karmaşık türlerle çalışmak istiyorum, muhtemelen Template Haskell'i kullanmak zorundayım. –