2013-07-19 3 views
9

Я пытаюсь узнать GHC Generics. Просмотрев несколько примеров, я хотел попытаться создать общий экземпляр Functor (без учета того, что GHC может автоматически их получить для меня). Однако я понял, что понятия не имею, как работать с параметризованными типами данных с помощью Generics, все примеры, которые я видел, были типа *. Возможно ли это, и если да, то как? (Меня также интересуют другие аналогичные структуры, такие как SYB.)Как построить общие экземпляры Functor с использованием GHC.Generics (или других подобных фреймворков)?

ответ

8

Лучшее место для поиска множества примеров с использованием GHC Generics - это generic-deriving package. Существует общее определение класса 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, который кодирует позиции параметров.

3

Для типов данных * -> * существует класс Generic1. Работа с ним в основном такая же, как с типами данных вида *, за исключением того, что для параметра есть Par1. Например, я использовал его в своем unfoldable package.

+0

Получает ли GHC экземпляры «Generic1» автоматически? –

+1

@ PetrPudlák Не полностью автоматически. Но с расширением языка DeriveGeneric вы можете использовать 'получение Generic', а также' получение Generic1' (где последний работает только для типов данных с хотя бы одним параметром, последний параметр которого имеет вид '*'). – kosmikus

+0

@kosmikus Спасибо. К сожалению, для моей цели я хотел бы работать с более сложными видами, поэтому, вероятно, мне придется использовать Template Haskell. –