I understand how you feel. I found that the composition of the function is quite difficult to understand at first. What helped me figure out was type signatures. Consider:
(*) :: Num x => x -> x -> x (+) :: Num y => y -> y -> y (.) :: (b -> c) -> (a -> b) -> a -> c
Now that you write (*) . (+) (*) . (+) , this is actually the same as (.) (*) (+) (i.e. (*) is the first argument (.) and (+) is the second argument (.) ):
(.) :: (b -> c) -> (a -> b) -> a -> c |______| |______| | | (*) (+)
Therefore, a signature of type (*) (i.e., Num x => x -> x -> x ) combines with b -> c :
(*) :: Num x => x -> x -> x -- remember that `x -> x -> x` | |____| -- is implicitly `x -> (x -> x)` | | b -> c (.) (*) :: (a -> b) -> a -> c | | | |‾‾‾‾| Num x => xx -> x (.) (*) :: Num x => (a -> x) -> a -> x -> x
Therefore, a signature of type (+) (i.e., Num y => y -> y -> y ) combines with Num x => a -> x :
(+) :: Num y => y -> y -> y -- remember that `y -> y -> y` | |____| -- is implicitly `y -> (y -> y)` | | Num x => a -> x (.) (*) (+) :: Num x => a -> x -> x | | | | |‾‾‾‾| |‾‾‾‾| Num y => yy -> yy -> y (.) (*) (+) :: (Num (y -> y), Num y) => y -> (y -> y) -> y -> y
I hope this clarifies where Num (y -> y) and Num y come from. You still have a very strange function like (Num (y -> y), Num y) => y -> (y -> y) -> y -> y .
Which makes him so strange that he expects both y and y -> y be Num instances. It is clear that y must be an instance of Num , but how y -> y ? Creating y -> y instance of Num seems illogical. It may not be right.
However, this makes sense when you look at which functional composition is actually:
( f . g ) = \z -> f ( gz) ((*) . (+)) = \z -> (*) ((+) z)
So you have the function \z -> (*) ((+) z) . Therefore, z must be an explicit Num instance, because (+) applies to it. Thus, the type \z -> (*) ((+) z) is equal to Num t => t -> ... , where ... is the type (*) ((+) z) that we recognize in an instant .
Therefore, ((+) z) is of type Num t => t -> t , because it requires one more number. However, before it is applied to another number, (*) will be applied to it.
Therefore, (*) expects ((+) z) be an instance of Num , so t -> t is expected to be an instance of Num . Thus, ... is replaced by (t -> t) -> t -> t and the restriction Num (t -> t) added, which leads to the type (Num (t -> t), Num t) => t -> (t -> t) -> t -> t .
The way you really want to combine (*) and (+) uses (.:) :
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d f .: g = \xy -> f (gxy)
Therefore, (*) .: (+) Coincides with \xy -> (*) ((+) xy) . Now, two arguments are given (+) , ensuring that ((+) xy) really just Num t => t , and not Num t => t -> t .
Therefore, ((*) .: (+)) 2 3 5 is (*) ((+) 2 3) 5 , which is (*) 5 5 , which is 25 , which I believe is what you want.
Note that f .: g can also be written as (f .) . g (f .) . g , and (.:) can also be defined as (.:) = (.) . (.) (.:) = (.) . (.) . You can read about it here:
What does (f.). Does g mean in Haskell?
Hope this helps.