We saw previously that functions in Haskell take single arguments. There are ways around passing multiple arguments into a function but this is restrictive as we have to ensure we always call the function with the same number of arguments.
For example, this:
multiply :: (Integer, Integer) -> Integer multiply (x,y) = x * y
…will only allow us to multiply two numbers and no more:
Prelude> multiply (3,4) 12 Prelude> multiply (3,4,5) :20:10: Couldn't match expected type ‘(Integer, Integer)’ with actual type ‘(t0, t1, t2)’ In the first argument of ‘multiply’, namely ‘(3, 4, 5)’ In the expression: multiply (3, 4, 5) In an equation for ‘it’: it = multiply (3, 4, 5)
In Haskell, functions are considered first-class objects as noted in the AQA specification:
– Know that a function is a first-class object in functional programming languages and in imperative programming languages that support such objects. This means that it can be an argument to another function as well as the result of a function call.
First-class objects in any programming language are those elements that can:
• appear in expressions• be assigned to a variable• be assigned as arguments• be returned in function calls.
An integer is an example of a first-class object. We can use an integer in an expression (e.g. the integer 5 used in the expression y = x + 5), assign it to a variable (z = 5), use it as an argument (addTwoMore(5)) and return it from a function (i = addTwoMore(5)). Functions in Haskell can be treated in the same way. This does not mean the results returned from the function but the functions themselves as objects in their own right.
We can see how this is useful if we employ a concept called partial function application. This again appears in the AQA specification:
– Know what is meant by partial function application for one, two and three argument functions.
What this means is we can call a function with fewer arguments than needed and store its state as a variable for later use as it is a first-class object. Consider the following function type declared in Haskell:
multiply :: Integer -> (Integer -> Integer)
This function will take an Integer and return what is in the brackets which is also a function that itself takes an Integer and returns an Integer! Let’s use the fact that we can assign a function to a variable to understand this process. Starting with the function definition:
multiply :: Integer -> (Integer -> Integer) multiply x y = x * y
…we can then use the interpreter to check it works as expected:
Prelude> multiply 4 2 8
It looks like the function is taking two arguments but actually it is partially applying it with the 4 and producing a second function. This is then applied with the 2 to get the correct result. We could break this down to see what happens:
Prelude> let partAns = multiply 4 Prelude> partAns :74:1: No instance for (Show (a0 -> a0)) arising from a use of ‘print’ In a stmt of an interactive GHCi command: print it
The error message explains that the variable partAns, that contains the partially applied function, doesn’t have any code that tells the compiler how to display a value. Importantly though it does contain a function that can accept an Integer and return an Integer result (equivalent to the (Integer -> Integer) part of the function type). Imagine that after this partial application the function definition would now be:
multiply 4 y = 4 * y
This matches the pattern of the function type. Take an Integer; return a function that takes an Integer and returns an Integer. We can then use the variable that contains the partially applied function together with a second argument to get our result:
Prelude> partAns 2 8
The fact that functions can be partially applied and that they are first-order objects allows us to chain functions together to create different results. For example, we can use the multiply function to effectively process three arguments by passing a partially applied functions as an argument to another function:
Prelude> let firstPart = multiply 5 Prelude> multiply (firstPart 6) 7 210
We have a series of functions that accept just one argument that can be used themselves as arguments to other functions. This behaviour is default in Haskell and our function type takes care of this. The -> operator is right-associative so we can leave out the brackets:
multiply :: Integer -> Integer -> Integer
All of this is just theoretical knowledge. We can use the rule of thumb that each arrow in the type represents an argument being used by the function until the last one which is the return type. We can adjust the multiply function to accept three parameters by default if we want:
multiply :: Integer -> Integer -> Integer -> Integer multiply x y z = x * y * z
…which can be called as:
Prelude> multiply 5 6 7 210
Note that we could use the same principle to use the result of an earlier function call to exhibit the same behaviour as outlined above. For example:
Prelude> multiply (multiply (2,3),4) 24
This is different in that the first function needs to evaluate so the result can be used as an argument to another function call rather than wait for the remaining arguments to be presented. The problem with tuples is that the data has to be ready to present at the next function call. The function cannot be partially applied waiting for other results so all arguments need to be present at the time of calling.
We can break apart a function with a tuple using a concept known as currying. This transforms a function with tuple arguments into a chain of functions each with a single argument that allows partial application to take place. So why the term curry? Someone called Haskell Curry came up with the idea!