Precedence and $
So Haskell has a very regular syntax. At the highest priority is parentheses; then comes applying a function to its argument. Function application is left-associative, or, as I prefer to call it, greedy nom: meaning that a function "eats up" the very first thing that it sees after it as its argument. So, if you write f g h
this becomes (f g) h
: f
eats g
first, and the return value eats h
afterward. Often, especially when you're defining a function, you want to write something like f (Constructor parameter1 parameter2) = ...
where you really need the explicit parentheses so that you're not accidentally writing ((f Constructor) parameter1) parameter2
.
After parentheses and applications, we have operators: these which have a whole hierarchy of priorities and associativities given by the "infix directives". The lowest-priority operator is defined as:
f $ g = f g
infixr 0 $
This operator is a totally normal operator which seemingly does nothing at all: more precisely it applies the function on its left to the argument on its right. It is low-priority right-associative, so it is "lazy nom" (the function before the $
is applied to everything after that $
). There is an interesting syntax dispute over whether f . g . h $ i
is more or less correct than f $ g $ h $ i
, which does the same thing in a different way.
Remember that $
is actually just an ordinary operator/function. For example you can do things like this:
Prelude> let factorial n = product [1..n]
Prelude> map ($ 3) [(5 +), (3 *), (3 +) . factorial . (2 *)]
[8,9,723]
Here we are creating a function ($ 3)
which takes a function as its argument, applying it to 3. We map the resulting function over several other functions. We could also write this as zipWith ($) functions (repeat 3)
if you really wanted, passing ($)
as the combining function that zipWith will use to zip two lists together. They're the same thing, and they're both fun tricks. You might even someday want to map (flip ($))
over a list of values, to get a list of values in the form of functions. It's an isomorphism; you can get the values back with id = map ($ id) . map (flip ($))
, but maybe that format will be more convenient for you someday.
Lower priority than this are special forms like if
, let
, case
, do
, where
, and \
. In general Haskell requires that these cannot appear immediately after a value or )
, but may appear after a (
or an operator. So if you want to write f \x -> 3 + 2 * x
Haskell will complain until you make this into one of the following:
f ((3 +) . (2 *)) -- no special forms
f (\x -> 3 + 2 * x) -- parenthesize the sub-expression
f $ \x -> 3 + 2 * x -- use $ to make the syntax "work" effortlessly.
Similarly you might see things like:
main = complicatedProcessingStep . preprocessing $ do
input <- io_input
...
where the $
is being used to avoid putting parentheses around the do
so that you don't have to dangle a )
token somewhere in the whitespace.
Functions have one argument
One huge way that Haskell differs from other languages is that every function has exactly one argument. This is probably going to confuse you at first: aren't operators functions of two arguments, and what about \a b c -> ...
, doesn't it have three?
The answer is no: \a b c -> ...
is syntactic sugar for \a -> \b -> \c -> ...
(this is not to mention the fact that you can pattern match on those arguments, too, so secretly \a -> ...
is syntactic sugar for \random_token -> case random_token of a -> ...
). Each function has one argument, but some functions return a function. In Haskell we can do what other languages do, and accept a tuple; \(a, b) -> a + b
works fine, and is equivalent to uncurry (+)
. We just usually don't do that -- we usually pass \a b -> a + b
.
You can make an operator out of any function which returns a function. The resulting operator takes its left-hand-side as an argument for the first function and its right-hand-side for an argument to the second. The canonical way to do this is with backticks:
13 `mod` 7 == mod 13 7
but if the types are not polymorphic, or if you write an explicit type signature or disable the "monomorphism restriction", you can also write things like (%%%) = mod
.
Three-argument operators.
So there's your answer about "three-argument operators": it returns a function which can then be applied to other values. When you write:
a x $|| b y $ c z
because of the above rules this parses as:
($) (($||) (a x) (b y)) (c z)
which by the definition of ($)
becomes:
($||) (a x) (b y) (c z)
Just using the operator on the sub-expression, ax $|| b y
, produces a function, which could either be applied using parentheses, as in (a x $|| b y) (c z)
, or with the $
operator which applies its left-hand side to its right-hand-side.