Exercise 2: Using the “Let” Keyword in F#

Strictly speaking, functional languages like F# do not have variables. Instead, values are said to be bound to identifiers so that they may be referenced elsewhere in the program. Accordingly, the F# “let” keyword allows you to assign any expression to an identifier.

In this exercise you learn how to use the let keyword to bind values to identifiers.

Task 1 – Using the Let Keyword to Bind a Value to an Identifier

  1. If it is not already open, open the F# interactive console (see the beginning of Exercise 1 for instructions)
  2. Enter the following command at the F# interactive command prompt:

    F#

    let a = 42;;

    Response

    val a : int = 42

  3. The value 42 is now accessible by the identifier a that it is bound to. To verify this, enter the following command at the F# interactive console prompt:

    F#

    a;;

    Response

    val it : int = 42

    Note:
    In functional languages like F#, instead of assigning a value to a variable, a value is said to be bound to an identifier. This is because in pure functional languages once a value is assigned to an identifier it never changes. If a developer binds a value to an identifier (i.e. “let x = 42;;”) and then later in the same scope binds a new value to an identifier of the same name (i.e. “let x = ‘forty two’”) F# creates a new identifier, and gives it the same name. The previous identifier (in this case, the one bound to 42) still exists, but is no longer reachable.

    F# is not, however, a pure functional language. By using the mutable keyword and the left ASCII arrow ( <- ) developers can create an identifier whose value can change. The mutable keyword is used to define the initial value, while the left arrow is used to change the value. It is possible to change the value; however, the type cannot be changed.

  4. Identifiers can be used in computations. Enter the following command at the F# interactive command prompt;

    F#

    a + 1;;

    Response

    val it : int = 43

    Note:
    F# used the bound identifier in the computation and returned the result.

  5. Remember that unless you are using the mutable keyword, values in F# cannot be changed. Enter the following commands at the F# interactive command prompt:

    F#

    let a = 0;; a;;

    Response

    val it : int = 0

    Note:
    F# has bound the value 0 to a new identifier also called a (as mentioned above, the previous a identifier still exists, but is no longer reachable).

  6. Because binding a new value to a causes F# to create a new identifier called a, the new value that is being bound can be a completely different type than the previously bound value. Enter the following command at the F# interactive command prompt:

    F#

    let a = "42";;

    Response

    val a : string = "42"

    Note:
    You can see that even though the previous a contained an integer value, this new a contains a string.

Task 2 – Working with tuples and identifiers

In this task you will learn how tuples can be composed of identifiers that have been bound to values and separated into individual values which can also be in turn bound to identifiers.

  1. For this example you will need a tuple defined in F#. Enter the following command at the F# interactive command prompt:

    F#

    let a = (42, "Hello F#");;

    Response

    val a : int * string = (42, "Hello F#")

    Note:
    This creates a tuple of type int * string (a structure with an integer and a string);

  2. F# provides the functions fst and snd to break apart tuples made up of two elements. Enter the following commands at the F# interactive command prompt:

    F#

    let b = fst a;;

    Response

    val b : int = 42

    F#

    let c = snd a;;

    Response

    val c : string = "Hello F#"

    F# binds the first element (the integer) of the tuple to b and the second element (the string ) to c. Enter the following into the interactive console to verify the values of your identifiers:

    F#

    b;;

    Response

    val it : int = 42

    F#

    c;;

    Response

    val it : string = "Hello F#"

    Note:
    As you can see, b and c have now been bound to the individual values of the tuplea.

  3. An alternative method for binding the individual values of a tuple to identifiers is to use a let statement to bind the values within the tuple to a pattern of identifiers. Enter the following commands into the F# interactive command prompt:

    F#

    let (b,c) = a;;

    Response

    val c : string = "Hello F#" val b : int = 42

    F#

    b;;

    Response

    val it : int = 42

    F#

    c;;

    Response

    val it : string = "Hello F#"

    Note:
    F# is able to use its pattern matching abilities to bind each individual value in the tuple to the b and c identifiers respectively.

  4. The fst and snd functions are fine if your tuple only has two values. For tuples that have more than two values, F# allows us to use the let statement to bind the elements of a tuple to a pattern. Enter the following command into the F# interactive console:

    F#

    let a = (42, "Hello F#", 42.0);;

    Response

    val a : int * string * float = (42, "Hello F#", 42.0)

    Because this tuple is made up of more than one element, fst and snd will not work. In fact, if you try to use these methods F# Interactive will complain.

    Figure 2

    Failure of fst and snd

  5. Even though fst and snd will only work on tuples containing two values, pattern matching can be used no matter how many values are contained in a tuple. Enter the following command at the F# interactive command prompt:

    F#

    let (b, c, d) = a;;

    Response

    val d : float = 42.0 val c : string = "Hello F#" val b : int = 42

    You can verify this by examining the values bound to each identifier. Enter the following into the F# interactive console:

    F#

    b;; c;; d;;

  6. When using patterns of identifiers to get individual values from a tuple, there may be occasions where you only want a subset of the available values. F# provides a mechanism with its pattern matching syntax to ignore values that are not needed. By simply replacing the identifier in the pattern with an underscore character (_), you can instruct F# to ignore that value when it performs the pattern match. In the example below, you are only concerned with getting the string value (the second value) from your tuple, you do not care about the first or third values. Enter the following command at the F# interactive command prompt:

    F#

    let (_, e, _) = a;;

    Response

    val e : string = "Hello F#"

    Note:
    F# has only bound the identifier that you provided in out pattern; in this case e is bound to the string “Hello F#”.

Next Step

Exercise 3: Functions