© Robert Pickering and Kit Eason 2016

Robert Pickering and Kit Eason, Beginning F# 4.0, 10.1007/978-1-4842-1374-2_4

4. Imperative Programming

Robert Pickering and Kit Eason

(1)St. Germain-En-Laye, France

As you saw in Chapter 3, you can use F# for pure functional programming. However, some issues, most notably I/O, are almost impossible to address without some kind of state change. F# does not require that you program in a stateless fashion. It allows you to use mutable identifiers whose values can change over time. F# also has other constructs that support imperative programming. You saw some in Chapter 3. Any example that wrote to the console included a few lines of imperative code alongside functional code. In this chapter, you’ll explore these constructs—and many others—in much more detail.

First, you’ll look at F#’s unit type, a special type that means “no value,” which enables some aspects of imperative programming. Second, you’ll look at some of the ways F# can handle mutable state, or types whose values can change over time. These include mutable identifiers, the ref type, mutable record types, and arrays. Finally, you’ll look at using .NET libraries. The topics will include calling static methods, creating objects and working with their members, using special members such as indexers and events, and using the F# |> operator.

The Unit Type

Any function that does not accept or return values is of type unit, which is similar to the type void in C# and System.Void in the CLR. To a functional programmer, a function that doesn’t accept or return a value might not seem interesting because a function that doesn’t accept or return a value doesn’t do anything. In the imperative paradigm, you know that side effects exist, so even if a function accepts or returns nothing, you know it can still have its uses. The unit type is represented as a literal value in the form of a pair of parentheses (()). This means that whenever you want a function that doesn’t take or return a value, you put () in the code, like so:

let aFunction() =
  ()

In this example, aFunction is a function because you placed parentheses after the identifier, where its parameters would go. If you hadn’t done this, it would have meant aFunction was not a function, but a value. You probably know that all functions are values, but here the difference between a function and a nonfunction value is important. If aFunction were a nonfunction value, the expressions within it would be evaluated only once. Because it is a function, the expressions will be evaluated each time it is called.

Similarly, placing () after the equals sign tells the compiler you will return nothing. Ordinarily, you need to put something between the equals sign and the empty parentheses, or the function is pointless. For the sake of keeping things simple, I’ll leave this function pointless. Now you’ll see the type of aFunction. The easiest way to see a function’s type is by using the tool tips that are available in Visual Studio and other IDEs, or by compiling it using F# Interactive. Alternatively, you can use the compiler’s fsc –i switch. In all cases, the result is as follows:

val aFunction: unit -> unit

As you can see, the type of aFunction is a function that accepts unit and transforms it into a value of type unit. Because the compiler now knows that the function doesn’t return anything, you can now use it with some special imperative constructs. To call the function, you can use the let keyword followed by a pair of parentheses and the equals sign. This is a special use of the let keyword, which here means “call a function that does not return a value.” Alternatively, you can use the keyword do, or you can simply call the function without any extra keywords at all, by placing the function at the top level.

let aFunction() =
  ()


let () = aFunction ()
// -- or --
do aFunction ()
// -- or --
aFunction ()

Similarly, you can chain functions that return unit together within a function; just make sure they all share the same indentation. The next example shows several printfn functions chained together to print text to the console:

let poem() =
  printfn "I wandered lonely as a cloud"
  printfn "That floats on high o'er vales and hills,"
  printfn "When all at once I saw a crowd,"
  printfn "A host, of golden daffodils"


poem()

It’s not quite true that the only functions that return a unit type can be used in this manner; however, using them with a type other than unit will generate a warning, which is something most programmers want to avoid. To avoid this, it’s sometimes useful to turn a function that does return a value into a function of type unit, typically because the function has a side effect, as well as returning a value. The need to do this is fairly rare when you’re using only F# libraries written in F# (although situations where it is useful do exist), but it is more common when using .NET libraries that were not written in F#.

The next example shows how to throw away the value of the result of a function, so that the resulting function returns unit:

let getShorty() = "shorty"
let _ = getShorty()
// -- or --
ignore(getShorty())
// -- or --
getShorty() |> ignore

You begin by defining a function called getShorty that returns a string. Now imagine that you want to call this function and ignore its result. The next two lines demonstrate different ways to do this. First, you can use a let expression with an underscore (_) character in place of the identifier. The underscore tells the compiler this is a value in which you aren’t interested. Second, this is such a common thing to do that it has been wrapped into a function named ignore, which is available in the F# base libraries and is demonstrated on the third line. The final line shows an alternative way of calling ignore that uses the pipe-forward operator to pass the result of getShorty() to the ignore function. (See the “The |> Operator” section for more information about the pipe-forward operator.)

The Mutable Keyword

In Chapter 3, I talked about how you could bind identifiers to values using the keyword let and noted how, under some circumstances, you could redefine and rebind, but not modify, these identifiers. If you want to define an identifier whose value can change over time, you can do this using the mutable keyword. A special operator, the left ASCII arrow (or just left arrow), is composed of a less-than sign and a dash (<-). You use it to update these identifiers. An update operation using the left arrow has type unit, so you can chain these operations together as discussed in the previous section. The following example demonstrates how to define a mutable identifier of type string and then change the value that it holds:

// a mutable idendifier
let mutable phrase = "How can I be sure, "


// print the phrase
printfn "%s" phrase
// update the phrase
phrase <- "In a world that's constantly changing"
// reprint the phrase
printfn "%s" phrase

The results are as follows:

How can I be sure,
In a world that's constantly changing

At first glance, this doesn’t look too different from redefining an identifier, but it has a couple of key differences. When you use the left arrow to update a mutable identifier, you can change its value but not its type; when you redefine an identifier, you can do both. A compile error is produced if you try to change the type, as this example demonstrates:

let mutable number = "one"
phrase <- 1

If you attempt to compile this code, you get the following error message:

Prog.fs(9,10): error: FS0001: This expression has type
  int
but is here used with type
  string

The other major difference is where these changes are visible. When you redefine an identifier, the change is visible only within the scope of the new identifier. When it passes out of scope, it reverts to its old value. This is not the case with mutable identifiers. Any changes are permanent, whatever the scope, as this example demonstrates:

// demonstration of redefining X
let redefineX() =
    let x = "One"
    printfn "Redefining: x = %s" x
    if true then
        let x = "Two"
        printfn "x = %s" x
    printfn "x = %s" x


