Functions in Haskell
So recently I started learning Haskell and one of the most interesting things about this language was to find out that a function in Haskell only takes one argument. If you are like me you are probably asking yourself: What are the function looking structures that look like the next piece of code:
It definitely looks like a function that takes 3 arguments, right? Well… that is syntactic sugar provided by Haskell to make it
look like this function is taking three arguments. The signature of this function gives us a diagram of what the skeleton
of the function looks like. But in order to understand what is goind on we have to dive into lambdas
.
This is the syntax for defining a lambda:
Now in my previous post about Procs & Lambdas in Ruby
I mentioned
that clojures (like lambda
’s) memorize the scope they were created in. The same is valid for clojures in Haskell
. Let’s look at the
next example:
In the example above we can see that addToThree
has memorized the value of the variable factoryArg
as being 3
even though the
next lambda creation set’s the value of factoryArg
to 5. So addToThree
has memorized the scope it was created in.
Also notice that since we gave lambdaFactory
only one argument it returns the lambda inside of it with the value of x
assigned to the variable factoryArg
.
A different way to write the lambdaFactory
function could be:
(Note: If you put both lambdaFactory
definitions in the same file a compile exceptions will be raised)
In the examples above we get to see the different ways to achieve the same result.
When we called the lambdaFactory
with two arguments it returned their sum.
Let’s analyze what happened there. So we called lambdaFactory
and it found the first argument, then it returned the lambda
inside of it. The returned lambda
found
the second argument and it processed the sum of the two arguments after lambdaFactory
.
When we created addToThree
we gave lambdaFactory
only one argument and it just returned the lambda
that it contains. addToThree
here is a partially applied function.
What does this mean you ask? Well let’s think about the lambda
inside the lambdaFactory
as an inner function that takes an argument and has the value of the argument of
the outer function (lambdaFactory
) in its scope. When the inner lambda
is returned it has the value o factoryArg
assigned already, now it just needs one more argument to be
able to calculate the sum of the two numbers. It just needs one more argument to be complete.
I think that now we are ready to tackle the example in the beginning of the blog. Let’s see what would the non syntactic sugar version look like:
So that looks a bit wierd let’s try and group them for better readability:
So now if we look at the signature of the function and the function definition we can see the resemblance. If we consider the signature Int -> Int
as a
function/lambda
that takes one argument of type Int
add has a return of type Int
as well, then let’s try and interpret the signature of addNumbers
:
So let’s interpret each arrow or lambda:
lambda #1
takes an argument of typeInt
and returnslambda #2
lambda #2
takes an argument of typeInt
and returnslambda #3
lambda #3
takes an argument of typeInt
and returns a value of typeInt
This is also why the signature for the return of any function doesn’t really stand out from the signature for the args.
Let’s take a look at the function definition:
lambda #1
takes an argument namedx
and returnslambda #2
lambda #2
takes an argument namedy
and returnslambda #3
lambda #3
takes an argument namedz
and returns the sum ofx y z
which is of typeInt
This is what is actually happening when you declare a function that takes multiple arguments.
Now when you will hear that all the functions in Haskell actually take just one argument you will know what it actually means. Functions with multiple arguments in their
definitions are just syntactic sugar that Haskell translates into nested code blocks that each take one argument with the deepest one actually calculating the return value.
As we saw in our case the body of the deepest block was x + y + z
just like the body of the function defined in the beginning.
I hope that this is going to be helpful to anyone that is trying to understand how functions work in Haskell. While it’s an amazing language, it does tend to be intimidating in the beginning of the journey because of the different aproach one needs to take in order to understand some concepts. I also hope that writting function type signatures will be less intimidating from now on.
###Resources: