Вероятно, вам сойдет с рук:
instance Typeable a => Data (My a) where
gfoldl k z (Constr a x1 x2 x3)
= z (Constr a) `k` x1 `k` x2 `k` x3
gunfold k z _ = k . k . k . z $ Constr undefined
toConstr _ = con
dataTypeOf _ = ty
con = mkConstr ty "Constr" [] Prefix
ty = mkDataType "Mod.My" [con]
, что позволяет:
> gmapT (mkT ((+1) :: Int -> Int)) $ Constr ["list","of","strings"] 10 "foo" 18.0
Constr ["list","of","strings"] 11 "foo" 18.0
> gmapT (mkT ((+1) :: Int -> Int)) $ Constr (123 :: Int) 10 "foo" 18.0
Constr 123 11 "foo" 18.0 -- note: 123 passes through
См. Ниже вариант, который можно использовать, если тип нарушения a
не является первый параметр конструктора.
Обратите внимание, что gunfold
практически не имеет значения. См. fromConstr
и fromConstrB
, чтобы узнать, как это используется. Здесь undefined
не хуже того, что уже делает fromConstr
.
Что действительно важно, так это gfoldl
. (Обратите внимание, что даже если это не требуется в минимальном определении, реализация по умолчанию бесполезна для типа данных с внутренней структурой, с которой вы действительно хотите работать, поэтому вы должны определить ее самостоятельно для всех нетривиальных структур данных. ) Здесь я только что написал так, чтобы параметр a
передавался через свертку, позволяя обработать остальное функцией k
.
Если тип a
выглядит как что-то кроме первого параметра конструктора или у вас есть несколько таких типов, которые вы хотите передать нетронутыми, вам понадобится что-то более общее, например:
data My a b = Constr Int a String b Double deriving (Show)
instance (Typeable a, Typeable b) => Data (My a b) where
gfoldl k z (Constr x1 a x2 b x3)
= z go `k` x1 `k` x2 `k` x3
where go y1 y2 y3 = Constr y1 a y2 b y3
gunfold k z _ = k . k . k . z $ go
where go y1 y2 y3 = Constr y1 undefined y2 undefined y3
toConstr _ = con
dataTypeOf _ = ty
con = mkConstr ty "Constr" [] Prefix
ty = mkDataType "Mod.My" [con]
, что дает:
> gmapT (mkT ((+1) :: Int -> Int)) $ Constr 10 (123 :: Int) "thing" [1,2,3] 3.1415
Constr 11 123 "thing" [1,2,3] 3.1415