// demonstration of mutating X
let mutableX() =
    let mutable x = "One"
    printfn "Mutating: x = %s" x
    if true then
        x <- "Two"
        printfn "x = %s" x
    printfn "x = %s" x


// run the demos
redefineX()
mutableX()

Executing the preceding code produces the following results:

Redefining:
x = One
x = Two
x = One
Mutating:
x = One
x = Two
x = Two

In versions of F# before 4.0, identifiers defined as mutable are somewhat limited because you can’t mutate them within a subfunction, as this example illustrates:

let mutableY() =
    let mutable y = "One"
    printfn "Mutating: x = %s" y
    let f() =
        // this causes an error as
        // mutables can't be captured
        y <- "Two"
        printfn "x = %s" y
  f()
  printfn "x = %s" y

If you attempt to compile this program using a version of F# before 4.0, you get the following error message:

Prog.fs(35,16): error FS0191: The mutable variable 'y' is used in an invalid way. Mutable
 variables may not be captured by closures. Consider eliminating this use of mutation or
 using a heap-allocated mutable reference cell via 'ref' and '!'.

Fortunately, from F# 4.0 onwards this restriction has largely been eliminated, and you can normally use a mutable value in this kind of context.

If you are using earlier versions of F#, or in rare situations where a mutable still cannot be used, you can use a ref type. Use of this type is covered in a following section.

Defining Mutable Records

In Chapter 3, when you first met record types, I discussed how to update their fields using the with syntax. This is because record types are immutable by default. Alternatively, you can use the mutable keyword to allow you to update the fields in record types. (Other types of values can be declared as mutable; I discuss this in the next chapter.) You make record fields mutable by using the keyword mutable before the field in a record type. I should emphasize that this operation changes the contents of the record’s field, rather than changing the record itself.

// a record with a mutable field
type Couple = { Her: string; mutable Him: string }


// a create an instance of the record
let theCouple = { Her = "Elizabeth Taylor "; Him = "Nicky Hilton" }


// function to change the contents of
// the record over time
let changeCouple() =
    printfn "%A" theCouple
    theCouple.Him <- "Michael Wilding"
    printfn "%A" theCouple
    theCouple.Him <- "Michael Todd"
    printfn "%A" theCouple
    theCouple.Him <- "Eddie Fisher"
    printfn "%A" theCouple
    theCouple.Him <- "Richard Burton"
    printfn "%A" theCouple
    theCouple.Him <- "Richard Burton"
    printfn "%A" theCouple
    theCouple.Him <- "John Warner"
    printfn "%A" theCouple
    theCouple.Him <- "Larry Fortensky"
    printfn "%A" theCouple


// call the fucntion
changeCouple()

Executing the preceding code produces the following results:

{Her = "Elizabeth Taylor ";
 Him = "Nicky Hilton";}
{Her = "Elizabeth Taylor ";
 Him = "Michael Wilding";}
{Her = "Elizabeth Taylor ";
 Him = "Michael Todd";}
{Her = "Elizabeth Taylor ";
 Him = "Eddie Fisher";}
{Her = "Elizabeth Taylor ";
 Him = "Richard Burton";}
{Her = "Elizabeth Taylor ";
 Him = "Richard Burton";}
{Her = "Elizabeth Taylor ";
 Him = "John Warner";}
{Her = "Elizabeth Taylor ";
 Him = "Larry Fortensky";}

This example shows a mutable record in action. A type, couple, is defined where the field him is mutable, but the field her is not. Next, an instance of couple is initialized, after which you change the value of him many times, each time displaying the results. Note that the mutable keyword applies per field, so any attempt to update a field that is not mutable will result in a compile error. For example, the next example will fail on the second line:

theCouple.Her <- "Sybil Williams"
printfn "%A" theCouple

If you attempt to compile this program, you get the following error message:

prog.fs(2,4): error: FS0005: This field is not mutable

The Reference Type

The ref type is a simple way for a program to use mutable state, or values that change over time, in those few situations where simply using the mutable keyword isn’t permitted. From F# 4.0 onwards, these situations are rare, but in previous versions you weren’t allowed to mutate a mutable value within a “closure,” such as an inner function or a sequence generator. The ref type is a record type with a single mutable field that is defined in the F# libraries. Some operators are defined to make accessing and updating the field as straightforward as possible. F#’s definition of the ref type uses type parameterization, a concept introduced in the previous chapter. Thus, although the value of the ref type can be of any type, you cannot change the type of the value once you create an instance of the value.

Creating a new instance of the ref type is easy; you use the keyword ref, followed by whatever item represents the initial value of the ref. The next example shows the compiler’s output (using the –i option, which shows that the type of phrase is string ref, or a reference type that can only contain strings):

let phrase = ref "Inconsistency"
val phrase : string ref

This syntax is similar to defining a union type’s constructors, also shown in the previous chapter. The ref type has two built-in operators to access it: the exclamation point (!) provides access to the value of the reference type, and an operator composed of a colon followed by an equals sign (:=) enables you to update it. The ! operator always returns a value of the type that matches the ref type’s contents, known to the compiler thanks to type parameterization. The := operator has the type unit because it doesn’t return anything.

The next example shows how to use a ref type to total the contents of an array. (Of course, in practice the easiest way to do this is to use Array.sum in the Array module.) On the third line of totalArray, you see the creation of the ref type. In this case, it is initialized to hold the value 0. On the seventh line, the let binding after the array definition, you see the ref type being both accessed and updated. First, ! is used to access the value with the ref type; second, after it has been added to the current value held in the array, the value of the ref type is updated through the use of the := operator. Now the code will correctly print 6 to the console.

let totalArray () =
    // define an array literal
    let array = [| 1; 2; 3 |]
    // define a counter
    let total = ref 0
    // loop over the array
   for x in array do
       // keep a running total
       total := !total + x
    // print the total
    printfn "total: %i" !total


totalArray()

Executing the preceding code produces the following result:

 total: 6
Caution

If you are used to programming in one of the C family of programming languages, you should be careful here. When reading F# code, it is quite easy to misinterpret the ref type’s ! operator as a Boolean “not” operator. F# uses a function called not for Boolean “not” operations.

The ref type is a useful way to share mutable values between several functions. An identifier can be bound to a ref type defined in scope that is common to all functions that want to use the value; then the functions can use the value of the identifier as they like, changing it or merely reading it. Because you can pass around functions in F# as if they were values, the value follows the function everywhere it goes. This process is known as capturing a local or creating a closure.

The next example demonstrates this by defining three functions: inc, dec, and show, which all share a common ref type holding an integer. The functions inc, dec, and show are all defined in their own private scopes and then returned to the top level as a tuple, so they are visible everywhere. Note how n is not returned; it remains private, but inc, dec, and show are all still able to access n. This is a useful technique for controlling what operations can take place on mutable data.

// capture the inc, dec, and show funtions
  let inc, dec, show =
      // define the shared state
      let n = ref 0
      // a function to increment
      let inc () =
          n := !n + 1
      // a function to decrement
      let dec () =
          n := !n - 1
      // a function to show the current state
      let show () =
          printfn "%i" !n


    // return the functions to the top level
    inc, dec, show


// test the functions
inc()
inc()
dec()
show()

Executing the preceding code produces the following result:

1

From F# 4.0 onwards, the preceding code can be rewritten using a mutable value, like so:

  // this will work from F# 4.0
  let inc, dec, show =
      // define the shared state
      let mutable n = 0
      // a function to increment
      let inc () =
          n <- n + 1
      // a function to decrement
      let dec () =
          n <- n - 1
      // a function to show the current state
      let show () =
          printfn "%i" n


      // return the functions to the top level
      inc, dec, show


// test the functions
inc()
inc()
dec()
show()

Arrays

Arrays are a concept that most programmers are familiar with, as almost all programming languages have some sort of array type. The F# array type is based on the BCL System.Array type, so anyone who has used arrays in C# or Visual Basic will find the underlying concepts the same.

Arrays are a mutable collection type in F#; it’s useful to compare them to the immutable list type explained in Chapter 3. Arrays and lists are both collections, but the values within arrays are updatable, whereas the values in lists are not. One-dimensional arrays are sometimes referred to as vectors, and multidimensional arrays are sometimes called matrices. Array literals are defined by a sequence of items separated by semicolons (;) and delimited by an opening square bracket and a vertical bar ([|) and a closing bar and square bracket (|]). The syntax for referencing an array element is the name of the identifier of the array followed by a period (.) and then the index of the element in square brackets ([]). The syntax for retrieving the value of an element stops there. The syntax for setting the value of an element is the left arrow (<-) followed by the value to be assigned to the element.

The next example shows you how to read from and write to an array. First, you define an array, rhymeArray, and then you read all the members from it. Next, you insert new values into the array, and finally, you print out all the values you have.

// define an array literal
let rhymeArray =
    [| "Went to market";
       "Stayed home";
       "Had roast beef";
       "Had none" |]


// unpack the array into identifiers
let firstPiggy = rhymeArray.[0]
let secondPiggy = rhymeArray.[1]
let thirdPiggy = rhymeArray.[2]
let fourthPiggy = rhymeArray.[3]


// update elements of the array
rhymeArray.[0] <- "Wee,"
rhymeArray.[1] <- "wee,"
rhymeArray.[2] <- "wee,"
rhymeArray.[3] <- "all the way home"


// give a short name to the new line characters
let nl = System.Environment.NewLine


// print out the identifiers & array
printfn "%s%s%s%s%s%s%s"
    firstPiggy nl
    secondPiggy nl
    thirdPiggy nl
    fourthPiggy
printfn "%A" rhymeArray

When you execute this code, you see the following results:

Went to market
Stayed home
Had roast beef
Had none
[|"Wee,"; "wee,"; "wee,"; "all the way home"|]

Arrays, like lists, use type parameterization, so the type of the array’s contents makes up part of the array’s type. This is written as content type, followed by the array’s type. Thus, rhymeArray has the type string array, which you might also write like string[] or array<string>.

Multidimensional arrays in F# come in two, slightly different flavors: jagged and rectangular. As the name suggests, jagged arrays are arrays where the second dimension is not a regular shape; rather they are arrays whose contents happen to be other arrays, and the length of the inner arrays is not forced to be the same. In rectangular arrays, all inner arrays are of the same length; in fact, there is no concept of an inner array because the whole array is the same object. The method of getting and setting items in the two different types of arrays differs slightly.

For jagged arrays, you use the period followed by the index in parentheses, but you have to use this twice (one time for each dimension) because the first time you get back the inner array, and the second time you get the element within it.

The next example demonstrates a simple jagged array called jagged. You can access the array members in two different ways. The first inner array (at index 0) is assigned to the identifier singleDim, and then its first element is assigned to itemOne. On the fourth line, the first element of the second inner array is assigned to itemTwo, using one line of code.

// define a jagged array literal
let jagged = [| [| "one" |] ; [| "two" ; "three" |] |]


// unpack elements from the arrays
let singleDim = jagged.[0]
let itemOne = singleDim.[0]
let itemTwo = jagged.[1].[0]


// print some of the unpacked elements
printfn "%s %s" itemOne itemTwo

When you compile and execute the results of this example, you get the following result:

one two

To reference elements in rectangular arrays, use a period (.) followed by all the indexes in square brackets, separated by commas. Unlike jagged arrays, which are multidimensional but use the same ([||]) syntax as single-dimensional arrays, you must create rectangular arrays with the create function of the Array2D and Array3D modules, which support two- and three-dimensional arrays, respectively. This doesn’t mean rectangular arrays are limited to three dimensions because it’s possible to use the System.Array class to create rectangular arrays with more than three dimensions; however, you should consider such an approach carefully because adding extra dimensions can quickly lead to extremely large objects.

In the following example, you create a rectangular array, square, and then populate its elements with the integers 1, 2, 3, and 4:

// create a square array,
// initally populated with zeros
let square = Array2D.create 2 2 0


// populate the array
square.[0,0] <- 1
square.[0,1] <- 2
square.[1,0] <- 3
square.[1,1] <- 4


// print the array
printfn "%A" square

Now let’s look at the differences between jagged and rectangular arrays. First, you create a jagged array to represent Pascal’s Triangle. Next, you create a rectangular array that contains various number sequences that are hidden within pascalsTriangle.

