Chapter 4. Imperative Programming

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've already seen 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, which is handy when dealing with .NET libraries.

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, or 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:

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 that was not a function. 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 to using the tool tips that are available in visual studio or by compiling it using F# interactive. Alternatively, you can compiler's fsc –i switch; the results of this are 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 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—simply 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 that returns unit:

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

You begin by defining a function getShorty that returns a string. Now imagine, for whatever reason, 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 it has been wrapped into a function, ignore, that 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 pass-forward operator to pass the result of getShorty() to the ignore function. See Chapter 3 for more information about the pass-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 rebound, 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 next 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

Identifiers defined as mutable are somewhat limited because you can't use 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, you get the following error message:

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

As the error messages says, this is why the ref type, a special type of mutable record, has been made available: to handle mutable variables that need to be shared among several functions. I discuss mutable records in the next section and the ref type in the section after that.

Defining Mutable Record Types

In Chapter 3, when you first met record types, I discussed how to update their fields. This is because record types are immutable by default. F# provides special syntax to allow you to update the fields in record types. You do this 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. I should 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 ref Type

The ref type is a simple way for a program to use mutable state, or values that change over time. 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 value of 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. 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
        // kep a running total
        total := !total + x
    // print the total
    printfn "total: %i" !total

totalArray()

Executing the preceding code produces the following result:

total: 6

Warning

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:

// capute 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

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 that 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 arrays have a couple a couple of properties that make them quite different from lists. The values within arrays are updatable, whereas lists are not, and lists can grow dynamically, whereas arrays cannot. One-dimensional arrays are sometimes referred to as vectors, and multidimensional arrays are sometimes called matrices. Arrays 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 compile 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 this: 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 Array2 and Array3 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 next 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 our 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 functional-style 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)|]

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 next 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 next 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, then you can give them the same indention, as shown in the next 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 indented 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 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. The code that forms the body of the for loop comes after this, sandwiched between the keywords do and done. 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.

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 changes 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 between the keywords do and done, 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 list reference
let matsuoBasho = ref [ "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 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:

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. In the next example, you see how you can 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 |]]

// iterator 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 files names to create a list of files streams. You want each stream to be created with a buffer size of 1024 bytes, so 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, 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 should 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 next 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 explain 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, especially in WinForms programming (see Chapter 8 for more information about WinForms). To set a property at construction time, you place the property name inside the constructor, 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 can you 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>() in
    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 (the following example 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 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:

open System.Collections.Generic

// create a ResizeArray
let stringList =
    let temp = new ResizeArray<string>() in
    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 hander to an event is fairly straightforward. Each event exposes a method called Add, and the handling event 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 shows a message box displaying a notice 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:

open System.Windows.Forms
open System.Timers

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

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

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

    // add an event to the timer
    temp.Elapsed.Add(fun _ ->
        // show the message box
        MessageBox.Show(List.nth messages !messageNo) |> ignore
        // update the message counter
        messageNo := (!messageNo + 1) % (List.length messages))

    // return the timer to the top level
    temp

// print a message then wait for a user action
printfn "Whack the return to finish!"
System.Console.ReadLine() |> ignore
timer.Enabled <- false

Note

If you want to compile this program, you need to add a reference to the System.Windows.Forms.dll assemblies. This gives you access to the System.Windows.Forms namespace.

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; you can 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 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:

open System
open System.Windows.Forms

// define a form
let form =
    // the temporary form defintion
    let temp = new Form(Text = "Events example")

    // define an event handler
    let stuff _ _ = MessageBox.Show("This is "Doing Stuff"") |> ignore
    let stuffHandler = new EventHandler(stuff)

    // define a button and the event handler
    let event = new Button(Text = "Do Stuff", Left = 8, Top = 40, Width = 80)
    event.Click.AddHandler(stuffHandler)


    // label to show the event status
    let label = new Label(Top = 8, Left = 96)

    // bool to hold the event status and function
    // to print the event status to the label
    let eventAdded = ref true
    let setText b = label.Text <- (Printf.sprintf "Event is on: %b" !b)
    setText eventAdded

    // define a second button and it's click event handler
    let toggle = new Button(Text = "Toggle Event",
                            Left = 8, Top = 8, Width = 80)
    toggle.Click.Add(fun _ ->
        if !eventAdded then
            event.Click.RemoveHandler(stuffHandler)
        else
            event.Click.AddHandler(stuffHandler)
        eventAdded := not !eventAdded
        setText eventAdded)

    // add the controls to the form
    let dc c = (c :> Control)
    temp.Controls.AddRange([| dc toggle; dc event; dc label; |])
// return the form to the top level
    temp

// start the event loop and show the form
do Application.Run(form)

This example shows you how to create a simple WinForm in F#. Events are synonymous with user-interface programming, so I thought it would be good to show an example event of events being used in this context. Near the beginning of the example, you create a delegate, stuffHandler, which is then added to the Click event on the button event. Later you add a handler directly to the toggle button's Click event, which adds or removes the handler from the button's event.

Warning

The previous sample will not work in the F# interactive console, fsi, because of the call to Application.Run. Users of fsi should replace this with form.Visible <- true;;.

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, 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, but 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 try ... match construct. The next example shows you 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 have already met the pipe-forward operator (|>) in the composing functions sections 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 example that follows 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 right-to-left 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 that 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 type, 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 what the type of the parameter d of the anonymous function is. 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

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