The dollar sign,
$, is a controversial little Haskell operator. Semantically, it doesn’t mean much, and its type signature doesn’t give you a hint of why it should be used as often as it is. It is best understood not via its type but via its precedence. We believe it is the relative semantic emptiness of this operator combined with the relative obscurity of precedence that makes it so confusing at first glance.
$ operator is an infix operator for … function application?
($) :: (a -> b) -> a -> b
a -> b function and an
a to apply it to, it gives us a
λ> sort $ "julie" "eijlu"
Weird infix, but okay. Haskell doesn’t need an operator for function application; white space is enough.
f :: a -> b x :: a $ x = f xf
λ> sort "julie" "eijlu"
This seems utterly pointless, until you look beyond the type.Here and throughout this article, we have simplified the type from what you may see if you query this information in your own REPL. The extra bits of information the REPL may give are not important to understanding this, so we’ve ignored them.
λ> :info ($) ($) :: (a -> b) -> a -> b -- Defined in ‘GHC.Base’ infixr 0 $
This little note, so easily overlooked, at the end holds the key to understanding the ubiquity of
infixrtells us it’s an infix operator with right associativity.
0tells us it has the lowest precedence possible.
In contrast, normal function application (via white space)
- is left associative and
- has the highest precedence possible (10).
So the role of the
$ operator is to give us function application with a different – opposite, really – associativity and precedence.
What that means is that you usually see the
$ where standard function application wouldn’t have the necessary associativity and precedence for the context. And what that means is you usually see it used instead of parentheses to associate things that otherwise wouldn’t.
That will be more clear with some examples. Compare:
λ> sort "julie" ++ "moronuki" "eijlumoronuki" λ> sort $ "julie" ++ "moronuki" "eiijklmnooruu"
In the first case, the application of
sort to “julie” is binding the tightest, so the argument to
sort is just the first string, “julie”, and the sorted string “eijlu” is the first argument to
(++). In the second case, using
$ changes the associativity, exactly as if we’d used parentheses:
λ> sort ("julie" ++ "moronuki") "eiijklmnooruu"
So the argument to
sort is the concatenated two strings, instead of only “julie”.
One pattern where you see the dollar sign used sometimes is between a chain of composed functions and an argument being passed to (the first of) those.
λ> head . sort $ "julie" 'e'
This is a bit odd since we just said the
$ is right associative; there’s really nothing to evaluate on the right. So, the key here is the precedence. We can’t do this:
λ> head . sort "julie"
It wouldn’t evaluate properly because
(.)operator has a precedence of 9, but
- function application (
sort "julie") has higher precedence.
That means the application of
sort to its argument would happen before the composition of
(.) expects two function arguments:
(.) :: (b -> c) -> (a -> b) -> a -> c
sort to an argument means it’s not a function anymore.
λ> :type (sort "julie") (sort "julie") :: [Char]
The second argument to
(.) can be
sort, but it cannot be
sort is an
a -> b function but
sort "julie" is not.
But, weirdly, this isn’t the only way
$ can be used with regard to function composition. In some cases
$ can replace the composition operator,
(.). So, both of these are valid Haskell:
λ> head . sort $ "julie" 'e' λ> head $ sort "julie" 'e'
In the first case, it’s the 0-precedence of
$ that matters most; in the second case, it’s the right-associativity that matters. In the second case, everything on the right gets evaluated first, and then
head applies to that result. So in this case we end up with the same result either way.
However, they cannot always be used interchangeably. These are both fine:
λ> headSort xs = head $ sort xs λ> headSort "julie" 'e' λ> headSort = head . sort λ> headSort "julie" 'e'
But this is not:
λ> headSort = head $ sort
In this case, for once, it’s because of the type, not because of the precedence.
sort by itself is not the right type of thing to be the second argument to
There are times when the dollar sign is used before a particularly large argument to a function. This is most common, perhaps, with
do blocks, but also shows up at times when there is a biggish anonymous function being passed as an argument to another function. Let’s look at a really typical example; this is from the documentation for the
scottyBeam me up. web framework.
We’ve rearranged the layout a tiny bit to better match our Type Classes
do-block aesthetics, but that’s otherwise straight from the docs. This short snippet has two
$ operators. Both
get each need some big action as their second argument, so the right-associativity of
$ allows the whole big chunk after it to act as a single argument to the function. You could substitute parentheses for the exact same effect.
But typically Haskellers do not prefer to wrap such big chunks in parentheses.
Starting with GHC 8.6.1, there is also a language extension called
BlockArguments that can, if enabled, make the
$ operator unnecessary in many of these contexts.
This is the same as the first two snippets. You may notice you need to keep the parentheses (or use
$) around the block that starts with
get isn’t one of the keywords that works with
And a curiosity
Finally, we will note a curiosity about
$. Its type
($) :: (a -> b) -> a -> b
can also be written
($) :: (a -> b) -> (a -> b)
In fact, that is how the type should be read and understood, but because Haskell is curried by default, the second set of parentheses is unnecessary and, therefore, not usually written. But that means
$ is just an identity function for … functions.
id :: a -> a -- a ~ (a -> b) id @(_ -> _) :: (a -> b) -> (a -> b)
That means, if you wanted, you could sometimes substitute the
id function for
$.The backticks around
id allow it to be used in infix position; putting backticks around normal prefix functions enables you to use them as infix functions, if you like.
λ> head $ sort "julie" 'e' λ> head `id` sort "julie" 'e'
Of course, if you are infixing
id you could be prefixing it, right?
λ> id head (sort "julie") 'e'
And that is just the same as this:
λ> head (sort "julie") 'e'
You cannot, of course, do this in all the contexts you see
$ used in, because infix
$ don’t have the same…you guessed it, associativity and precedence.When you make a prefix function, such as
id infix by backticking it but don’t otherwise specify the associativity and precedence for its infix use, it defaults to left-associativity and a precedence of 9, making it the same precedence as function composition but lower (by one) than normal prefix function application. You can substitute infix
id for the dollar signs in the
scotty examples, but you need to add some parentheses as well. We will leave understanding exactly where and why as an exercise for the reader.
While this little curiosity about the relationship of
id is interesting, please do not start writing Haskell with a lot of infix
id. It will lead you to bad places and make your coworkers hate you.