简体   繁体   中英

Data.Data — producing dataCast1 for an arity 2 type constructor (partially specialized)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM