I am trying to understand how to reason about the types for partial applied method chaining .
I do not understand why :
:t (+)(+2)
is (a->a)->a->a
or why:
:t (+)(+)
is (a->a->a)->a->a->a
I mean for the first example
I do not understand , when looking at the (+)
do i need to look at what it needs a->a->a
or what method is before it (+2)
(which needs an a
).
At the second example i know the first (+)
needs a->a->a
, but once i fullfill the first method why does the second one need again the same parameters?
You've actually slightly misreported the type. There are some all-important typeclass constraints in there:
(+)(+2) :: (Num a, Num (a -> a)) => (a -> a) -> a -> a
So where does this come from? It's actually quite simple. Firstly, the type signature of (+)
is
(+) :: Num a => a -> a -> a
Or, rewriting to make the currying explicit:
(+) :: Num a => a -> (a -> a)
Meanwhile, the type of (+2)
(which is the result of just doing exactly that partial application) is:
(+2) :: Num a => a -> a
Now when you do (+)(+2)
, what you are doing is (partially) applying the (+)
function to the function (+2)
. That is, we're treating (+2)
as the first argument of (+)
. In order for this to work, its type - which is Num a => a -> a
- must be an instance of Num
. So this is why we have a further type contraint, that a -> a
must be an instance of Num
. (This is never the case by default, but you can define your own instance for numerical functions - typically it would apply both functions to the input and add the results.)
So let's specialise the type signature of (+)
to the case where it's applied to a function (a -> a)
(which as I've just said must then itself be an instance of Num
, as well as a
itself). We get:
(+) :: (Num a, Num (a -> a)) => (a -> a) -> (a -> a) -> (a -> a)
or with currying made explicit:
(+) :: (Num a, Num (a -> a)) => (a -> a) -> ((a -> a) -> (a -> a))
That is, it takes an a -> a
function and returns a "higher-order function" of type (a -> a) -> (a -> a)
. So that when we apply this to (+2)
we get exactly such a higher-order function:
(+)(+2) :: (Num a, Num (a -> a)) => (a -> a) -> (a -> a)
This is exactly what is reported, since the final pair of parentheses is unnecessary. (This is due to currying again.)
The second case is entirely analagous, except that the function you're applying to is a -> a -> a
rather than a -> a
.