9 votes

Comment construire des instances de foncteurs génériques en utilisant GHC.Generics (ou d'autres frameworks similaires) ?

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).

9voto

kosmikus Points 11669

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.

3voto

Sjoerd Visscher Points 8310

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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X