// define Pascal's Triangle as an
// array literal
let pascalsTriangle =
    [| [|1|];
       [|1; 1|];
       [|1; 2; 1|];
       [|1; 3; 3; 1|];
       [|1; 4; 6; 4; 1|];
       [|1; 5; 10; 10; 5; 1|];
       [|1; 6; 15; 20; 15; 6; 1|];
       [|1; 7; 21; 35; 35; 21; 7; 1|];
       [|1; 8; 28; 56; 70; 56; 28; 8; 1|]; |]


// collect elements from the jagged array
// assigning them to a square array
let numbers =
    let length = (Array.length pascalsTriangle) in
    let temp = Array2D.create 3 length 0 in
    for index = 0 to length - 1 do
        let naturelIndex = index - 1 in
        if naturelIndex >= 0 then
            temp.[0, index] <- pascalsTriangle.[index].[naturelIndex]
      let triangularIndex = index - 2 in
      if triangularIndex >= 0 then
          temp.[1, index] <- pascalsTriangle.[index].[triangularIndex]
      let tetrahedralIndex = index - 3 in
      if tetrahedralIndex >= 0 then
          temp.[2, index] <- pascalsTriangle.[index].[tetrahedralIndex]
    done
    temp


// print the array
printfn "%A" numbers

When you compile and execute this code, you get the following results:

[|[|0; 1; 2; 3; 4; 5; 6; 7; 8|]; [|0; 0; 1; 3; 6; 10; 15; 21; 28|];
  [|0; 0; 0; 1; 4; 10; 20; 35; 56|]|]

The following results show the types displayed when you use the compiler’s –i switch:

val pascalsTriangle: int array array
val numbers: int [,]

As you might expect, jagged and rectangular arrays have different types. The type of a jagged array is the same as a single-dimensional array, except that it has an array per dimension, so the type of pascalsTriangle is int array array. Rectangular arrays use a notation more similar to C#. It begins with the name of the type of the array’s elements, and then includes square brackets ([]) with one comma for every dimension greater than 1, so the type of your two-dimensional numbers array is int[,].

Array Comprehensions

I introduced comprehension syntax for lists and sequences in Chapter 3. You can use a corresponding syntax to create arrays. The only difference between this and the list and sequence syntax is the characters that delimit the array. You use vertical bars surrounded by square brackets for arrays.

// an array of characters
let chars = [| '1' .. '9' |]


// an array of tuples of number, square
let squares =
    [| for x in 1 .. 9 -> x, x*x |]


// print out both arrays
printfn "%A" chars
printfn "%A" squares

Executing the preceding code produces the following results:

[|'1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9'|]
[|(1, 1); (2, 4); (3, 9); (4, 16); (5, 25); (6, 36); (7, 49); (8, 64); (9, 81)|]

Array Slicing

It’s often useful to pull out some range of the values in an array. This can be done using array slicing. A simple slice of an array can be taken like this:

let arr = [|1; 3; 5; 7; 11; 13|]
let middle = arr.[1..4] // [|3; 5; 7; 11|]

Omit the starting or ending index to start from the beginning or end of the input array, like so:

let start = arr.[..3] // [|1; 3; 5; 7|]
let tail = arr.[1..] // [|3; 5; 7; 11; 13|]

Slicing also works for multi-dimensional arrays, as in this simple implementation of part of a “Battleships” game:

let ocean = Array2D.create 100 100 0
// Create a ship:
for i in 3..6 do
    ocean.[i, 5] <- 1
// Pull out an area hit by a 'shell':
let hitArea = ocean.[2..5, 2..5]

If you want to include all of the elements in one of the dimensions, use a *:

// We can see a rectangular area by 'radar':
let radarArea = ocean.[3..4, *]

Note that from F# 4.0 onwards, the same slicing syntax can also be applied to F# lists.

Control Flow

Unlike the pseudo-control-flow syntax described in Chapter 3, F# does have some imperative control-flow constructs. In addition to the imperative use of if, there are also while and for loops.

The major difference from using the if expression in the imperative style—that is, using it with a function that returns the type unit—is that you aren’t forced to use an else, as the following example demonstrates:

if System.DateTime.Now.DayOfWeek = System.DayOfWeek.Sunday then
    printfn "Sunday Playlist: Lazy On A Sunday Afternoon - Queen"

Although it isn’t necessary to have an else expression if the if expression has the type unit, you can add one if necessary. This too must have the type unit, or the compiler will issue an error.

if System.DateTime.Now.DayOfWeek = System.DayOfWeek.Monday then
    printfn "Monday Playlist: Blue Monday - New Order"
else
printfn "Alt Playlist: Fell In Love With A Girl - White Stripes"

You can use whitespace to detect where an if expression ends. You indent the code that belongs to the if expression, and the if expression ends when it goes back to its original indentation. In the following example, the string "Tuesday Playlist: Ruby Tuesday - Rolling Stones" will be printed on a Tuesday, while "Everyday Playlist: Eight Days A Week - Beatles" will be printed every day of the week:

if System.DateTime.Now.                                                    DayOfWeek = System.DayOfWeek.Tuesday then
    printfn "Tuesday Playlist: Ruby Tuesday - Rolling Stones"
printfn "Everyday Playlist: Eight Days A Week - Beatles"

If you want multiple statements to be part of the if statement, you can give them the same indention, as shown in the following example, where both strings will be printed only on a Friday:

if System.DateTime.Now.DayOfWeek = System.DayOfWeek.Friday then
    printfn "Friday Playlist: Friday I'm In Love - The Cure"
    printfn "Friday Playlist: View From The Afternoon - Arctic Monkeys"

Most programmers are familiar with for loops because they are commonly found in imperative programming languages. In F#, for loops are overloaded, so a for loop can either enumerate a collection, behaving in a similar way to the foreach loop available in many programming languages, or it can specify an identifier that will be incremented by one for each iteration of the loop.

First, let’s look at using for to enumerate collections. In this case, the for loop performs an imperative action, one that returns the unit on each element in the collection. This is probably the most common imperative usage of for loops in F#. The syntax begins with the for keyword, followed by the identifier that will be bound to each item in the collection. Next comes the keyword in, followed by the collection, and then the keyword do. The code for processing each item in the collection comes next; you indent this to show that it belongs to the for loop. The following example demonstrates this syntax, enumerating an array of strings and printing each one:

