To be clear, given your definition of Exp
above, the answer is "yes": any function with type signature eval :: Exp a -> a
can only return a (defined) value of type Int
, Bool
, or some other type with a Show
instance. Because any type (of kind *
) can be given a Show
instance, that technically means that eval
can return any type, but within a particular program that has a fixed set of Show
instances, eval
will be limited to returning values from this set of types.
You can see that this must be true as follows. Suppose that eval e
returned a value of some fixed type t
for some expression e
. By the type signature of eval
, that would imply that e
must have type Exp t
. However, data type declarations are "closed" meaning that the set of constructors given in the data Exp a
declaration is exhaustive, and they represent the only methods of constructing a (defined) value of type Exp a
. It's clear from the set of constructors that the only possible values of type Exp a
are those that appear in the rightmost positions of the constructors' signatures: Exp Int
, Exp Bool
, and Exp a
with the constraint Show a
. Therefore, these are the only types possible for e
implying that t
must be Int
, Bool
, or some other a
satisfying the constraint Show a
.
As always in reasoning about Haskell types, we have to be a little bit careful in considering undefined/bottom values. If you consider "returning an undefined value" to be meaningful, then, indeed, eval
can "return" an undefined value of any type, even one without a Show
instance. For example, the following will typecheck:
stupid :: Exp (Int -> Int)
stupid = eval undefined
However, if your reason for asking the question is to determine whether you'd ever be in a position where the expression eval e
might unexpectedly have a type other than Int
, Bool
, or some Show a => a
that you would somehow have to handle, then no. The form of the GADT places limits on the possible types a
in the signature eval :: Exp a -> a
.
Show
covers an infinite set of types. The wording "or a value of any type implementing Show" is also not strictly correct, it should be or any type implementing Show – Willem Van OnsemShow
, more accurately. – AJFeval
, you need a value of typeExp a
. Such value can be created by one of the five constructors, andConst
allows any type implementingShow
to be boxed there. – Bartek BanachewiczExp a -> a
, it is possible if the definition ofExp
changes. Do so would increase the number of values belonging to the typeExp a -> a
. You are basically asking for the union ofimage(f)
for all possible functionsf :: Exp a -> a
. – chepner