My question is very simple, as anyone beginning haskell I've been thinking about types, function composition and how to apply them. I started thinking about what the result of ((+) . (*))
might be.
Now obviously the solution to that question is open ghci and find out. So I did that and inspected the type:
λ> :t ((*) . (+))
((*) . (+)) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
Is this type possible? I'm struggling to understand what it might be or what it means?
apologies again for the simplistic question, everything I tried to put into the function failed. I'm simply trying to develop intuition for function composition with dyadic functions.
Unfortunately, GHC doesn't give a very good message for this. This is almost certainly not what you want. There is no instance for Num (a -> a)
by default and implementing one, though it could be useful for some things, could lead to very unintuitive runtime errors. Even in this case, this function would not likely be very useful.
Let's look at type restricted (*)
and (+)
to simplify the situations and avoid the added complexity of the type class:
(*!) :: Int -> Int -> Int
(*!) = (*)
(+!) :: Int -> Int -> Int
(+!) = (+)
Now when we try
λ> :t (*!) . (+!)
<interactive>:1:8:
Couldn't match type ‘Int -> Int’ with ‘Int’
Expected type: Int -> Int
Actual type: Int -> Int -> Int
Probable cause: ‘(+!)’ is applied to too few arguments
In the second argument of ‘(.)’, namely ‘(+!)’
In the expression: (*!) . (+!)
which indicates that we aren't applying (+!)
to enough arguments to be able to apply its result to (*!)
. If we expand the function composition we see this, which might make it more clear why it doesn't make sense:
(*!) . (+!) == \x -> (*!) ((+!) x) -- definition of (.)
== \x y -> (*!) ((+!) x) y -- eta-expansion
== \x y -> ((+!) x) *! y -- changed (*.) from prefix to infix
The left argument of (*!)
is a function, which doesn't match the expected type of Int
.
In order to do this, we need a function (b -> c) -> (a1 -> a2 -> b) -> a1 -> a2 -> c
. Luckily, that's exactly what ((.) . (.))
is.
λ> :t ((.) . (.)) (*!) (+!)
((.) . (.)) (*!) (+!) :: Int -> Int -> Int -> Int
Some libraries provide this under the name (.:)
(like this one). Sometimes people like to write it fmap . fmap
(this generalizes to more than just normal function composition though).
It's a bit cryptic though and is usually avoided for that reason. It's almost always more clear to just explicitly write out the function.
that's a fun question....
First of all, I'll explain why you get the type you do.
Both (+) and (*) have type
Num a=>a->a->a
which basically means that they have two numbers as input, and output one number (as should be expected from add and multiply)
Function composition chains two functions of type (a->b) together (where, of course, the output of the first needs to be the same type as the input to the next).
(.)::(b->c)->(a->b)->a->c
So, at first glance, neither (+) or (*) seem to be of that type.... Except that in the world of Haskell, you can think of them as type
(+)::Num a=>a->(a->a)
which makes sense.... If you fill in one value of (+), you get a function that increments the value of a number by that amount, for instance
(+) 1 --evaluates to incrementByOne
where incrementByOne x = 1+x
So, you can chain the two together, but.....
Remember, the inputs of (*) need to be numbers! (because of the Num a=>
)
Applying (.) to (+) and (*) yields your type
(Num (a -> a), Num a) => a -> (a -> a) -> a -> a
But, it has the strange constraint Num (a->a)
, which states that a function needs to be a number. This is basically not supposed to be done, but there is nothing in Haskell that would forbid it, so the compiler doesn't complain at this point. It is only when you try to use the function that it performs the check.
((+) . (*)) 1 (+ 1) 2
<interactive>:16:1:
No instance for (Num (a0 -> a0)) arising from a use of ‘it’
In a stmt of an interactive GHCi command: print it
The three inputs have the correct type, except for the constraint.... The interpreter is complaining here that the function (+ 1) is not a number.
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