// an array for words
let words = [| "Red"; "Lorry"; "Yellow"; "Lorry" |]


// use a for loop to print each element
for word in words do
    printfn "%s" word

Executing the preceding code produces the following results:

Red
Lorry
Yellow
Lorry

As you’ll see later in this chapter and in many examples throughout the book, this can be a convenient way to work with typed or untyped collections returned by .NET BCL methods.

The other usage of a for loop is to declare an identifier, whose scope is the for loop, that increases or decreases its value by 1 (or some other specified amount) after each iteration of the loop. The identifier is given a starting value and an end value, and the end value provides the condition for loop termination. F# follows this syntax. It starts with the keyword for, followed by the identifier that will hold the counter value; next comes an equals sign, followed by an expression for the initial counter value, the keyword to, and then an expression for the terminal value, and finally the keyword do. The body of the loop follows, normally in an indented scope. The for loop has the type unit, so the code that forms the body of the loop should have the type unit; otherwise, the compiler will issue a warning.

The next example demonstrates a common usage of a for loop: to enumerate all the values in an array. The identifier index will take on values starting at 0 and ending at 1 less than the length of the array. You can use this identifier as the index for the array.

// a Ryunosuke Akutagawa haiku array
let ryunosukeAkutagawa = [| "Green "; "frog,";
    "Is"; "your"; "body"; "also";
    "freshly"; "painted?" |]


// for loop over the array printing each element
for index = 0 to Array.length ryunosukeAkutagawa - 1 do
    printf "%s " ryunosukeAkutagawa.[index]

When you compile and execute this code, you get the following results:

Green frog, Is your body also freshly painted?

In a regular for loop, the initial value of the counter must always be less than the final value, and the value of the counter will increase as the loop continues. There is a variation on this, where you replace to with downto. In this case, the initial counter value must always be greater than the final value, and the counter will decrease as the loop continues. You can see how to use downto in this example:

// a Shuson Kato hiaku array (backwards)
let shusonKato = [| "watching."; "been"; "have";
    "children"; "three"; "my"; "realize"; "and";
    "ant"; "an"; "kill"; "I";
    |]


// loop over the array backwards printing each word
for index = Array.length shusonKato - 1 downto 0 do
    printf "%s " shusonKato.[index]

When you compile and execute this code, you get the following results:

I kill an ant and realize my three children have been watching.

An alternative (and commonly used) syntax for for loops uses range notation for the index value. You can have both upward- and downward-counting loops, and even loops that increment or decrement the index value by more than 1.

  // Count upwards:
  for i in 0..10 do
     printfn "%i green bottles" i
  // Count downwards:
  for i in 10..-1..0 do
     printfn "%i green bottles" i
  // Count upwards in tens
  for i in 0..10..100 do
     printfn "%i green bottles" i

The while loop is another familiar imperative language construct. It is an expression that creates a loop over a section of code until a Boolean expression evaluates to false. To create a while loop in F#, you use the keyword while followed by a Boolean expression that determines whether the loop should continue. As with for loops, you place the body of the loop after the keyword do, and the body should have the type unit; otherwise, the compiler will issue a warning. This code illustrates how to create a while loop:

// a Matsuo Basho hiaku in a mutable list
let mutable matsuoBasho = [ "An"; "old"; "pond!";
    "A"; "frog"; "jumps"; "in-";
    "The"; "sound"; "of"; "water" ]


while (List.length matsuoBasho > 0) do
    printf "%s " (List.head matsuoBasho)
    matsuoBasho <- List.tail matsuoBasho

This program enumerates the list, and the Boolean expression to terminate the loop is based on whether the list is empty. Within the body of the loop, you print the head of the list and then remove it, shortening the list on each iteration.

When you compile and execute this code, you get the following results:

An old pond! A frog jumps in- The sound of water

Calling Static Methods and Properties from .NET Libraries

One extremely useful feature of imperative programming in F# is the ability to use just about any library written in a .NET programming language, including the many methods and classes available as part of the BCL itself. I consider this to be imperative programming because libraries written in other languages make no guarantees about how state works inside them, so you can’t know whether a method you call has side effects.

A distinction should be made between calling libraries written in F# and libraries written in any other language. This is because libraries written in F# have metadata that describes extra details about the library, such as whether a method takes a tuple or whether its parameters can be curried. This metadata is specific to F#, and it is stored in a binary format as a resource to the generated assembly. This is largely why the Microsoft.FSharp.Reflection API is provided: to bridge the gap between F# and .NET metadata.

You use the same basic syntax when calling static or instance properties or methods. Method calls to a non-F# library must have their arguments separated by commas and surrounded by parentheses. (Remember, F# function calls usually use whitespace to separate arguments, and you need to use parentheses only to impose precedence.) Method calls to a non-F# library cannot be curried; in fact, methods from non-F# libraries behave as though they take a tuple of arguments. Despite this difference, calling a method from a non-F# library is straightforward. You start off by using static properties and methods, like so:

open System.IO
// test whether a file "test.txt" exist
if File.Exists("test.txt") then
    printfn "Text file "test.txt" is present"
else
    printfn "Text file "test.txt" does not exist"

This example calls a static method from the .NET Framework BCL. Calling a static method is almost identical to calling an F# function. You begin with the class name followed by a period (.) and then the name of the method; the only real difference is in the syntax for passing the arguments, which are surrounded by parentheses and separated by commas. You make a call to the System.IO.File class’s Exists method to test whether a file exists and print an appropriate message, depending on the result.

You can treat static methods from other .NET libraries as values in the same way that you can treat F# functions as values and pass them to other function as parameters. The following example shows how to pass the File.Exist method to the F# library function List.map:

open System.IO

// list of files to test
let files1 = [ "test1.txt"; "test2.txt"; "test3.txt" ]


// test if each file exists
let results1 = List.map File.Exists files1


// print the results
printfn "%A" results1

Because .NET methods behave as if they take tuples as arguments, you can also treat a method that has more than one argument as a value. Here you see how to apply the File.WriteAllBytes to a list of tuples; the tuples contain the file path (a string) and the desired file contents (an array of bytes):

open System.IO

// list of files names and desired contents
let files2 = [ "test1.bin", [| 0uy |];
               "test2.bin", [| 1uy |];
               "test3.bin", [| 1uy; 2uy |]]


