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 tr开发者_JS百科ies 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. e.g. 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.
精彩评论