2013-07-19 7 views
9

Ich versuche GHC Generics zu lernen. Nach der Überprüfung einiger Beispiele wollte ich versuchen, generische Functor Instanzen zu erstellen (abgesehen davon, dass GHC sie automatisch für mich ableiten kann). Wie auch immer, ich merkte, dass ich keine Ahnung hatte, wie ich mit Generics mit parametrisierten Datentypen arbeiten sollte, alle Beispiele, die ich gesehen habe, waren von der Art *. Ist das möglich, und wenn ja, wie? (Ich bin auch an anderen ähnlichen Frameworks wie SYB interessiert.)Wie generische Functor-Instanzen mit GHC.Generics (oder anderen ähnlichen Frameworks) erstellt werden?

Antwort

8

Der beste Ort für viele Beispielfunktionen mit GHC Generics ist der generic-deriving package. Es gibt eine generische Definition der Klasse Functor darin. Kopieren (etwas vereinfacht) von Generics.Deriving.Functor:

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 

Um dies zu auf einem Datentyp zu verwenden, haben Sie Generic1 anstatt Generic abzuleiten. Der Hauptunterschied der Generic1-Darstellung besteht darin, dass sie den Par1-Datentyp verwendet, der die Parameterpositionen codiert.

3

Es gibt eine Generic1 Klasse für Datentypen der Art * -> *. Mit ihm zu arbeiten ist meistens dasselbe wie mit Datentypen der Art *, außer dass es auch Par1 für den Parameter gibt. Ich habe es zum Beispiel in meinem unfoldable package verwendet.

+0

Liefert GHC Instanzen von 'Generic1' automatisch? –

+1

@ PetrPudlák Nicht vollständig automatisch. Aber mit der 'DerivativeGeneric'-Spracherweiterung können Sie' generisches Generieren' sowie 'Generisches1herleiten' verwenden (wobei letzteres nur für Datentypen mit mindestens einem Parameter funktioniert, wobei der letzte Parameter vom Typ' * 'ist). – kosmikus

+0

@kosmikus Danke. Leider möchte ich für mein Ziel mit komplexeren Arten arbeiten, also werde ich wahrscheinlich Template Haskell verwenden müssen. –