// iterate over the list of files creating each one
List.iter File.WriteAllBytes files2

Often, you want to use the functionality of an existing .NET method, but you also want the ability to curry it. A common pattern in F# to achieve this is to import the .NET method function by writing a thin F# wrapper, as in the following example:

open System.IO

// import the File.Create function
let create size name =
    File.Create(name, size, FileOptions.Encrypted)


// list of files to be created
let names = [ "test1.bin"; "test2.bin"; "test3.bin" ]


// open the files create a list of streams
let streams = List.map (create 1024) names

Here you see how to import the File.Create; in this case, you use the overload that takes three parameters, but you expose only two of them as parameters: the buffer size (size) and the file name (name). Notice how you specify that the size parameter comes first. You do it this way because it’s more likely that you’ll want to create several files with the same buffer size than with the same name. In the final line of the listing, you apply the create function to a list of file names to create a list of file streams. You want each stream to be created with a buffer size of 1024 bytes, so you pass the literal 1024 to the create function, like so: (create 1024). This returns a new function, which is then used with the List.map function.

When using .NET methods with lots of arguments, it can sometimes be helpful to know the names of the arguments to help you keep track of what each argument is doing. F# lets you use named arguments, where you give the name of the argument, an equals sign, and then the value of the argument. The following example demonstrates this with an overload of File.Open() that takes four arguments:

open System.IO

// open a file using named arguments
let file = File.Open(path = "test.txt",
                        mode = FileMode.Append,
                        access = FileAccess.Write,
                        share = FileShare.None)


// close it!
file.Close()

Using Objects and Instance Members from .NET Libraries

Using classes from non-F# libraries is also straightforward. The syntax for instantiating an object consists of the keyword new, the name of the class you want to instantiate, and then constructor arguments separated by commas within parentheses. You can use the let keyword to bind an instance of a class to an identifier. Once associated with an identifier, the object behaves a lot like a record type; the object referred to cannot be changed, but its contents can. Also, if the identifier is not at the top level, then it can be redefined or hidden by an identifier of the same name in another scope. C# and Visual Basic programmers should find accessing fields, properties, events, and methods to be intuitive because the syntax is similar. To access any member, you use the identifier of the object followed by a period (.) and then the name of the member. Arguments to instance methods follow the same convention as for static methods, and they must be within parentheses and separated by commas. To retrieve the value of a property or field, you need only the name of member, and you set it using the left arrow (<-).

The following example demonstrates how to create a System.IO.FileInfo object and then use various members of the class to manipulate it in different ways. On the first line, you make the System.IO namespace available to F#. On the second, you create the FileInfo object, passing it the name of the file in which you’re interested. Next, you check whether the file exists using the Exists instance property. If it doesn’t exist, you create a new file using the CreateText() instance method and then set it to read-only using the Attributes instance property. Here, you use the use binding to clean up resources, by calling their Dispose method when they drop out of scope.

open System.IO
// create a FileInfo object
let file = new FileInfo("test.txt")


// test if the file exists,
// if not create a file
if not file.Exists then
    use stream = file.CreateText()
    stream.WriteLine("hello world")
    file.Attributes <- FileAttributes.ReadOnly


// print the full file name
printfn "%s" file.FullName

I explained this fully in Chapter 3. F# also allows you to set properties when constructing an object. It’s quite common to set object properties as part of the process of initially configuring the object. To set a property at construction time, you place the property name inside the constructor call, followed by an equals sign and then by the value for the property. Separate multiple properties with commas. The following is a variation on the previous example; it sets the ReadOnly attribute when the object is the constructor:

open System.IO
// file name to test
let filename = "test.txt"


// bind file to an option type, depending on whether
// the file exist or not
let file =
    if File.Exists(filename) then
      Some(new FileInfo(filename, Attributes = FileAttributes.ReadOnly))
  else
      None

Note that you need to test for the file’s existence to avoid a runtime exception when trying to set the Attributes property. F# allows you to set type parameters when calling a constructor because it is not always possible to infer the type parameter when making a constructor call. The type parameters are surrounded by angle brackets (<>) and separated by commas. The next example demonstrates how to set a type parameter when calling a constructor. You can create an instance of System.Collections.Generic.List, which you can use with integers only by setting its type parameter when you create it. In F#, System.Collections.Generic.List is called ResizeArray to avoid confusion with F# lists.

open System

// an integer list
let intList =
    let temp = new ResizeArray<int>()
    temp.AddRange([| 1; 2; 3 |]);
    temp


// print each int using the ForEach member method
intList.ForEach( fun i -> Console.WriteLine(i) )

Executing the preceding code produces the following results:

1
2
3

The previous example also demonstrates another nice feature of F# when interoperating with non-F# libraries. .NET APIs often use a .NET construct called delegates, which are conceptually a kind of function value. F# functions will automatically be converted to .NET delegate objects if their signatures match. You can see this on the last line, where an F# function is passed directly to a method that takes a .NET delegate type.

To keep methods as flexible as possible, you might prefer not to specify a type parameter when you import methods that take generic delegates or when you create a wrapper F# function around constructors for a non-F# library. You achieve this by using the underscore (_) in place of the type parameter, as in the first line of the next example (which also uses the forward operator, |>, which I explain in the “The |> Operator” section):

open System
// how to wrap a method that take a delegate with an F# function
let findIndex f arr = Array.FindIndex(arr, new Predicate<_>(f))


// define an array literal
let rhyme = [| "The"; "cat"; "sat"; "on"; "the"; "mat" |]


// print index of the first word ending in 'at'
printfn "First word ending in 'at' in the array: %i"
    (rhyme |> findIndex (fun w -> w.EndsWith("at")))

When you compile and execute this example, you get the following result:

First word ending in 'at' in the array: 1

Here you import the FindIndex method from the System.Array class, so you can use it in a curried style. If you had not explicitly created a delegate, the identifier f would have represented a predicate delegate rather than a function. This means all calls to findIndex would need to create a delegate object explicitly, which is not ideal. However, if you had specified a type when creating the Predicate delegate in the definition of findIndex, then you would have limited the use of the findIndex function to arrays of a specific type. Occasionally, this might be what you want to do, but that’s not usually the case. By using the underscore, you avoid having to specify a type for the findIndex function, while keeping it nice and flexible.

