As part of my Haskell learning process, I like to explicitly type out the type declarations for functions. I would like to be able to do so for functions defined in a where clause, but I don't know how to specify, that a type variable in a where clause should denote the same type as some type variable in the outer type declaration. For instance, the following code:
foo :: (a -> a) -> a -> a
foo f arg = bar arg
where
bar :: a -> a
bar a = f a
yields this error:
src\Test.hs:7:14:
Couldn't match expected type `a' against inferred type `a1'
`a' is a rigid type variable bound by
the type signature for `foo' at src\Test.hs:3:8
`a1' is a rigid type variable bound by
the type signature for `bar' at src\Test.hs:6:11
In the first argument of `f', namely `a'
In the expression: f a
In the definition of `bar': bar a = f a
How can I express that the first argument to bar should be of the same type as the second argument to foo, so that I can apply f to it?
Thanks.
I think you can do this in general with ScopedTypeVariables which GHC supports. This certainly compiles:
{-# LANGUAGE ScopedTypeVariables #-}
foo :: forall a. (a -> a) -> a -> a
foo f arg = bar arg
where
bar :: a -> a
bar a = f a
Note the "forall a."
There is another workaround. Instead of referencing f
within the inner function bar
, extend bar
to accept f
as a first argument and use partial application in the parent.
foo :: (a -> a) -> a -> a
foo f arg = (bar f) arg
where
bar :: (a -> a) -> a -> a
bar f a = f a
It does not require ScopedTypeVariables or explicit type checking code as the other answers.
For clarity let's change the type parameter in bar
to b
and also rename its argument.
foo :: (a -> a) -> a -> a
foo f arg = bar arg
where
bar :: b -> b
bar x = f x
Haskell complains because bar
is annotated as b -> b
(for any arbitrary type b
), but f x
is attempting to apply an argument of type b
to a function of type a -> a
(for a specific, bound a
). In other words, the inner function is not as general as its type annotation advertises.
Passing f
to bar
means that for the expression (bar f)
, the type variable b
is bound to the same type as a
.
And finally, without changing anything else, if you're willing to omit the type signature for the inner function bar
, Haskell will infer its type exactly the way you want. That is, since bar
applies f
from from the parent function foo
, the type of bar
will reuse the type parameter a
from the type of foo
.
foo :: (a -> a) -> a -> a
foo f arg = bar arg
where
-- Type: bar :: a -> a
bar a = f a
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With