So Data.Map has dataCast2
defined, which makes sense, as it has an arity 2 type constructor. dataCast1
defaults to const Nothing
. dataCast2
is easily defined as gcast2
.
For reference:
class Typeable a => Data a where
dataCast1 :: Typeable1 t => (forall d. Data d => c (t d)) -> Maybe (c a)
dataCast2 :: Typeable2 t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a)
...
gcast1 :: (Typeable1 t, Typeable1 t') => c (t a) -> Maybe (c (t' a))
gcast2 :: (Typeable2 t, Typeable2 t') => c (t a b) -> Maybe (c (t' a b))
The question at hand is this: given everything in Data.Data
, Data.Typeable
, etc., and given an arity 2 type constructor for which dataCast2
is defined (say, Map
, or (,)
), is it possible to write a version of dataCast1
that does the right thing for a partial specialization of this type constructor, either for one specific constructor at a time, or in general?
Intuitively, I think there should be a good solution, but my first few tries crashed and burned.
I'm not sure if this is what you want, but it might steer you in the right direction if it's not. It is written in a very similar style to the gcast
, gcast1
, and gcast2
functions in the Data.Typeable library. For more details, "read the source, Luke".
myDataCast1 :: forall c t d e a.(Typeable d, Typeable e) => c (t d a) -> Maybe (c (t e a))
myDataCast1 x = r
where
r = case typeOf (getArg x) == typeOf (getArg (fromJust r)) of
True -> Just $ unsafeCoerce x
False -> Nothing
getArg :: c (t x a) -> x
getArg = undefined
Using this function you can, for instance write foo
foo :: Typeable d => c (d, a) -> Maybe (c (Int, a))
foo = myDataCast1
According to this paper the way to implement dataCast1 is either as
dataCast1 f = gcast1 f -- for unuary type constructors
or
dataCast1 f = Nothing -- for non-unary type constructors
They haven't said that this is the only way to implement it, but it might be the case. Perhaps it's worth asking the authors? I think the core problem is that we can't partially apply type constructors. eg the following is not possible
data T a b = ...
instance Typeable a => Data (T Int) where
dataCast1 f = ...
GHC will complain that T has not been applied to enough type arguments.
I can think of a work-around though. We define a newtype
newtype PairInt a = PairInt (Int, a) deriving Typeable
instance (Typeable a, Data a) => Data (PairInt a) where
dataCast1 f = gcast1 f
It's pretty annoying but it does the job. But perhaps it doesn't fit with what you're trying to achieve.
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.