Using Indexers from .NET Libraries

Indexers are a .NET concept that is designed to make a collection class look more like an array. Under the hood, an indexer is a special property that is always called Item and has one or more parameters. It is important that you have easy access to an indexer property because many classes within the BCL have indexers.

F# offers two different syntaxes for accessing properties. You can explicitly use the Item property, or you can use an array-like syntax, with brackets instead of parentheses around the index, like so:

open System.Collections.Generic

// create a ResizeArray
let stringList =
    let temp = new ResizeArray<string>()
    temp.AddRange([| "one"; "two"; "three" |]);
    temp


// unpack items from the resize array
let itemOne = stringList.Item(0)
let itemTwo = stringList.[1]


// print the unpacked items
printfn "%s %s" itemOne itemTwo

This example associates the strings "one" and "two" with the identifiers itemOne and itemTwo, respectively. The association of "one" with itemOne demonstrates how to use the Item property explicitly. The association of "two" with itemTwo uses the bracket syntax.

Note

This example also demonstrates a common pattern in F#. Note how you want to create the identifier stringList as an object from a non-F# library, yet at the same time initialize it to a certain state. To do this, you assign the object to a temporary identifier and then call an instance member on the object to manipulate its state. Finally, you return the temporary identifier, so it becomes the value of stringList. In this way, you keep the object creation and initialization logic close together.

Working with Events from .NET Libraries

Events are special properties of objects that allow you to attach functions to them. The functions you attach to events are sometimes referred to as handlers. When the event occurs, it executes all the functions that have been attached to it. For example, you might create a Button object that exposes a Click event, which occurs when a user clicks the button. This would mean that any functions that have been attached to the button’s Click event would execute when the button is clicked. This is extremely useful because it’s common to need notifications of what the user has done when creating user interfaces.

Adding a handler to an event is fairly straightforward. Each event exposes a method called Add, and the handling function is passed to this method. Events come from non-F# libraries, so the Add method follows the convention that its arguments must be surrounded by parentheses. In F#, it is common to place the handler function inside the Add method itself, using F#’s anonymous function feature. The type of the handler function must match the type of the Add method’s parameter, and this parameter has the type 'a -> unit. This means that for events exposed by objects in the BCL, the parameter of the Add method will have a type similar to EventArgs -> Unit.

The next example shows the creation of a Timer object and a function being added to the timer’s Elapsed event. A Timer object is an object that fires its Elapsed event at regular intervals. In this case, the handler prints a message to the user. Notice how you do not care about the argument that will be passed to the handler function, so you ignore it using the underscore.

#if INTERACTIVE
#else
module HandlersDemo
#endif


open System.Timers

let timedMessages() =
    // define the timer
    let timer = new Timer(Interval = 3000.0,
                          Enabled = true)


    // a counter to hold the current message
    let mutable messageNo = 0


    // the messages to be shown
    let messages = [ "bet"; "this"; "gets";
                     "really"; "annoying";
                     "very"; "quickly" ]


    // add an event to the timer
    timer.Elapsed.Add(fun _ ->
        // print a message
        printfn "%s" messages.[messageNo]
        messageNo <- messageNo + 1
        if messageNo = messages.Length then
            timer.Enabled <- false)


timedMessages()

To run this example, select all lines and send them to F# Interactive. You should see a series of words appear in the interactive console, one every three seconds. The words will stop when the last one in the array has been printed, because at that point the code sets the Enabled property of the timer to false.

> bet
this
gets
really
annoying
very
quickly
>

It is also possible to remove handlers from events. To do this, you must keep the function you will add to the event in scope, so you can later pass it to the event’s RemoveHandler method. The RemoveHandler method accepts a delegate, which is an object that wraps a regular .NET method to allow it to be passed around like a value. This means the handler function must be given to the event already wrapped in a delegate and you must therefore use the event’s AddHandler (or RemoveHandler) method instead of its Add (or Remove) method. Creating a delegate in F# is straightforward. You simply call the delegate’s constructor, the same way you call any constructor for an object from any non-F# library, passing it the function that delegate should wrap:

#if INTERACTIVE
#else
module HandlersDemo
#endif


open System.Timers

let timedMessagesViaDelegate() =
    // define the timer
    let timer = new Timer(Interval = 3000.0,
                          Enabled = true)


    // a counter to hold the current message number
    let mutable messageNo = 0


    // the messages to be shown
    let messages = [ "bet"; "this"; "gets";
                     "really"; "annoying";
                     "very"; "quickly" ]


    // function to print a message
    let printMessage = fun _ _ ->
        // print a message
        printfn "%s" messages.[messageNo]
        messageNo <- (messageNo + 1) % messages.Length


    // wrap the function in a delegate
    let del = new ElapsedEventHandler(printMessage)


    // add the delegate to the timer
    timer.Elapsed.AddHandler(del) |> ignore


    // return the time and the delegate so we can
    // remove one from the other later
    (timer, del)


// Run this first:
let timer, del = timedMessagesViaDelegate()


// Run this later:
timer.Elapsed.RemoveHandler(del)

To run this example, select all lines other than the last and send to F# Interactive. You should see a series of words appear in the interactive console, one every three seconds.

> bet
this
gets
really
annoying
very

To stop the flow of words, send the last line of the example to F# Interactive.

The way this example works is very similar to the previous one. The difference is that you wrap the function to be called when the timer fires in a delegate, by passing it to the constructor of ElapsedEventHandler. Then you add the delegate to the timer via AddHandler (instead of Add) and return both the timer and the delegate (as a tuple) to the caller. This means that the timer and the delegate are available later so that the handler can be removed from the timer via RemoveHandler, as in the last line of the example.

Pattern Matching over .NET Types

As you saw in Chapter 3, pattern matching is a powerful feature of F#. Pattern matching allows a programmer to specify that different computations are executed depending on the value being matched against. F# has a construct that allows pattern matching over .NET types. The rule to match a .NET type is formed with a colon and question mark operator (:?) followed by the name of the .NET type you want to match. Because it is impossible to have an exhaustive list of .NET types, you must always provide a default rule when pattern matching over .NET types.

// a list of objects
let simpleList = [ box 1; box 2.0; box "three" ]


