Take the type signature of fmap
(the Functor
method) as an example:
(a -> b) -> f a -> f b
How is that different from the following type signature?
a -> b -> f a -> f b
Is there even a difference between those two type signatures?
Yes, there is a difference, because the ->
type constructor is right-associative. In other words,
a -> b -> f a -> f b
is equivalent to
a -> (b -> (f a -> f b))
This type signature denotes a function that takes a parameter of type a
and returns a function, which itself takes a parameter of type b
and returns a function, which itself takes a parameter of type f a
and returns a value of type f b
.
On the other hand,
(a -> b) -> f a -> f b
denotes a function that takes a parameter of type a -> b
(i.e. a function that takes a parameter of type a
and returns a value of type b
) and returns a function, which itself takes a parameter of type f a
and returns a value of type f b
.
Here is a contrived example that illustrates the difference between the two type signatures:
f :: (Int -> Bool) -> [Int] -> [Bool]
f = map
g :: Int -> Bool -> [Int] -> [Bool]
g n b = map (\n' -> (n' == n) == b)
λ> let ns = [42, 13, 42, 17]
λ> f (== 42) ns
[True,False,True,False]
λ> g 42 True ns
[True,False,True,False]
λ> g 42 False ns
[False,True,False,True]