Functions

We have already seen functions in sections about basic datatypes and evaluating expressions. In this section, we explore the how the functions work and introduce the concepts of 'function literals', and functions as values.

Function literals

In addition to predefined functions, such as reverse, we can also define our own. The most primitive way of achieving this is to use function literals, which are literal representation1 for function values.

As an example, the following is a function that multiplies its argument by itself and subtracts one:


\x -> x * x - 1

In the above, the variable between \ and -> is the parameter of the function. When the function is called, all occurences of this parameter in the left of the -> are replaced by the argument of the function. The name of the parameter does not matter, and the above could have been equivalently written as (\y -> y * y - 1) or even (\number -> number * number - 1).

Like predefined functions, the only operation on function literals is application. For example, we can apply the above function to the value 10:


   (\x -> x * x - 1) 10
== {- Function application, x:=10 -}
    10 * 10 - 1
== {- Arithmetic -}
   99

Function literals, like other values, can be given a names. You can do this in GHCi session by using let-statements. This allows you to re-use function literals:

GHCi, version 7.6.1: http://www.haskell.org/ghc/  :? for help
Prelude> let firstFunction = \x -> x * x - 1
Prelude> let oneMore = \x -> x+1
Prelude> firstFunction 2
3
Prelude> oneMore (firstFunction 2)
4

Alternatively, you can save your function definitions in a file and load them with GHCi. See Section 'Saving declarations to modules' for further details on how to do this.

Worked example: Evaluating an expression with function literals.

Task: Given following definitions,


myFunction = \x -> x * x - 1 

evaluate expression myFunction 5.

Answer


  myFunction 5
== {- Definition, myFunction = \x -> x*x-1 -}
  (\x -> x*x-1) 5
== {- Function application, x := 5 -}
  5*5-1
== {- Arithmetic -}
  24  

Worked example: Function as argument and return value.

This example demonstrates that functions can be passed as arguments and used as return values.

Task: Given

Download sample

myFunction = \x -> x * x - 1 
twice      = \f -> (\x -> f (f x))

evaluate twice myFunction 5.

Answer


  twice myFunction 5

== {- Definition, twice = \f -> (\x -> f (f x)) -}
  (\f -> (\x -> f (f x))) myFunction 5

== {- Application, f := myFunction-}
  (\x -> myFunction (myFunction x)) 5

== {- Application, x := 5 -}
  myFunction (myFunction 5)

== {- Definition, myFunction = \x -> x*x-1 -}
  (\x -> x*x-1) (myFunction 5)

== {- Application, x := (myFunction 5) -}
  (myFunction 5) * (myFunction 5) - 1

== {- According to the previous calculation,  myFunction 5 == 24 -}
  24 * 24 - 1

Notice how the evaluation is purely mechanical and how function application works on purely symbolic level. We do not really care what (myFunction 5) actually is when we replace f with it. All that matters is that we write it in the place of f.

You can try out more examples below:

When faced with a difficult Haskell expression, try to calculate its value with pen and paper. This will clarify to you how the expression works.

Functions with multiple parameters

The function literals above have only a single parameter. This seems restricive. What if we would need a function to calculate the area of a rectangle based on its width and height? Although this operation requires are two parameters, it can be represented with the uniparameter function literals we just saw.

First, we suppose that the height is constant and make a function which takes the width as a parameter:


(\width -> width * height)

Next, we perform a trick known as 'currying', and return the above function from another function that takes the height as a parameter:


(\height -> (\width -> width * height))

Now, this construct can be supplied two parameters, one after another:


   (\height -> (\width -> width * height)) 5 3
== {- function application is right associative, so this is same as -}
   ((\height -> (\width -> width * height)) 5)   3
== {- function application, height := 5 -}
   (\width -> width * 5)   3
== {- function application, width := 3 -}
   3 * 5
== {- arithmetic -}
   15

Writing function definitions

To avoid writing the above function every time we wish to calculate an area, we give it a name and save it to a file:

Download sample

module Rectangle where
-- You can write this to file Rectangle.hs and
-- load it in GHCi with command `ghci Rectangle.hs`

rectangleArea = \height -> (\width -> width * height)

Since \height -> (\width -> width * height) is quite complex to write, Haskell allows you to shorten it to:


\height width -> width * height

Furthermore, if you intended to give the function a name, it can be written even more concisely as:

Download sample

rectangleArea2 height width = width * height

Remember that this means the same as the original definition with function literals.

Similarly, we often omit converting the function name into a function literal before applying it and calculate like this:


   rectangleArea2 5 3
== {- definition of rectangleArea2, height:=5, width:=3 -}
   3 * 5
== {- arithmetic -}
   15

Drills

