Let us consider the following code snippet:
blah :: a -> b -> a
blah x y = ble x where
ble :: b -> b
ble x = x
This compiles fine under GHC, which essentially means that b
from the 3rd line is something different than b
from the first line.
My question is simple: is there a way to somehow relate in the type declaration of ble
to a type used in an outer context, i.e. the type declaration of blah
?
Obviously, this is just an example and not a real-world use-case for type declarations.
This is possible with the ScopedTypeVariables extension. You need to use explicit forall's to bring the type variables into scope.
blah :: forall a b. a -> b -> a
blah x y = ble x where
ble :: b -> b
ble x = x
Trying to load this definition with ScopedTypeVariables enabled gives:
foo.hs:2:16:
Couldn't match type `a' with `b'
`a' is a rigid type variable bound by
the type signature for blah :: a -> b -> a at foo.hs:2:1
`b' is a rigid type variable bound by
the type signature for blah :: a -> b -> a at foo.hs:2:1
In the first argument of `ble', namely `x'
In the expression: ble x
In an equation for `blah':
blah x y
= ble x
where
ble :: b -> b
ble x = x
You can tell that GHC interprets the two b
s as the same type because the error says that a
and b
are bound on the same line.
If you don't want to use ScopedTypeVariables, you can use the good ole fashion asTypeOf
function.
-- defined in Prelude
asTypeOf :: a -> a -> a
x `asTypeOf` y = x
blah :: a -> b -> a
blah x y = ble x where
ble x = x `asTypeOf` y
Of course, this won't compile because of the type error.
Update:
I would like to point out that sometimes you might have to be a little crafty to do what you want with asTypeOf
. Take the following example that superflously uses asTypeOf
because I don't want to think of a case that actually needs asTypeOf
. Similar solutions would work the same for real world cases.
foo :: Bounded a => Maybe a -> a
foo m = x
where
x = maxBound -- Q: how do I make (x :: a) when given (Maybe a)?
_ = Just x `asTypeof` m -- A: witchcraft!