GHC Generics를 배우려고합니다. 몇 가지 예를 검토 한 후에 일반 Functor
인스턴스를 만들려고했습니다 (GHC가 나를 위해 자동으로 파생되지 않는다는 것을 무시하고). 그러나 Generics에서 매개 변수화 된 데이터 유형을 사용하는 방법을 알지 못한다는 것을 깨달았습니다. 모든 예제는 친절한 *
입니다. 가능 한가요? 그렇다면 어떻게 할 수 있습니까? SYB와 같은 다른 유사한 프레임 워크에도 관심이 있습니다.GHC.Generics (또는 다른 유사한 프레임 워크)를 사용하여 일반적인 Functor 인스턴스를 생성하는 방법은 무엇입니까?
9
A
답변
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에서 사용했습니다.
GHC는 자동적으로'Generic1'의 인스턴스를 파생합니까? –
@ PetrPudlák 자동으로 충분하지 않습니다. 그러나'DeriveGeneric' 언어 확장을 사용하면'Deriving Generic'과'Generic1 파생 '을 사용할 수 있습니다 (후자는 적어도 하나의 매개 변수가있는 데이터 유형에만 사용되며 마지막 매개 변수는 종류가 *). – kosmikus
@kosmikus 감사합니다. 불행히도 내 목표를 위해 좀 더 복잡한 종류로 작업하고 싶다. 아마도 Template Haskell을 사용해야 할 것이다. –