I have following datatype:
data My a = Constr a Int String Float VariousComplexTypes ...
I want to be able to call over template (+1)
on it to change all Int
fields in this data type and down the road. However, I can't request a
to be an instance of Data
(its constructors are hidden). At the same time, I don't really interested in changing it with over template
call, so I tried to write an instance Data (My a)
manually, making it ignore first field of type a
, but failed. The following code
instance Data (My a) where
gunfold k z _ = k (k (k (z Constr)))
toConstr _ = con
dataTypeOf _ = ty
con = mkConstr ty "Constr" [] Prefix
ty = mkDataType "Mod.My" [con]
produces following error:
• No instance for (Data a) arising from a use of ‘k’
• In the first argument of ‘k’, namely ‘(k (z Constr))’
In the first argument of ‘k’, namely ‘(k (k (z Constr)))’
In the expression: k (k (k (z Constr)))
|
61 | gunfold k z _ = k (k (k (z Constr)))
Is this possible at all? What other options do I have, except writing all the boilerplate by hand?
You can probably get away with:
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]
which allows for:
> 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
See below for a variation to use if the offending type a
isn't the first constructor parameter.
Note that gunfold
barely matters. See fromConstr
and fromConstrB
for how it's used. Here, the undefined
is no worse than what fromConstr
already does.
What really matters is gfoldl
. (Note that even though it's not required in the minimal definition, the default implementation is useless for a data type with internal structure that you actually want to operate on, so you're supposed to define it yourself for all non-trivial data structures.) Here, I've just written it so that the a
parameter is passed through the fold, allowing the rest to be processed by the k
function.
If the a
type appears as something other than the first constructor parameter or you have multiple such types that you want to pass through untouched, you'll need something more general, like:
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]
giving:
> gmapT (mkT ((+1) :: Int -> Int)) $ Constr 10 (123 :: Int) "thing" [1,2,3] 3.1415
Constr 11 123 "thing" [1,2,3] 3.1415
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.