The purpose of a $ is to help reduce the number of parentheses () in your expressions. Here's its type signature:
($) :: (a -> b) -> a -> b
In other words, all it does is take a function and its single argument, and applies the function. Really boring. Terrifically boring. That is to say:
($) f x = f x , or more simply,
($) f = f , or more simply,
($) = id
The only thing which makes ($) worthwhile is that it has a different precedence than a regular function like id. That is, ($) has a very low precedence; in contrast, regular functions such as id have a very high precedence in application.
When acting as an infix operator, $ is right associative. That's another useful property.
Question 1: Why ($) not works on "p $ 2 $ 2 $ 5"?
Let's get rid of some $ signs. The following are all equivalent:
p $ 2 $ 2 $ 5
= p (2 $ 2 $ 5)
= p (2 (2 $ 5))
= p (2 (2 5))
I suppose you can figure out why an expression like (2 5) doesn't make any sense.
Question 2: Why ($$) works on "p $$ 2 $$ 2 $$ 5"?
When you define a new function such as ($$), it is given left assosciativity (and also highest precedence) by default. Then:
p $$ 2 $$ 2 $$ 5
= (p 2) $$ 2 $$ 5
= ((p 2) 2) $$ 5
= ((p 2) 2) 5
This is of course plain old partial application.
Question 3: Why ($$) not works on "p $$ [1]++[2..3] $$ 4 $$ 5"?
p $$ [1] ++ [2..3] $$ 4 $$ 5
= (((p $$ [1]) ++ [2..3]) $$ 4) $$ 5
= (((p [1]) ++ [2..3]) 4) 5
Question 4: Is there a more elegant way to write "p $$ [1]++[2..3] $$ [1]++[2..3] $$ 5" correctly?
Without the wacky $$, I suppose. :)