I'm learning about existential quantification, phantom types, and GADTs at the moment. How do I go about creating a heterogeneous list of a data type with a phantom variable? For example:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ExistentialQuantification #-}
data Toy a where
TBool :: Bool -> Toy Bool
TInt :: Int -> Toy Int
instance Show (Toy a) where
show (TBool b) = "TBool " ++ show b
show (TInt i) = "TInt " ++ show i
bools :: [Toy Bool]
bools = [TBool False, TBool True]
ints :: [Toy Int]
ints = map TInt [0..9]
Having functions like below are OK:
isBool :: Toy a -> Bool
isBool (TBool _) = True
isBool (TInt _) = False
addOne :: Toy Int -> Toy Int
addOne (TInt a) = TInt $ a + 1
However, I would like to be able to declare a heterogeneous list like so:
zeros :: [Toy a]
zeros = [TBool False, TInt 0]
I tried using an empty type class to restrict the type on a
by:
class Unify a
instance Unify Bool
instance Unify Int
zeros :: Unify a => [Toy a]
zeros = [TBool False, TInt 0]
But the above would fail to compile. I was able to use existential quantification to do get the following:
data T = forall a. (Forget a, Show a) => T a
instance Show T where
show (T a) = show a
class (Show a) => Forget a
instance Forget (Toy a)
instance Forget T
zeros :: [T]
zeros = [T (TBool False), T (TInt 0)]
But this way, I cannot apply a function that was based on the specific type of a
in Toy a
to T
e.g. addOne
above.
In conclusion, what are some ways I can create a heterogeneous list without forgetting/losing the phantom variable?
data Toy = TBool Bool | TInt Int
;isBool (TBool _) = True; isBool _ = False
,addOne (TInt a) = TInt $ a + 1
;zeros = [Toy]; zeros = [TBool False, TInt 0]
. Or do you really need that polymorphism there and your actual use case is more complex? – bheklilrdata Sequence = Sequence (Toy Int)
where it doesnot apply toToy Bool
, and vice versa. And also, in your reply, wouldn't your definition ofaddOne
not total? .i.e. CallingaddOne
onTBool b
would cause an error? – gspindlesaddOne (TInt a) = TInt $ a + 1; addOne x = x
. – bheklilrnewtype TInt = TInt Int
,TBool
similarly anddata Toy = ToyInt TInt | ToyBool TBool
. So you can haveSequence TInt
. I wouldn't use GADTs before they are really necessary, e.g. having constructor likeEqToy :: Toy a -> Toy a -> Toy Bool
orMultiToy :: Toy a -> Toy [a]
– phadejnewtype
instead ofdata
). But untimately, I wanted to work withToy
as the bottom most data where everything else operarates on. Hence the need for the phantom and going out of my way to make it work in higher up composite data/function. I.e. in theSequence
data in reply to @bleklilr, I don't wantSequence
to takeTInt
in your example, I want it to take aToy
. – gspindles