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