J'essaie d'apprendre les génériques de GHC. Après avoir passé en revue plusieurs exemples, je voulais essayer de créer un générique Functor
(sans tenir compte du fait que GHC peut les dériver automatiquement pour moi). Cependant, je me suis rendu compte que je n'ai aucune idée de la façon de travailler avec des types de données paramétrés avec Generics, tous les exemples que j'ai vus étaient du type *
. Est-ce possible, et si oui, comment ? (Je suis également intéressé par d'autres cadres similaires, tels que SYB).
Réponses
Trop de publicités?Le meilleur endroit pour trouver de nombreux exemples de fonctions utilisant les génériques GHC est la page generic-deriving
paquet . Il y a une définition générique de la Functor
classe là-dedans. Copie (légèrement simplifiée) de 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
Pour l'utiliser sur un type de données, vous devez dériver l'option Generic1
plutôt que Generic
. La différence essentielle de l Generic1
est qu'elle fait appel à la Par1
qui encode les positions des paramètres.
Il existe un Generic1
pour les types de données de type * -> *
. Le travail avec ce type de données est essentiellement le même qu'avec les types de données de type *
sauf qu'il y a aussi Par1
pour le paramètre. Je l'ai utilisé dans mon paquet dépliable par exemple.