I'm currently having a fight with the typechecker when I try to create a function returning Thing a
(where Thing
is a GADT). A minimal contrived example:
{-#LANGUAGE GADTs, EmptyDataDecls #-}
module Main where
-- Define a contrived GADT
data TFoo
data TBar
data Thing a where
Foo :: Int -> Thing TFoo
Bar :: String -> Thing TBar
combine :: [Thing a]
combine = [Foo 1, Bar "abc"]
main :: IO ()
main = undefined
The typechecker gets unhappy that a
doesn't match TBar
. Presumably this is because it has already inferred that a
is TFoo
. But, this is surprising, because with regular sum types you can do:
data Thing = Foo Int | Bar String
combine :: [Thing]
combine = [Foo 1, Bar "abc"]
Is there anyway to return a type parametric over the GADT parameter?
Outside of the context of the contrived examples, I need GADTs so I can type certain functions to only take a Foo
, but before that I need to also be able to just return a list of Thing
s.
You've got your quantifiers mixed up.
means (the
forall
is implicit in the syntax)Essentially,
combine
needs to be a[Thing a]
no matter whata
is, becausea
is chosen by the code that callscombine
. Or, in another sense,combine
is a function that takes a type as argument and promises to construct a list ofThings
of that type. The only possible definition ofcombine
with this type signature iscombine = []
, plus a bunch of dumb ones like[undefined]
, etc. E.g.combine = [Foo 1]
would not work either, becausea
is not being inferred, because it's notcombine
that setsa
; it's the user.You want
which means "
combine
is a list of things, and each thing is aThing
of some unknown type" (and everyThing
can be of a different type). Theexists
quantifier is the flipside offorall
. It means the definition ofcombine
can set whatever type it wants and the user will have to deal with it. Haskell doesn't supportexists
out-of-the-box, so you need to define an intermediate data type:The syntax is a bit backwards for using the universal quantifier to create existential quantification, but the idea is that you get
which essentially erases the knowledge of what
a
is.You can then have