Как создать общие экземпляры Functor, используя GHC.Generics (или другие подобные структуры)?
Я пытаюсь изучить GHC Generics. Изучив несколько примеров, я хотел попытаться создать общий Functor
случаи (не учитывая, что GHC может получить их автоматически для меня). Однако я понял, что понятия не имею, как работать с параметризованными типами данных с помощью Generics, все примеры, которые я видел, были *
, Возможно ли это, и если да, то как? (Я также интересуюсь другими подобными структурами, такими как SYB.)
2 ответа
Лучшее место для поиска множества примеров функций с использованием GHC Generics - это generic-deriving
пакет Есть общее определение Functor
класс там. Копирование (слегка упрощенное) из 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
Чтобы использовать это для типа данных, вы должны получить Generic1
скорее, чем Generic
, Ключевое отличие Generic1
представление о том, что он использует Par1
тип данных, который кодирует позиции параметров.
E сть Generic1
класс для типов данных вида * -> *
, Работа с ним в основном такая же, как с типами данных *
кроме того, что есть также Par1
для параметра. Я использовал это в моей несложной упаковке, например.