Can we transform a GADT without a given constraint on its constructors to a GADT that does have the said constraint? I want to do this because I want to get a deep-embedding of Arrows and do some interesting things with the representation that (for now) seem to require Typeable
. (One reason)
data DSL a b where
Id :: DSL a a
Comp :: DSL b c -> DSL a b -> DSL a c
-- Other constructors for Arrow(Loop,Apply,etc)
data DSL2 a b where
Id2 :: (Typeable a, Typeable b) => DSL2 a a
Comp2 :: (Typeable a, Typeable b, Typeable c) => DSL2 b c -> DSL2 a b -> DSL2 a c
-- ...
We could try the following from
function but that breaks quickly as we don't have the
Typeable
information for the recursive point
from :: (Typeable a, Typeable b) => DSL a b -> DSL2 a b
from (Id) = Id2
from (Comp g f) = Comp2 (from g) (from f)
So instead we try to capture the transformation in a type class. However, that will break as well as we will be missing the Typeable b
information as b
is an existential.
class From a b where
from :: a -> b
instance (Typeable a, Typeable b) => From (DSL a b) (DSL2 a b) where
from (Id) = Id2
from (Comp g f) = Comp2 (from g) (from f)
Any other suggestions? Ultimately I want to create a deep-embedding of Category
and Arrow
together with Typeable
information on the type parameters. This is so I can use the arrow-syntax to construct a value in my DSL and have fairly standard Haskell code. Maybe I have to resort to Template Haskell?
Perhaps you should resort to
{-# LANGUAGE RebindableSyntax #-}
(while noting that it impliesNoImplicitPrelude
). Create your ownarr
,(>>>)
,first
,app
,(|||)
andloop
functions and GHC will use them when desugaring arrow notation, instead of the standard versions from Control.Arrow.The manual states that “the types of these functions must match the Prelude types very closely”. If you're building a parallel class hierarchy (a copy of the hierarchy in Control.Arrow but with
Typeable
constraints added to the type signatures) you should be fine.(n.b. I am not familiar with arrow notation so have never used it in conjunction with RebindableSyntax; my answer is an intelligent guess.)
The problem with the recursive case is fatal to transforming
DSL a b
intoDSL2 a b
.To do this transformation would require finding the
Typeable
dictionary for the existential typeb
in theComp
case - but whatb
actually is has already been forgotten.For example consider the following program:
In other words if there was a way to do the conversion in general, you could somehow invent a
Typeable
instance for a type that doesn't actually have one.