2013-07-19 6 views
9

GHC Generics를 배우려고합니다. 몇 가지 예를 검토 한 후에 일반 Functor 인스턴스를 만들려고했습니다 (GHC가 나를 위해 자동으로 파생되지 않는다는 것을 무시하고). 그러나 Generics에서 매개 변수화 된 데이터 유형을 사용하는 방법을 알지 못한다는 것을 깨달았습니다. 모든 예제는 친절한 *입니다. 가능 한가요? 그렇다면 어떻게 할 수 있습니까? SYB와 같은 다른 유사한 프레임 워크에도 관심이 있습니다.GHC.Generics (또는 다른 유사한 프레임 워크)를 사용하여 일반적인 Functor 인스턴스를 생성하는 방법은 무엇입니까?

답변

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' 언어 확장을 사용하면'Deriving Generic'과'Generic1 파생 '을 사용할 수 있습니다 (후자는 적어도 하나의 매개 변수가있는 데이터 유형에만 사용되며 마지막 매개 변수는 종류가 *). – kosmikus

+0

@kosmikus 감사합니다. 불행히도 내 목표를 위해 좀 더 복잡한 종류로 작업하고 싶다. 아마도 Template Haskell을 사용해야 할 것이다. –