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?
9
A
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.
Liefert GHC Instanzen von 'Generic1' automatisch? –
@ 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
@kosmikus Danke. Leider möchte ich für mein Ziel mit komplexeren Arten arbeiten, also werde ich wahrscheinlich Template Haskell verwenden müssen. –