Exercise 3: Functions

Because F# views functions as being simply another type of data, the language provides a great deal of flexibility in how you work with functions. In this exercise you’ll continue to work with the F# interactive console to become familiar with the way F# handles functions.

Task 1 – Binding Functions to Identifiers

In this task you will see how functions are values by binding them to identifiers.

  1. Create a function and bind it to the identifier addTenToNumber. Enter the following command at the F# interactive command prompt;

    F#

    let addTenToNumber = (fun x -> x + 10);;

    Response

    val addTenToNumber : int -> int

    This binds a function to the identifier addTenToNumber. The function is declared with the fun keyword, followed by an argument (or list of arguments), the -> operator and finally the body of the function.

    Note:
    F# has bound the identifier addTenToNumber to your type, which is defined as int -> int. This indicates that the value bound to addTenToNumber is a function which takes an argument that is an integer, and returns an integer.

  2. You can invoke the function by referencing it via its identifier. Enter the following command at the F# interactive command prompt;

    F#

    addTenToNumber 32;;

    Response

    val it : int = 42

Task 2 – Binding Functions with Multiple Parameters

In this task you will examine how F# handles functions with multiple parameters.

  1. First you need to create a function with multiple parameters. To build on your last example of addTenToNumber, you will create a more general function called addTwoNumbers. Enter the following command at the F# interactive command prompt:

    F#

    let addTwoNumbers = (fun x -> (fun y -> x + y));;

    Response

    val addTwoNumbers : int -> int -> int

    Note:
    The syntax used here to declare a function with two arguments goes to further demonstrate that in F# function are values. Read that code carefully. The first thing to notice is that addTwoNumbers isn’t really one function, it’s actually two functions: one function that accepts the first parameter and returns yet another function that accepts the second parameter.

    This is completely different to how functions behave in traditional .NET languages that exist today.

    Your first thought might be “wow, that syntax is very ugly and hard to understand.” Luckily, there is a much easier shortcut syntax that you will see shortly. But once you understand how functions are defined under the hood like this, it becomes much easier to understand other advanced concepts you will cover like “partially-applied functions.”

  2. You can call the function and see that it behaves the way you would expect it to by adding two numbers together. Enter the following code at the F# interactive command prompt:

    F#

    addTwoNumbers 21 21;;

    Response

    val it : int = 42

  3. While the syntax used in the previous example works (and is more in line with the way F# handles functions), it can be quite difficult to work with, especially as the number of parameters continues to grow. Even in your scenario with just two arguments, the code is more difficult to read and understand than it needs to be. Luckily, F# provides a simpler syntax for declaring functions. Enter the following command at the F# interactive command prompt;

    F#

    let addTwoNumbers x y = x + y;;

    Response

    val addTwoNumbers : int -> int -> int

    Note:
    F# has bound your function, which takes two integers, to the identifier addTwoNumbers. Notice that F# still says the signature is “int -> int -> int”, meaning a “function that takes an int that returns a function that takes an int that returns an int.” And yes, that sentence may take a couple of reads to soak in!

    It is important to remember that in F#, arguments for functions are not typically enclosed in parentheses like they are in C#. To declare a function with two arguments, the syntax is:

    let functionName x y = …;;

    This is NOT the same as:

    let functionName (x, y) = …

    In the second case you have told F# that your function expects a single parameter, a Tuple, for the argument to your function.

  4. Just like any other value, values returned from functions in F# can be bound to identifiers. Enter the following command in the F# interactive console;

    F#

    let a = addTwoNumbers 21 21;;

    Response

    val a : int = 42

    F#

    a;;

    Response

    val it : int = 42

Task 3 – Working with partially-applied functions

Because of the way functions are defined in F#, F# lends itself naturally to the usage of “partially-applied” functions (also known as “curried” functions).

  1. Partially-applied functions are functions that allow them to be called without having all of their input arguments applied. What is returned from a partially-applied function is a function whose parameter is the next expected parameter in the function chain. First, you need to recreate your function that takes two integer values as arguments. Enter the following code into the F# interactive command prompt:

    F#

    let addNumbers x y = x + y;;

    Response

    val addNumbers : int -> int -> int

  2. You can call this function by passing it two integers and see the results. Enter the following command at the F# interactive command prompt:

    F#

    addNumbers 20 22;;

    Response

    val it : int = 42

  3. To show the power of partially-applied functions, rewrite your addTenToNumber function to partially apply the addNumbers function. Enter the following command into the F# interactive command prompt:

    F#

    let addTenToNumber = addNumbers 10;;

    Response

    val addTenToNumber : (int -> int)

    Note:
    Notice that even though the addNumbers function expects two parameters, you are only calling it with a single parameter. Remember how functions are defined in F#. Your addNumbers function is really defined as “let addNumbers = (fun x -> (fun y -> x + y))”. So, when you call addNumbers with a single parameter, a function is returned that is expecting the second parameter. In this vein, the addNumbersfunction has been partially applied. This returned function is then bound to the identifier addTenToNumber.

  4. In this case, the identifier addTenToNumber is bound to a function that expects an integer argument and returns an integer. This happens to be the output of the addNumbers function when it is passed one argument. You can call the function bound to addTenToNumber by entering the following code at the F# interactive command prompt:

    F#

    addTenToNumber 32;;

    Response

    val it : int = 42

Next Step

Exercise 4: Lists