When you are new to haskell, the weird looking operators might get you puzzled. In this blog post i will explain two basic ones the
.. They are used to specify in which order haskell should try to evaluate a bunch of functions.
Let's say you have two functions for operating on strings
trim :: String -> String upperFirst :: String -> String
userInput :: String which comes from the console or a webinterface. We will used these functions to trim off the excess white space and then capitalize the first character. How they work is not important here, only that they take a string and return a string.
You probably know haskell can chain functions, so your first initial thought would be to write
result = upperFirst trim userInput
This will evaluate as
result = (upperFirst trim) userInput
Which will not work because
upperFirst expects a string as it's first argument and not a function. This is due to associativity, which can be either left or right. Since we are trying to "give arguments to functions", or more commonly described as "applying the function to it's argument", we are talking about function application. For which the associativity is left. That means if you have a function (
trim), and to the left of that is another function (
upperFirst) haskell tries the left function first.
To fix this we can just put the parenthesis in the right place ourselves
result = upperFirst (trim userInput)
Another important concept in haskell is precedence. We say if something is more important to evaluate first then it has higher precedence. Just like in
2 + 3 * 4 the
* has higher precedence and thus should be evaluated first even though it's on the right side.
Normal function application has the highest precedence of 10. You can use ghci to get information precedence.
prelude> :i ($) infixr 0 $ prelude> :i (.) infixr 9 .
note: some extra information given was left out
We see that both
. have lower precedence than normal (10).
$ is even extremely low (0). This becomes important going into the next sections.
(low-precedence) function application
$ is a shorthand to evaluate stuff on the right first is
$. Operators are also functions in haskell and so this operator has a function definition
($) :: (a -> b) -> a -> b
Since this function (operator) is used between (infix) two other things, this might be more clear
(a -> b) $ a
First comes a function
a -> b that takes a value that is on the right side of
$ we can rewrite the string functions as
result = upperFirst $ trim userInput
trim userInput is evaluated first into
a and then given to
(a -> b) (upperFirst).
$ is called (low-precedence) function application because everything to the left and right gets evaluated first. If it had higher precedence we would have to write
(function here) $ (value here)
Which would make the
. can be used to compose function together. Thus with
. we can make a new function, like so
prettify = upperFirst . trim
now you can do
result = prettify userInput
However there are a couple of important things to note about
Firstly you might think you can use
. directly like so
result = upperFirst . trim userInput
This doesn't work because
. has less precedence than normal function application. So in effect this would evaluate as
result = upperFirst . (trim userInput)
. is given a string as it's second argument. While it's function signature is
(.) :: (b -> c) -> (a -> b) -> a -> c
and this expects a function instead (
(a -> b)). The right thing to write would be
result = (upperFirst . trim) userInput -- or result = upperFirst . trim $ userInput
. function signature might look pretty complicated. Can you find all the mentioned arguments in the code? Maybe not, and this is because that function composition was written in pointfree style. Pointfree has nothing to do with
. directly, it just so happens that a pointfree function often use
.. Note that the
pointfree does NOT refer to the
This is a pointfree function
prettify = upperFirst . trim
Then what's the not-pointfree "normal" variant? Well it's this
prettify x = (upperFirst . trim) x
Now we can find everything back from the
. function signature
(.) :: (b -> c) -> (a -> b) -> a -> c -- upperFirst is used for b -> c -- trim is used for a -> b -- x is used for a -- and of course c is the value you get when you run prettify
. we would have to write this function as
prettify x = upperFirst (trim x)
which is about just as much typing as with using
.. Only you can not reduce this further to pointfree without
. in this case.
We can say that
. can help make code shorter, but this pointfree style also can make your code harder to understand. When you are starting out in haskell you should know what
. does, but at the same time do not try to go overboard and use it everywhere. It's important that your code stays very readable. Maybe in time you will get used to the
. operator and you prefer to use it more often.
I will leave you with an example from the haskell wiki pointfree page which shows it's not required to use
. to make a pointfree function.
sum = foldr (+) 0