// a function that pattern matches over the
// type of the object it is passed
let recognizeType (item : obj) =
    match item with
    | :? System.Int32 -> printfn "An integer"
    | :? System.Double -> printfn "A double"
    | :? System.String -> printfn "A string"
    | _ -> printfn "Unknown type"


// iterate over the list of objects
List.iter recognizeType simpleList

Executing the preceding code produces the following results:

An integer
A double
A string

This example shows a function named recognizeType that is designed to recognize three of the .NET basic types via pattern matching. This function is then applied to a list. This function has a couple of noteworthy details. First, the function takes an argument of the type obj, and you need to use a type annotation to make sure it does. If you didn’t use the type annotation, the compiler would infer that the function can take any type and would use type 'a. This would be a problem because you cannot use pattern matching of this kind over F#’s types, only over .NET types. Second, the function’s default case uses the underscore to ignore the value.

Once you recognize that a value is of a certain type, it’s common to want to do something with that value. To use the value on the right side of a rule, you can use the as keyword followed by an identifier. You can see this in the next example, where you rewrite recognizeType to include the value in the message that is printed when a type is recognized:

// list of objects
let anotherList = [ box "one"; box 2; box 3.0 ]


// pattern match and print value
let recognizeAndPrintType (item : obj) =
    match item with
    | :? System.Int32 as x -> printfn "An integer: %i" x
    | :? System.Double as x -> printfn "A double: %f" x
    | :? System.String as x -> printfn "A string: %s" x
    | x -> printfn "An object: %A" x


// interate over the list pattern matching each item
List.iter recognizeAndPrintType anotherList

When you compile and execute this example, you get the following results:

A string: one
An integer: 2
A double: 3.000000

Notice how you use an identifier for a final default rule. You don’t need to match it to a type because you already know it will be of the type obj, as the value being matched over is already of the type obj.

Pattern matching over .NET types is also useful for handling exceptions thrown by .NET methods. You form the pattern match rules in the same way, except you use them with the try … with construct instead of the match construct. The next example shows how to match and catch two .NET exceptions. You match over the exceptions thrown and then print a different message to the console depending on the type of exception thrown.

try
    // look at current time and raise an exception
    // based on whether the second is a multiple of 3
    if System.DateTime.Now.Second % 3 = 0 then
       raise (new System.Exception())
    else
       raise (new System.ApplicationException())
with
| :? System.ApplicationException ->
    // this will handle "ApplicationException" case
    printfn "A second that was not a multiple of 3"
| _ ->
    // this will handle all other exceptions
    printfn "A second that was a multiple of 3"

The | > Operator

You met the pipe-forward operator (|>) in the Function Application section in Chapter 3. This operator allows you to pass a value to a function, reversing the order that the function and parameter would normally appear in the source file. As a quick reminder, the following example shows the operator’s definition and usage:

// the definition of the pipe-forward operator
let (|>) x f = f x


// pipe the parameter 0.5 to the sin function
let result = 0.5 |> System.Math.Sin

This technique proves especially useful when working with .NET libraries because it helps the compiler infer the correct types for a function’s parameters, without the need for explicit type annotations.

To understand why this operator is useful, it is helpful to probe a little deeper into how left-to-right type inference works. Consider the following simple example, where you define a list of integers, called intList, of the type int list, and then pass this list as the second argument to the library function List.iter. The first argument to List.iter is a function of the type int -> unit.

let intList = [ 1; 2; 3 ]
    // val printInt: int list


let printInt = printf "%i"
    // val printInt: int -> unit


List.iter printInt intList

Now you need to understand how these expressions in the program were assigned their types. The compiler started at the top of the input file, found the identifier intList, and inferred its type from the literal that is bound to it. Then it found the identifier printInt and inferred its type to be int -> unit because this is the type of the function returned from the call to the printfn function. Next, it found the function List.iter and knew that its type is ('a -> unit) -> 'a list -> unit. Because it has a generic or undetermined type 'a within it, the compiler examines the next identifier to the right, in this case the function printInt. This function has the type int -> unit, so the compiler infers that the type of the generic parameter 'a is int, which means the list passed to the function must be of the type int list.

So it is the type of the function that determines what the type of the list must be. However, it is often useful to have the type of the function inferred from the type of the list that it will be applied to. This is especially true when working with .NET types, as it allows you to access their members without a type annotation. The pipe forward operator lets you do this by allowing you to place the list before the function that operates on it. Consider the following example:

open System

// a date list
let importantDates = [ new DateTime(1066,10,14);
                       new DateTime(1999,01,01);
                       new DateTime(2999,12,31) ]


// printing function
let printInt = printf "%i "


// case 1: type annotation required
List.iter (fun (d: DateTime) -> printInt d.Year) importantDates


// case 2: no type annotation required
importantDates |> List.iter (fun d -> printInt d.Year)

Here you have two ways of printing the year from a list of dates. In the first case, you need to add a type annotation to access the methods and properties on the DateTime structure. The type annotation is required because the compiler has not yet encountered the importantDates, so it has no information it can use to infer the type of the parameter d of the anonymous function. In the second case, the compiler infers automatically that d is of the type DateTime because it has encountered the importantDates list already, which means it has enough information to infer the type of d.

The pipe-forward operator also proves useful when trying to chain functions together–that is, when one function operates on the result of another. Consider the next example, where you obtain a list of all the .NET assemblies in memory and then process this list until you end up with a list of all the .NET methods in memory. As each function operates on the result of the previous function, the forward operator is used to show the results being piped or passed forward to the next function. You don’t need to declare intermediate variables to hold the results of a function.

// grab a list of all methods in memory
let methods = System.AppDomain.CurrentDomain.GetAssemblies()
               |> List.ofArray
               |> List.map ( fun assm -> assm.GetTypes() )
               |> Array.concat
               |> List.ofArray
               |> List.map ( fun t -> t.GetMethods() )
               |> Array.concat


// print the list
printfn "%A" methods
Note

This example will take a few moments to run, especially on Linux.

You’ll find this a useful technique, and it will crop up now and again throughout the rest of the book.

Summary

In this chapter, you learned about the imperative features of F#. Combining this information with the functional features covered in Chapter 3 gives you a full range of techniques to attack any computing problem. F# allows you to choose techniques from the appropriate paradigm and combine them whenever necessary. In the next chapter, you’ll see how F# supports the third major programming paradigm, object-oriented programming.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.109.8