Check your understanding. Which of the below is the result of the application f a, if f = \x -> reverse x and a = words "a cat"?

Check your understanding. Which expressions below are equivalent to the expression f a, given the definitions f = \x -> \y -> (x++y++x), a = reverse y and y = "cat"

Partial application

Functions can be partially applied. For example, rectangleArea 5 is a valid Haskell expression, which denotes a function from width to area of a rectangle. This can be used to build new definitions, such as,

Download sample

areaOfRectangleWithHeight5 = rectangleArea 5

The meaning of areaOfRectangleWithHeight5 can be seen by calculating its value:


   areaOfRectangleWithHeight5' 

== {- Definition -}
   rectangleArea 5

== {- Value of rectangleArea -}
   (\height -> (\width -> width * height)) 5

== {- Function application, height := 5 -}
   \width -> width * 5

That is, areaOfRectangleWithHeight5 is just a function that multiplies it argument by five.

Besides being useful in creating specialized functions, partial application is especially useful with predifined functions such as map (see definition of map)


   map (rectangleArea 5) [1,2,4]
== {- definition of rectangleArea -}
   map ((\height -> (\width -> width * height)) 5) [1,2,4]
== {- Function application, height := 5 -}
   map (\width -> width * 5) [1,2,4]
== {- Map applies the function to all elements of the list -}
   [(\width -> width * 5) 1 ,(\width -> width * 5) 2 ,(\width -> width * 5) 4]
== {- function applications with width:=1, width:=2, width:=4, respectively -}
   [1 * 5, 2 * 5, 4 * 5]
== {- Arithmetic -}
   [5,10,20]

Drills

Check your understanding. Which of the expressions below are equivalent to the expression (\x -> (\x -> x*x)) 5?

Explicit application ($) and function Composition (.)

Function application is denoted by writing the argument after the function name separated by a space. Everything that is not an operator is passed on as a parameter, which sometimes makes it difficult to pass a result of a function as a parameter to another. Consider,

Download sample

reverseWords str = reverse (words str)
-- vs.
nonSense     str = reverse words str

Notice the need of parenthesis above. We can make function application explicit by defining a new operator called $ or 'apply':


f $ x = f x
infixr 0 $

With the explicit application operator, we can write

Download sample

reverseWords str = reverse $ words str

This works because operators are not given as arguments to functions:


   reverse $ words str
== {- Adding parenthesis for clarity -}
   reverse $ (words str)
== {- Definition of $, f = reverse, x = (words str) -}
   reverse (words str)

Function composition is denoted by symbol (.), which is defined like this


f . g = \x -> f (g x)
infixr 9 .

Similar to its use in mathematics, function composition is often used to build new functions by composing together other functions. For example, let us consider the task of reversing the order of words in a string:

Download sample

reverseWords2 = unwords . reverse . words

The reverseWords2 definition defines a new function, even though we do not use function literals, or name the parameters. Again, this is obvious by simple calculation:


   reverseWords2 

== {- Definition-} 
   unwords . reverse . words

== {- (.) is right associative -}
   unwords . (reverse . words)

== {- Application, f . g = \x -> f (g x), and f := reverse, g := words -}
   unwords . (\x -> reverse (words x))

== {- Application, f . g = \x -> f (g x), and f := unwords, g := \x -> .. -}
   \x -> unwords ((\x -> reverse (words x)) x)

== {- Notice the to different variables named x. For clarity, rename one of them to y -}
   \y -> unwords ((\x -> reverse (words x)) y)

== {- Application, x := y -}
   \y -> unwords (reverse (words y))

Function composition and explicit application have many different uses, but perhaps the most common usage is to construct chains of operations such as f . g . h . j $ x.

Drills

Check your understanding. Which of the expressions below are equivalent to the expression f . g . h . j $ x?

Exercises

Exercise -- Practise evaluation

Reading derivations can help you understand how Haskell expressions work. Do some derivations yourself to make the understanding concrete:

  1. Derive, without a computer, the result of expression (\x -> x 2 * 2) (\x -> x+x)
  2. Derive, without a computer, the result of expression unwords . map sort . words
  3. Given the definition f = \x -> x:f (x+1), derive the result of expression f 5. (The operator : adds an element to the head of a list. See here)
Upload answer

(Firefox temporarily unsupported. Use chrome)


  1. A literal value is the concrete textual representation for abstract values. For example, the characters 1 and 2 form the literal representation for the value 12. In the same way, the characters \x->2*x represent a function that doubles its argument.

Give feedback on:

[TOPIC]



Clarity of presentation

Difficulty of the topic (1 star=easy, 5 stars=very hard)

Interestingness of the topic


Detailed comments/Criticisms




Comments on this section