Reputation: 305
Apart from readability and loss of generality, what is wrong with the following definition:
maxPlusOne :: (Ord a, Num a) => a -> a -> a
maxPlusOne = (1+) . max
The compiler will complain that it can't deduce Num (a -> a)
from +
.
But it seems to me what it needs is Num a => a -> a
, which is precisely the type of (1+)
.
Here's the error:
<interactive>:5:52:
Could not deduce (Num (a -> a)) arising from a use of ‘+’
from the context (Ord a, Num a)
Upvotes: 2
Views: 106
Reputation: 477881
I think you interpret the .
too mathematically (as in f
"after" g
). The .
is simply:
(.) :: (b -> c) -> (a -> b) -> a -> c
So its semantical meaning is that it takes one parameter a
, then calls g
on a
, and then call f
on g
. You can define it as:
(.) f g x = f $ g x
Although Haskell extensions are proposed (like this one) that use the type system to derive how much "parameters" to transport, and some have written this in template Haskell, it is not a standard feature.
How can you solve it?
Simply query at least one parameter x
in the head of your function:
maxPlusOne :: (Ord a, Num a) => a -> a -> a
maxPlusOne x = (1+) . max x
or specify it fully:
maxPlusOne :: (Ord a, Num a) => a -> a -> a
maxPlusOne x y = (1+) $ max x y
or use extensions on (.)
.
If you use jaspervdj's extension
maxPlusOne :: (Ord a, Num a) => a -> a -> a
maxPlusOne = (1+) $.$ max
Upvotes: -2
Reputation: 11924
Let's inspect the individual types:
max :: (Ord a) => a -> a -> a
Okay, this takes an a
, and returns a function that takes an a
and returns an a
.
(1 +) :: (Num a) => a -> a
This is simple enough. It takes a number a
and adds one to it.
If we recall that a . b
is equivalent to \q -> a (b q)
we get this from your definition:
(1 +) . max == \q -> 1 + (max q)
Can you see the problem? You're passing a type a -> a
to (1 +)
, because max
is only getting one argument.
This is how to fix it, whilst keeping pointfree style.
maxPlusOne = (.) (1 +) . max
This expands to
maxPlusOne = \q -> (1 +) . max q
which expands to
maxPlusOne = \q1 -> \q2 -> 1 + (max q1 q2)
Which is your desired function.
You could also define another type of composition:
(.:) :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
(.:) f g a b = f (g a b)
and then define
maxPlusOne = (+ 1) .: max
Upvotes: 9