Type synonmys are like macros at the type level, and GHC is much more liberal
about them than Haskell 98. In particular:
You can write a forall (including overloading)
in a type synonym, thus:
type Discard a = forall b. Show b => a -> b -> (a, String)
f :: Discard a
f x y = (x, show y)
g :: Discard Int -> (Int,Bool) -- A rank-2 type
g f = f Int True |
You can write an unboxed tuple in a type synonym:
type Pr = (# Int, Int #)
h :: Int -> Pr
h x = (# x, x #) |
GHC does validity checking on types after expanding type synonyms
so, for example,
this will be rejected:
type Pr = (# Int, Int #)
h :: Pr -> Int
h x = ... |
because GHC does not allow unboxed tuples on the left of a function arrow.
However, it is often convenient to use these sort of generalised synonyms at the right hand
end of an arrow, thus:
type Discard a = forall b. a -> b -> a
g :: Int -> Discard Int
g x y z = x+y |
Simply expanding the type synonym would give
g :: Int -> (forall b. Int -> b -> Int) |
but GHC "hoists" the
forall to give the isomorphic type
g :: forall b. Int -> Int -> b -> Int |
In general, the rule is this:
to determine the type specified by any explicit
user-written type (e.g. in a type signature), GHC expands type synonyms and then repeatedly
performs the transformation:
type1 -> forall a1..an. context2 => type2
==>
forall a1..an. context2 => type1 -> type2 |
(In fact, GHC tries to retain as much synonym information as possible for use in
error messages, but that is a usability issue.) This rule applies, of course, whether
or not the
forall comes from a synonym. For example, here is another
valid way to write
g's type signature:
g :: Int -> Int -> forall b. b -> Int |