Currently I’m studying function application with $, but I don’t really understand why the following happens :
I try to explore the difference between (+ 1) $ (+ 2) and (+ 1) (+ 2).
I first check their types :
(+ 1) (+ 2) :: (Num a, Num (a -> a)) => a -> a
(+ 1) $ (+ 2) :: (Num a, Num (a -> a)) => a -> a
It’s not suprise that they have the same type because of the definition of $.
The weird thing happens when I try to evaluate (+ 1) $ (+ 2) and (+ 1) (+ 2) at 1.
ghci > (+ 1) $ (+ 2) 1
4
But (+ 1) (+ 2) 1 doesn’t work, error message says “No instance for Num (Integer -> Integer)” and of course (+ 1) ((+ 2) 1) will fix this problem.
I’m confused because the two function has exactly the same type and same type constraints. Why (+ 1) $ (+ 2) notices the instance and (+ 1) (+ 2) doesn’t.
Function application binds to the left. So your (+1) (+2) 1 reads as ((+1) (+2)) 1 amd GHC is trying to apply (+1) to the function(+2) hence the error message that function type Integer -> Integer has no Num instance.
$ on the other hand has wery low priority, allowing (+2) 1 to bind naturally, that’s it’s probably most common usage scenario.
I noticed the fact you said, but I think Haskell shouldn’t be able to check the type of (+ 1) (+ 2). Since function application binds to left when (+ 1) takes (+ 2) I think Haskell should somehow “Get stuck”. I’m really interested in how does Haskell pass it and produce the type.
It’s able to check the type, but weirdly. Since plain (+1) has a type of Num a => a -> a, when it sees (+1) (+2) it tells you ‘it’ll work if you provide an instance Num (a -> a)’ (which you don’t).
I guess some of the confusion actually comes from the type of (+1) $ (+2) which is what GHC shows, but isn’t what it uses when you give (+1) $ (+2) 1.
Try running ((+1) $ (+2)) 1 or let f = (+1) $ (+2) in f 1 and note the error message is as in your first case.
GHCi does nothing special here. It’s just that (+1) $ (+2) is not a subexpression of (+1) $ (+2) 1 because of the associativity. Instead, it is a subexpression of let f = (+1) $ (+2) in f 1 or equivalently ((+1) $ (+2)) 1. The bracketing is important.