Chapter 5. Object-Oriented Programming

Object-oriented programming is the third major programming paradigm. There has been a tendency to try and show that the function paradigm and the object-oriented paradigm as competing, but I believe them to be complementary techniques that work well together, which I will try to demonstrate in this chapter. At its heart, object-oriented programming has a few simple ideas, sometimes referred to as the tenets of object-oriented programming: encapsulation, polymorphism, and inheritance.

Possibly the most important tenet is encapsulation, the idea that the implementations and state should be encapsulated, or hidden behind well-defined boundaries. This makes the structure of a program easier to manage. In F#, you hide things by using signatures for modules and type definitions, as well as by simply defining them locally to an expression or class construction (you'll see examples of both in this chapter).

The second tenet, polymorphism, is the idea that you can implement abstract entities in multiple ways. You've met a number of simple abstract entities already, such as function types. A function type is abstract because you can implement a function with a specific type in many different ways; for example, you can implement the function type int -> int as a function that increments the given parameter, a function that decrements the parameter, or any one of millions of mathematical sequences. You can also build other abstract entities out of existing abstract components, such as the interface types defined in the .NET BCL. You can also model more sophisticated abstract entities using user-defined interface types. Interface types have the advantage that you can arrange them hierarchically; this is called interface inheritance. For example, the .NET BCL includes a hierarchical classification of collection types, available in the System.Collections and System.Collections.Generic namespaces.

In OOP, you can sometimes arrange implementation fragments hierarchically. This is called implementation inheritance, and it tends to be less important in F# programming because of the flexibility that functional programming provides for defining and sharing implementation fragments. However, it is significant for domains such as graphical user interface (GUI) programming.

While the tenets of object-oriented programming are import, object-oriented programming has also become synonymous with organizing your code around the values of the system nouns and then providing operations on those values as members, functions, or methods that operate on this value. This is often as simple as taking a function written in the style where the function is applied to a value (such as String.length s) and rewriting it using the dot notation (such as s.Length). This simple act can often make your code a good deal clearer. You'll see in this chapter how F# allows you to attach members to any of its types, not just its classes, enabling you to organize all your code in an object-oriented style if you wish.

F# provides a rich object-oriented programming model that allows you to create classes, interfaces, and objects that behave similarly to those created by C# and VB.NET. Perhaps more importantly, the classes you create in F# are indistinguishable from those that are created in other languages when packaged in a library and viewed by a user of that library. However, object-oriented programming is more than simply defining objects, as you'll see when you start looking at how you can program in an object-oriented style using F# native types.

Records As Objects

It is possible to use the record types you met in Chapter 3 to simulate object-like behavior. This is because records can have fields that are functions, which you can use to simulate an object's methods. While this technique does have some limitations compared to using F# classes, it also has some advantages. Only the function's type (or as some prefer, its signature) is given in the record definition, so you can easily swap the implementation without having to define a derived class, as you would in object-oriented programming. I discuss defining new implementations of objects in greater detail in the "Object Expressions" and "Inheritance" section later in this chapter.

Let's take a look at a simple example of using records as objects. The next example defines a type, Shape, that has two members. The first member, Reposition, is a function type that moves the shape; and the second member, Draw, draws the shape. You use the function makeShape to create a new instance of the shape type. The makeShape function implements the reposition functionality for you; it does this by accepting the initPos parameter, which is then stored in a mutable ref cell and updated when the reposition function is called. This means the position of the shape is encapsulated, accessible only through the reposition member. Hiding values in this way is a common technique in F# programming:

open System.Drawing

// a Shape record that will act as our object
type Shape =
    { Reposition: Point -> unit;
      Draw: unit -> unit }

// create a new instance of Shape
let makeShape initPos draw =
    // currPos is the internal state of the object
    let currPos = ref initPos
    { Reposition =
        // the Reposition member updates the internal state
        (fun newPos -> currPos := newPos);
      Draw =
        // draw the shape passing the current position
        // to given draw function
        (fun () -> draw !currPos); }

// "draws" a shape, prints out the shapes name and position
let draw shape (pos: Point) =
    printfn "%s, with x = %i and y = %i"
        shape pos.X pos.Y

// creates a new circle shape
let circle initPos =
    makeShape initPos (draw "Circle")
// creates a new square shape
let square initPos =
    makeShape initPos (draw "Square")

// list of shapes in their inital positions
let shapes =
    [ circle (new Point (10,10));
      square (new Point (30,30)) ]

// draw all the shapes
let drawShapes() =
    shapes |> List.iter (fun s -> s.Draw())

let main() =
    drawShapes() // draw the shapes
    // move all the shapes
    shapes |> List.iter (fun s -> s.Reposition (new Point (40,40)))
    drawShapes() // draw the shapes

// start the program
do main()
Circle, with x = 10 and y = 10
Square, with x = 30 and y = 30
Circle, with x = 40 and y = 40
Square, with x = 40 and y = 40

This example might seem trivial, but you can go quite a long way with this technique. The next example takes things to their natural conclusion, drawing the shapes on a form:

open System
open System.Drawing
open System.Windows.Forms

// a Shape record that will act as our object
type Shape =
    { Reposition: Point -> unit;
      Draw : Graphics -> unit }

// create a new instance of Shape
let movingShape initPos draw =
    // currPos is the internal state of the object
    let currPos = ref initPos in
{ Reposition =
        // the Reposition member updates the internal state
        (fun newPos -> currPos := newPos);
      Draw =
        // draw the shape passing the current position
        // and graphics object to given draw function
        (fun g -> draw !currPos g); }

// create a new circle Shape
let movingCircle initPos diam =
    movingShape initPos (fun pos g ->
        g.DrawEllipse(Pens.Blue,pos.X,pos.Y,diam,diam))

// create a new square Shape
let movingSquare initPos size =
    movingShape initPos (fun pos g ->
    g.DrawRectangle(Pens.Blue,pos.X,pos.Y,size,size) )

// list of shapes in their inital positions
let shapes =
    [ movingCircle (new Point (10,10)) 20;
      movingSquare (new Point (30,30)) 20;
      movingCircle (new Point (20,20)) 20;
      movingCircle (new Point (40,40)) 20; ]

// create the form to show the items
let mainForm =
    let form = new Form()
    let rand = new Random()
    // add an event handler to draw the shapes
    form.Paint.Add(fun e ->
        shapes |> List.iter (fun s ->
        s.Draw e.Graphics))
    // add an event handler to move the shapes
    // when the user clicks the form
    form.Click.Add(fun e ->
        shapes |> List.iter (fun s ->
        s.Reposition(new Point(rand.Next(form.Width),
                               rand.Next(form.Height)))
        form.Invalidate()))
    form

// Show the form and start the event loop
[<STAThread>]
do Application.Run(mainForm)

This application produces a GUI, as shown in Figure 5-1.

Drawing shapes using records to simulate objects

Figure 5.1. Drawing shapes using records to simulate objects

Again, you define a Shape record type that has the members Reposition and Draw. Next, you define the functions makeCircle and makeSquare to create different kinds of shapes, using them to define a list of Shape records. Finally, you define the form that will hold your records. Here you must do a bit more work than perhaps you would like. Because you don't use inheritance, the BCL's System.Winows.Forms.Form doesn't know anything about your shape objects, and you must iterate though the list, explicitly drawing each shape. This is quite simple to do, and it takes only three lines of code to you add an event handler to mainForm's Paint event:

temp.Paint.Add(
    fun e ->
        List.iter (fun s -> s.draw e.Graphics) shapes);

This example shows how you can quickly create multifunctional records without having to worry about any unwanted features you might also be inheriting. In the next section, you'll look at how you can represent operations on these objects in a more natural way: by adding members to F# types.

F# Types with Members

It is possible to add functions to both F#'s record and union types. You can call a function added to a record or union type using dot notation, just as you can a member of a class from a library not written in F#. It is also proves useful when you want to expose types you define in F# to other .NET languages. (I discuss this in more detail in Chapter 13.) Many programmers prefer to see function calls made on an instance value, and this technique provides a nice way of doing this for all F# types.

The syntax for defining an F# record or union type with members is the same as the syntax you learned in Chapter 3, except here it includes member definitions that always come at the end, after the with keyword. The definition of the members themselves start with the keyword member, followed by an identifier that represents the parameter of the type the member is being attached to, followed by a dot, the function name, and then any other parameters the function takes. After this comes an equals sign followed by the function definition, which can be any F# expression.

The following example defines a record type, Point. It has two fields, Left and Top; and a member function, Swap. The function Swap is a simple function that creates a new point with the values of Left and Top swapped over. Note how you use the x parameter, given before the function name Swap, within the function definition to access the record's other members:

// A point type
type Point =
    { Top: int;
      Left: int }
    with
        // the swap member creates a new point
        // with the left/top coords reveresed
        member x.Swap() =
            { Top = x.Left;
              Left = x.Top }

// create a new point
let myPoint =
    { Top = 3;
      Left = 7 }

let main() =
    // print the inital point
    printfn "%A" myPoint
    // create a new point with the coords swapped
    let nextPoint = myPoint.Swap()
    // print the new point
    printfn "%A" nextPoint

// start the app
do main()

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

{Top = 3;
 Left = 7;}
{Top = 7;
 Left = 3;}

You might have noticed the x parameter in the definition of the function Swap:

member x.Swap() =
    { Top = x.Left;
      Left = x.Top }

This is the parameter that represents the object on which the function is being called. Now look at the case where you call a function on a value:

let nextPoint = myPoint.Swap()

The value you call the function on is passed to the function as an argument. This is logical when you think about it because the function needs to be able to access the fields and methods of the value on which you call it. Some OO languages use a specific keyword for this, such as this or Me, but F# lets you choose the name of this parameter by specifying a name for it after the keyword member—x, in this case.

Union types can have member functions, too. You define them in the same way that you define record types. The next example shows a union type, DrinkAmount, that has a function added to it:

// a type representing the amount of a specific drink
type DrinkAmount =
    | Coffee of int
    | Tea of int
    | Water of int
    with
        // get a string representation of the value
        override x.ToString() =
            match x with
            | Coffee x -> Printf.sprintf "Coffee: %i" x
            | Tea x -> Printf.sprintf "Tea: %i" x
            | Water x -> Printf.sprintf "Water: %i" x

// create a new instance of DrinkAmount
let t = Tea 2

// print out the string
printfn "%s" (t.ToString())

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

Tea: 2

Note how this uses the keyword override in place of the keyword member. This has the effect of replacing, or overriding, an existing function of the base type. This is not that common a practice with function members associated with F# types because only four methods are available to be overridden: ToString, Equals, GetHashCode, and Finalize. Every .NET type inherits these from System.Object. Because of the way some of these methods interact with the CLR, the only one I recommend overriding is ToString. Only four methods are available for overriding because record and union types can't act as base or derived classes, so you cannot inherit methods to override (except from System.Object).

Object Expressions

Object expressions are at the heart of succinct object-oriented programming in F#. They provide a concise syntax to create an object that inherits from an existing type. This is useful if you want to provide a short implementation of an abstract class or interface, or if you want to tweak an existing class definition. An object expression allows you to provide an implementation of a class or interface while at the same time creating a new instance of it.

You surround the definition of an object expression with braces. You put the name of the class or interfaces at the beginning. You must follow the name of a class with a pair of parentheses that can have any values passed to the constructor between them. Interface names need nothing after them, though both class names and interface names can have a type parameter following them; you need to surround this type parameter with angled brackets. Next, you include the keyword with and the definition of the methods of the class or interfaces you want to implement. You declare these methods just as you declare members on records and union types (see the previous section for more information on this). You declare each new method using the keywords member or override, followed by the instance parameter, a dot, and the method name. The name of the method must be the same as the name of a virtual or abstract method in the class or interface definition, and its parameters must be surrounded by parentheses and separated by commas, just as .NET methods must be (unless the method has one parameter, in which case you can get away with excluding the parentheses). Ordinarily you don't need to give type annotations; however, if the base class contains several overloads for a method, then you might have to give type annotations. You include an equals sign after the name of a method and its parameters, followed by the implementation of the method's body, which is an F# expression that must match the return value of the method:

open System
open System.Collections.Generic

// a comparer that will compare string in there reversed order
let comparer =
    { new IComparer<string>
        with
            member x.Compare(s1, s2) =
                // function to reverse a string
                let rev (s: String) =
                    new String(Array.rev (s.ToCharArray()))
                // reverse 1st string
                let reversed = rev s1
                // compare reversed string to 2nd strings reversed
                reversed.CompareTo(rev s2) }

// Eurovision winners in a random order
let winners =
    [| "Sandie Shaw"; "Bucks Fizz"; "Dana International" ;
"Abba"; "Lordi" |]

// print the winners
printfn "%A" winners
// sort the winners
Array.Sort(winners, comparer)
// print the winners again
printfn "%A" winners

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

[|"Sandie Shaw"; "Bucks Fizz"; "Dana International"; "Abba"; "Lordi"|]
[|"Abba"; "Lordi"; "Dana International"; "Sandie Shaw"; "Bucks Fizz"|]

This short snippet shows how to implement the IComparer interface. This is an interface with one method, Compare, that takes two parameters and returns an integer that represents the result of the parameter comparison. It accepts one type parameter; in this case, you pass it a string. You can see this on the second line of the definition of the identifier comparer. Next, you include the definition of the method body, which in this case compares reversed versions of the string parameters. Finally, you use the comparer by defining an array and then sorting, displaying the before and after results in the console.

It is possible to implement multiple interfaces or a class and several other interfaces within one object expression. It's also possible to attach an interface to a pre-existing class without altering any of the class methods. However, it is not possible to implement more than one class within an object expression, basically because neither F# nor the CLR allow multiple inheritance of classes. When you implement a class and an interface, the class must always come first in the expression. Regardless, the implementation of any other interfaces after the first interface or class must come after the definitions of all the methods of the first interface or class. You prefix the name of the interface with the keyword interface and follow that with the keyword with. The definition of the methods is the same as for the first interface or class. If you don't change any methods on the class, then you don't use the keyword with:

open System
open System.Drawing
open System.Windows.Forms

// create a new instance of a number control
let makeNumberControl (n: int) =
    { new TextBox(Tag = n, Width = 32, Height = 16, Text = n.ToString())
          // implement the IComparable interface so the controls
          // can be compared
interface IComparable with
              member x.CompareTo(other) =
                  let otherControl = other :?> Control in
                  let n1 = otherControl.Tag :?> int in
                  n.CompareTo(n1) }

// a sorted array of the numbered controls
let numbers =
    // initalize the collection
    let temp = new ResizeArray<Control>()
    // initalize the random number generator
    let rand = new Random()
    // add the controls collection
    for index = 1 to 10 do
        temp.Add(makeNumberControl (rand.Next(100)))
    // sort the collection
    temp.Sort()
    // layout the controls correctly
    let height = ref 0
    temp |> Seq.iter
        (fun c ->
            c.Top <- !height
            height := c.Height + !height)
    // return collection as an array
    temp.ToArray()

// create a form to show the number controls
let numbersForm =
    let temp = new Form() in
    temp.Controls.AddRange(numbers);
    temp

// show the form
[<STAThread>]
do Application.Run(numbersForm)

The previous example shows the definition of the object expression that implements the interface IComparable for the TextBox class. IComparable allows objects that implement this interface to be compared, primarily so they can be sorted. In this case, the implementation of IComparable's CompareTo method sorts the controls according to which number is displayed as the text of the TextBox. After you implement the makeNumberControl function, you create an array of controls, numbers. The definition of numbers is a little complicated. Begin by initializing it so it's full of controls in a random order, then you sort the array. Finally, ensure that each control is displayed at the appropriate height. You can see the resulting user interface in Figure 5-2.

Sorted text box controls

Figure 5.2. Sorted text box controls

You might also want to override methods from the object in the object expression. To do that in this case, you would use the same syntax, except you would follow the object name with the keyword with. Imagine that, instead of displaying the numbers in a text box, you want to custom draw them by overriding an object's OnPaint method:

open System
open System.Drawing
open System.Windows.Forms

// create a new instance of a number control
let makeNumberControl (n: int) =
    { new Control(Tag = n, Width = 32, Height = 16) with
          // override the controls paint method to draw the number
          override x.OnPaint(e) =
              let font = new Font(FontFamily.Families.[2], 12.0F)
              e.Graphics.DrawString(n.ToString(),
                                    font,
                                    Brushes.Black,
                                    new PointF(0.0F, 0.0F))
// implement the IComparable interface so the controls
      // can be compared
      interface IComparable with
          member x.CompareTo(other) =
              let otherControl = other :?> Control in
              let n1 = otherControl.Tag :?> int in
              n.CompareTo(n1) }

You can see the resulting user interface in Figure 5-3.

Sorted, custom-drawn controls

Figure 5.3. Sorted, custom-drawn controls

Object expressions are a powerful mechanism to quickly and concisely introduce object-oriented functionality from objects from non-F# libraries into your F# code. They have the drawback that they do not allow you to add extra properties or methods to these objects. For example, in the previous example, notice how it was necessary to place the number associated with control in the control's Tag property. This is more of a workaround than a proper solution. However, sometimes you don't need extra properties or methods on a type, and this syntax can be useful for those cases.

Defining Classes

You have already seen quite a few examples of using classes defined in the .NET BCL library; next, you'll learn how to define your own classes. In object-oriented programming, a class should model some concept used within the program or library you are creating. For example, the String class models a collection of characters, and the Process class models an operating-system process.

A class is a type, so a class definition starts with the type keyword, followed by the name of the class and the parameters of the class's constructor between parentheses. Next comes an equals sign, followed by a class's member definitions. The most basic member of a class is called a method, which is a function that has access to the parameters of the class.

The next example shows a class that represents a user. The user class's constructor takes two parameters: the users name and a hash of the user's password. Your class provides two member methods: Authenticate, which checks whether the user's password is valid; and LogonMessage, which gets a user-specific logon message:

open Strangelights.Samples.Helpers

// a class that represents a user
// it's constructor takes two parameters, the user's
// name and a hash of their password
type User(name, passwordHash) =
    // hashs the users password and checks it against
    // the known hash
    member x.Authenticate(password) =
        let hashResult = hash (password, "sha1")
        passwordHash = hashResult

    // gets the users logon message
    member x.LogonMessage() =
        Printf.sprintf "Hello, %s" name

// create a new instance of our user class
let user = User("Robert", "AF73C586F66FDC99ABF1EADB2B71C5E46C80C24A")


let main() =
    // authenticate user and print appropriate message
    if user.Authenticate("badpass") then
        printfn "%s" (user.LogonMessage())
    else
        printfn "Logon failed"

do main()

The second half of the example demonstrates how to use the class. It behaves exactly as like other classes you've seen from the .NET BCL. You can create a new instance of User using the new keyword, then call its member methods.

It's often useful to define values that are internal to your classes. Perhaps you need to pre-calculate a value that you share between several member methods, or maybe you need to retrieve some data for the object from an external data source. To enable this, objects can have let bindings that are internal to the object, but shared between all members of the object. You place the let bindings at the beginning of the class definition, after the equals sign, but before the first member definition. These let bindings form an implicit construct that execute when the object is constructed; if the let bindings have any side effects, then these too will occur when the object is constructed. If you need to call a function that has the unit type, such as when logging the objects construction, you must prefix the function call with the do keyword.

The next example demonstrates private let bindings by taking your User class and modifying it slightly. Now the class constructor takes a firstName and lastName, which you use in the let binding to calculate the user's fullName. To see what happens when you call a function with a side effect, you can print the user's fullName to the console:

open Strangelights.Samples.Helpers

// a class that represents a user
// it's constructor takes three parameters, the user's
// first name, last name and a hash of their password
type User(firstName, lastName, passwordHash) =
    // calculate the user's full name and store of later use
    let fullName = Printf.sprintf "%s %s" firstName lastName
    // print users fullname as object is being constructed
    do printfn "User: %s" fullName

    // hashs the users password and checks it against
    // the known hash
    member x.Authenticate(password) =
        let hashResult = hash (password, "sha1")
        passwordHash = hashResult

    // retrieves the users full name
    member x.GetFullname() = fullName

Notice how the members also have access to class's let bindings, the member GetFullName returns the pre-calculated fullName value.

It's common to need to be able to change values within classes. For example, you might need to provide a ChangePassword method to reset the user's password in the User class. F# gives you two approaches to accomplish this. You can make the object immutable; in this case, you copy the object's parameters, changing the appropriate value as you go. This method is generally considered to fit better with functional-style programming, but it can be a little inconvenient if the object has a lot of parameters or is expensive to create. For example, doing this might be computationally expensive, or it might require a lot of I/O to construct it. The next example illustrates this approach. Notice how in the ChangePassword method you call the hash function on the password parameter, passing this to the User object's constructor along with the user's name:

open Strangelights.Samples.Helpers

// a class that represents a user
// it's constructor takes two parameters, the user's
// name and a hash of their password
type User(name, passwordHash) =
    // hashs the users password and checks it against
    // the known hash
    member x.Authenticate(password) =
        let hashResult = hash (password, "sha1")
        passwordHash = hashResult

    // gets the users logon message
    member x.LogonMessage() =
        Printf.sprintf "Hello, %s" name

    // creates a copy of the user with the password changed
    member x.ChangePassword(password) =
        new User(name, hash password)

The alternative to an immutable object is to make the value you want to change mutable. You do this by binding it to a mutable let binding. You can see this in the next example, where you bind the class's parameter passwordHash to a mutable let binding of the same name:

open Strangelights.Samples.Helpers

// a class that represents a user
// it's constructor takes two parameters, the user's
// name and a hash of their password
type User(name, passwordHash) =
    // store the password hash in a mutable let
    // binding, so it can be changed later
    let mutable passwordHash = passwordHash

    // hashs the users password and checks it against
    // the known hash
    member x.Authenticate(password) =
        let hashResult = hash (password, "sha1")
        passwordHash = hashResult

    // gets the users logon message
    member x.LogonMessage() =
        Printf.sprintf "Hello, %s" name
// changes the users password
    member x.ChangePassword(password) =
        passwordHash <- hash password

This means you are free to update the passwordHash to a let binding, as you do in the ChangePassword method.

Optional Parameters

Member methods of classes (as well as member methods of other types) and class constructors can have optional parameters. These are useful because they allow you to set default input values. This means users of your class don't have to specify all its arguments, which can help make client code look cleaner and less cluttered.

You mark a parameter as optional by prefixing it with a question mark. You can have more than one optional parameter, but optional parameters must always appear at the end of the parameter list. Also, you must use tuple style in cases where you have a member method that contains more than one argument with one or more optional arguments. You do this by surrounding optional arguments with parentheses, separated by commas. An optional parameter might (or might not) have a type annotation. This type annotation appears after the parameter name, separated by a colon. Optional parameters are always of the option<'a> type, so you must not include them in the type annotation.

The following example shows some optional parameters in action. Here you define a class, AClass, that has an optional integer as a parameter of its constructor. This class has one member method, PrintState, and two parameters (the second parameter is optional). As you might expect, you use pattern matching over the option<'a> type to test whether the optional parameters were passed as an argument:

type AClass(?someState:int) =
    let state =
        match someState with
        | Some x -> string x
        | None -> "<no input>"
    member x.PrintState (prefix, ?postfix) =
        match postfix with
        | Some x -> printfn "%s %s %s" prefix state x
        | None -> printfn "%s %s" prefix state

let aClass = new AClass()
let aClass' = new AClass(109)

aClass.PrintState("There was ")
aClass'.PrintState("Input was:", ", which is nice.")

The second half of the example shows some of the class's client code. You create two instances of the class: you create the first class without any arguments being passed to the constructor; and you create the second with a value of 109 being passed to the constructor. Next, you call the PrintState member of the class, calling it initially without the optional argument, then calling it again with the optional argument. Compiling and executing this code produces the following results:

There was <no input>
Input was: 109 , which is nice.

Currently functions defined by let bindings cannot have optional parameters. It is under investigation how optional parameters for functions might be added to the language in a future version.

Defining Interfaces

Interfaces can contain only abstract methods and properties, or members that you declare using the keyword abstract. Interfaces define a contract for all classes that implement them, exposing those components that clients can use while insulating clients from their actual implementation. A class can inherit from only one base class, but it can implement any number of interfaces. Because any class implementing an interface can be treated as being of the interface type, interfaces provide similar benefits to multiple-class inheritance, while avoiding the complexity of that approach.

You define interfaces by defining a type that has no constructor and where all the members are abstract. The following example defines an interface that declares two methods: Authenticate and LogonMessage. Notice how the interface name starts with a capital I; this is a naming convention that is strictly followed thought the .NET BCL, and you should follow it in your code too because it will help other programs distinguish between classes and interfaces when reading your code:

// an interface "IUser"
type IUser =
    // hashs the users password and checks it against
    // the known hash
    abstract Authenticate: evidence: string -> bool
    // gets the users logon message
    abstract LogonMessage: unit -> string

let logon (user: IUser) =
    // authenticate user and print appropriate message
    if user.Authenticate("badpass") then
        printfn "%s" (user.LogonMessage())
    else
        printfn "Logon failed"

The second half of the example illustrates the advantages of interfaces. You can define a function that uses the interface without knowing the implementation details. You define a logon function that takes an IUser parameter and uses it to perform a logon. This function will then work with any implementations of IUser. This is extremely useful in many situations; for example, it enables you to write one set of client code that you can reuse with several different implementations of the interface.

Implementing Interfaces

To implement an interface, use the keyword interface, followed by the interface name, the keyword with, and then the code to implement the interface members. You prefix member definitions with the keyword member, but they are otherwise the same as the definition of any method or property. You can implement interfaces by either classes or structs; you can learn how to create classes in some detail in the following sections, and you can learn more about structs in the "Structs" section later in this chapter.

The next example defines, implements, and uses an interface. The interface is the same IUser interface you implemented in the previous section; here you implement it in a class called User:

open Strangelights.Samples.Helpers

// an interface "IUser"
type IUser =
    // hashs the users password and checks it against
    // the known hash
    abstract Authenticate: evidence: string -> bool
    // gets the users logon message
    abstract LogonMessage: unit -> string

// a class that represents a user
// it's constructor takes two parameters, the user's
// name and a hash of their password
type User(name, passwordHash) =
    interface IUser with
        // Authenticate implementation
        member x.Authenticate(password) =
            let hashResult = hash (password, "sha1")
            passwordHash = hashResult

        // LogonMessage implementation
        member x.LogonMessage() =
            Printf.sprintf "Hello, %s" name

// create a new instance of the user
let user = User("Robert", "AF73C586F66FDC99ABF1EADB2B71C5E46C80C24A")
// cast to the IUser interface
let iuser = user :> IUser
// get the logon message
let logonMessage = iuser.LogonMessage()
let logon (iuser: IUser) =
    // authenticate user and print appropriate message
    if iuser.Authenticate("badpass") then
        printfn "%s" logonMessage
    else
        printfn "Logon failed"

do logon user

Notice how in the middle of example you see casting for the first time; you can find a more detailed explanation of casting at the end of the chapter in the "Casting" section. But for now here's a quick summary of what happens: the identifier user is cast to the interface IUser via the downcast operator, :?>:

// create a new instance of the user
let user = User("Robert", "AF73C586F66FDC99ABF1EADB2B71C5E46C80C24A")
// cast to the IUser interface
let iuser = user :?> IUser

This is necessary because interfaces are explicitly implemented in F#. Before you can use the method LogonMessage, you must have an identifier that is of the type IUser and not just of a class that implements IUser. Toward the end of the example, you will work around this in a different way. The function logon takes a parameter of the IUser type:

let logon (iuser: IUser) =

When you call logon with a class that implements IUser, the class is implicitly downcast to this type.

You can add the interface members to the definition of the class if you want the methods of the interface to be available directly on the class that implements it, instead of after forcing the users of your class to cast the object to the interface in some way. To revise the example, you simply add the methods Authenticate and LogonMessage as members of the class User. Now it is no longer necessary to cast the identifier user (you'll learn about adding members to methods in the "Classes and Methods" section later in the chapter):

open Strangelights.Samples.Helpers

// a class that represents a user
// it's constructor takes two parameters, the user's
// name and a hash of their password
type User(name, passwordHash) =
    interface IUser with
        // Authenticate implementation
        member x.Authenticate(password) =
            let hashResult = hash (password, "sha1")
            passwordHash = hashResult
// LogonMessage implementation
        member x.LogonMessage() =
            Printf.sprintf "Hello, %s" name

    // Expose Authenticate implementation
    member x.Authenticate(password) = x.Authenticate(password)
    // Expose LogonMessage implementation
    member x.LogonMessage() = x.LogonMessage()

Classes and Inheritance

I have already covered inheritance in a limited way in the "Object Expressions" and "Implementing Interfaces" sections. Inheritance allows you to extend a class that is already defined, as well as to add new functionality or possibly to modify or replace the original functionality. Like most modern, object-oriented languages, F# allows single inheritance (from one base class), as well as the implementation of multiple interfaces (see the previous sections, "Defining interfaces" and "Implementing Interfaces"). This section will cover the basics of inheriting from a base class and adding new functionality. The next section, "Methods and Inheritance," will show you how to implement methods to make full use of inheritance.

You specify inheritance with the inherit keyword, which must come directly after the equals sign, which follows a class's constructor. After the keyword inheritance, you provide the name of the class you want to inherit from, followed by the arguments you intend to pass to its constructor. Let's kick things off by looking at a simple example of inheritance between two F# types. The following example shows an F# class, Sub, that derives from a base class, Base. The class Base has one method, GetState; and the class Sub also has one method, called GetOtherState. The example shows that how the Sub-derived class can use both methods because GetState is inherited from the base class:

type Base() =
    member x.GetState() = 0

type Sub() =
    inherit Base()
    member x.GetOtherState() = 0

let myObject = new Sub()

printfn
    "myObject.state = %i, myObject.otherState = %i"
    (myObject.GetState())
    (myObject.GetOtherState())

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

myObject.state = 0, myObject.otherState = 0

Methods and Inheritance

The preceding sections gave you the basics of inheritance between classes. Now you'll take a look at getting the most out of object-oriented programming by learning how to override methods and give them new behaviors. A derived class can define new methods, as well as override methods inherited from its base class.

You define methods are defined using one of four keywords: member, override, abstract, or default. You've already seen the keywords member and abstract, which you use to define methods. The Member keyword defines a simple method that cannot be overridden with an implementation, while the abstract keyword defines a method with no implementation that must be overridden in a derived class. The override keyword defines a method that overrides an inherited method that has an implementation in a base class. Finally, the keyword default has a similar meaning to the override keyword, except it is used only to override an abstract method.

The next example illustrates how to use all four kinds of methods:

// a base class
type Base() =
    // some internal state for the class
    let mutable state = 0
    // an ordinary member method
    member x.JiggleState y = state <- y
    // an abstract method
    abstract WiggleState: int -> unit
    // a default implementation for the abstract method
    default x.WiggleState y = state <- y + state
    member x.GetState() = state

// a sub class
type Sub() =
    inherit Base()
    // override the abstract method
    default x.WiggleState y = x.JiggleState (x.GetState() &&& y)

// create instances of both methods
let myBase = new Base()
let mySub = new Sub()

// a small test for our classes
let testBehavior (c : #Base) =
    c.JiggleState 1
    printfn "%i" (c.GetState())
    c.WiggleState 3
    printfn "%i" (c.GetState())
// run the tests
let main() =
    printfn "base class: "
    testBehavior myBase
    printfn "sub class: "
    testBehavior mySub

do main()

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

base class:
1
4
sub class:
1
1

You first implement a method, JiggleState, in class Base. The method cannot be overridden, so all derived classes will inherit this implementation. You then define an abstract method, WiggleState, that can be overridden (and, in fact, must be) by derived classes. To define a new method that can be overridden, you always need to use a combination of the abstract and default keywords. This could mean that you use abstract on the base class, while you use default on the derived class; however, you will often use them together in the same class, as shown in the previous example. This requires you to give types explicitly to a method you provide to be overridden. Although the F# philosophy doesn't generally require the programmer to give explicit types, leaving it to the compiler work them out, the compiler has no way to infer these types, so you must give them explicitly.

As shown in the preceding results, the behavior remains the same in both the base class and the derived class when JiggleState is called; this is in contrast to the behavior of WiggleState, which changes because it is overridden.

Accessing the Base Class

When accessing a virtual method within a class, the version of the method in the most-derived class is called. This means that if you try to call a method on the base class that has been overridden by the derived class, then it will automatically call the version on the derived class. Ordinarily, you use this technique to call the base implementation of a method you want to override. This isn't always necessary, but it's generally required by library design-guidelines because it can lead to the base class malfunctioning if you do not do this.

To get access to methods on the base class, you use the base keyword that gives you access to the base class's methods. The following example shows an implementation of a class that derives from System.Windows.Form. The identifier base is assigned to base class Form, as shown at the top of the definition of the MySquareForm class. The example uses implicit class construction, indicated by the fact that the type MySquareForm takes a single parameter, color:

open System.Drawing
open System.Windows.Forms

// define a class that inherits from 'Form'
type MySquareForm(color) =
    inherit Form()
    // override the OnPaint method to draw on the form
    override x.OnPaint(e) =
        e.Graphics.DrawRectangle(color,
                                 10, 10,
                                 x.Width - 30,
                                 x.Height - 50)
        base.OnPaint(e)
    // override the OnResize method to respond to resizing
    override x.OnResize(e) =
        x.Invalidate()
        base.OnResize(e)

// create a new instance of the form
let form = new MySquareForm(Pens.Blue)

// show the form
do Application.Run(form)

In this form, you override two methods, OnPaint and OnResize; and in these methods, you use the keyword base, which grants access to the base class that you use to call the base class's implementation of this method.

Properties and Indexers

A property is a special type of method that looks like a value to the code that calls it. Indexers fulfill a similar purpose; they make a method look a bit like a collection to the calling code. Both properties and indexers have accessors, which include a get accessor for reading and a set accessor for writing.

A property definition starts the same way as a method definition, with the keyword member followed by the parameter that represents the object. Next, you include a dot and then the member name. Instead of using the method parameters after this, you use the keyword with, followed by either get or set. The parameters come next; a get method must take unit, and a set method must take a single parameter. An equals sign follows next, then an expression that forms the method body. If a second method is required, you use the keyword and to join them together.

The following sample shows the definition of a class that has a single property, MyProp, which returns a random number. Setting the property resets the seed of the random-number generator:

// a class with properties
type Properties() =
    let mutable rand = new System.Random()
// a property definition
    member x.MyProp
        with get () = rand.Next()
        and set y = rand <- new System.Random(y)

// create a new instance of our class
let prop = new Properties()

// run some tests for the class
prop.MyProp <- 12
printfn "%d" prop.MyProp
printfn "%d" prop.MyProp
printfn "%d" prop.MyProp

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

2137491492
726598452
334746691

You can also declare abstract properties. The syntax is similar, but you replace the keyword member with abstract, and you omit the parameter that represents the object, just as you do for a method. After the member name, you include the name of the type, separated from the member name by a colon. The keyword with comes next, followed by either get or set, which represents whether the inheritor must implement a get or set method, or both, separated by a comma. Properties look exactly like a field to the calling code.

The next example revises the previous one so now it uses an interface, IAbstractProperties. You will notice how the derived class ConcreteProperties must implement the get and set methods using the keywords with and and:

// an interface with an abstract property
type IAbstractProperties =
    abstract MyProp: int
        with get, set

// a class that implements our interface
type ConcreteProperties() =
    let mutable rand = new System.Random()
    interface IAbstractProperties with
        member x.MyProp
            with get() = rand.Next()
            and set(y) = rand <- new System.Random(y)

Indexers are properties that take two or more parameters, one to represent the element being placed in the pseudo-collection and others to represent the index in it. In C#, all indexers are called Item in the underlying implementation, but the programmer never uses this name because it is always implicit. In F#, the programmer can choose the name of the indexer property. If the programmer chooses the name Item, then F# provides special syntax for accessing the property.

The syntax for creating an indexer is the same as for a property, except that a get method has one or more parameters, and a set method has two or more parameters. The next step is to access an element in an indexer. If its name is Item, you can use a special syntax that looks like array access, except you use replace the parentheses replaced with square brackets:

// a class with indexers
type Indexers(vals:string[]) =
    // a normal indexer
    member x.Item
        with get y = vals.[y]
        and set y z = vals.[y] <- z
    // an indexer with an unusual name
    member x.MyString
        with get y = vals.[y]
        and set y z = vals.[y] <- z

// create a new instance of the indexer class
let index = new Indexers [|"One"; "Two"; "Three"; "Four"|]

// test the set indexers
index.[0] <- "Five";
index.Item(2) <- "Six";
index.MyString(3) <- "Seven";

// test the get indexers
printfn "%s" index.[0]
printfn "%s" (index.Item(1))
printfn "%s" (index.MyString(2))
printfn "%s" (index.MyString(3))

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

Five
Two
Six
Seven

Note

When working with indexers with a name other than Item, you should keep in mind that it will be difficult for other .NET languages to use your classes.

Overriding Methods from Non-F# Libraries

When overriding methods from non-F# libraries, you must implement the method definition in the tuple style; that is, you must surround the parameters with parentheses and separated them by commas.

The following sample shows a class that implements the interface, System.Net.ICredentials. Its single method, GetCredential, has two parameters. Just after the place where you implement the interface, you can see how to use the interface as a value in the method GetCredentialList:

type CredentialsFactory() = class
    interface System.Net.ICredentials with
        member x.GetCredential(uri, authType) =
            new System.Net.NetworkCredential("rob", "whatever", "F# credentials")
    member x.GetCredentialList uri authTypes =
        let y = (x :> System.Net.ICredentials)
        let getCredential s = y.GetCredential(uri, s)
        List.map getCredential authTypes
end

You can learn more about the relationship between F# signatures and C# signatures in Chapter 14.

Abstract Classes

The generally accepted way of defining a contract in F# is to use an interface, which works well for the most part. However, interfaces have one significant drawback: any change to an interface's definition is a breaking change to client code. This isn't a problem if you're creating the application, and you have complete control over the code base. Indeed, it can even be useful because the compiler will automatically notify you of all the code than needs changing. However, if you're shipping the interface as part of a library, then you are likely to run into problems if you change your interface's definition. This is where abstract classes prove useful. For example, assume you have an interface that an abstract class defines as a contract; the important difference here is that abstract bases classes can have concrete methods and properties. This makes versioning an abstract-base class easier than versioning an interface because you can add a concrete member without making breaking changes. Unlike interfaces, abstract class can have concrete members, which means a class can only inherit from one abstract class.

The abstract class syntax is exactly the same as the syntax for a class, except an abstract class can have abstract members. To ensure that you haven't made a mistake in adding an abstract member that you didn't provide an implementation for, you need to make the abstract class with the [<AbstactClass>] attribute. The next example shows what your User example might look like if you were to choose to use an abstract class:

// a abstract class that represents a user
// it's constructor takes one parameters,
// the user's name
[<AbstractClass>]
type User(name) =
    // the implmentation of this method should hashs the
    // users password and checks it against the known hash
    abstract Authenticate: evidence: string -> bool
// gets the users logon message
    member x.LogonMessage() =
        Printf.sprintf "Hello, %s" name

Classes and Static Methods

Static methods are like instance methods, except they are not specific to any instance of a class, so have no access to a class's fields.

To create a static method, you use the keyword static, followed by the keyword member. Next, you include the method name, its parameters, an equals sign, and then the method definition. This is basically the same as declaring an instance method, but with the addition of the keyword static and the removal of the parameter that represents the object. Removing the parameter that represents the object is quite logical because the method has no access to the object's properties.

Static methods are useful for providing alternative ways of creating a new instance of an object. F# provides no way of overloading class constructors, so you provide a static method that calls the class's constructor. In the next example, you return to the User class example, this time adding a static method that allows you to create a user from its unique identifier in the database:

open Strangelights.Samples.Helpers
// a class that represents a user
// it's constructor takes two parameters, the user's
// name and a hash of their password
type User(name, passwordHash) =
    // hashs the users password and checks it against
    // the known hash
    member x.Authenticate(password) =
        let hashResult = hash (password, "sha1")
        passwordHash = hashResult

    // gets the users logon message
    member x.LogonMessage() =
        Printf.sprintf "Hello, %s" name

    // a static member that provides an alterative way
    // of creating the object
    static member FromDB id =
        let name, ph = getUserFromDB id
        new User(name, ph)

let user = User.FromDB 1

Notice that the static methods called use the name of the type they are associated with, rather than a value of the type the method is associated with.

Static methods can also be useful for providing operators for your classes to use. The basic syntax for declaring an operator is the same as for declaring any other static method, except that you replace the name of the method with the operator in brackets. You must provide the parameters of the operator as a tuple; typically, you need to use type annotations to indicate their types.

The following example assumes that you want to reimplement the int type in a class called MyInt. The MyInt class has a plus operator defined on it:

type MyInt(state:int) = class
    member x.State = state
    static member ( + ) (x:MyInt, y:MyInt) : MyInt = new MyInt(x.State + y.State)
    override x.ToString() = string state
end

let x = new MyInt(1)
let y = new MyInt(1)

printfn "(x + y) = %A" (x + y)

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

(x + y) = 2

Classes with Explicit Fields and Constructors

So far this chapter has concentrated on the implicit syntax for classes, but F# also has another type of syntax: the explicit syntax. The implicit syntax is generally preferable for a number of reasons. First, it's often a good deal shorter than the explicit syntax. Second, the F# compiler can optimize the class. The implicit syntax often means that let bindings and parameters to the class's constructor don't need to become fields in the class. With the implicit syntax, the compiler is free to remove them, if possible (making the object smaller in memory). However, these optimizations are occasionally unwelcome. Certain APIs that use reflection require classes and their instances to have certain fields; in this case, you as a programmer need the additional control that using the explicit syntax can give you.

In the explicit syntax, you don't provide the class constructor after the type name; instead, the type name is followed directly by the equals sign, the keyword class, and the definition (such as type User = class ...). When using the explicit syntax, you terminate the class with the keyword end.

You define fields using the keyword val, followed by the name of the field and the name of the type, which is separated from the property name by a colon. Fields in a class are immutable by default; once they have been bound to a value, the value can be rebound to another value. From time to time, it can be useful to rebind a field to another value. To allow this to happen, F# provides the keyword mutable; when a field is defined as mutable, it can be rebound whenever the programmer chooses.

To enable you to create an instance of the class, you must explicitly add a constructor. To do this, you need to add a member, which is always named new and is followed by the constructor within parentheses. Next, you include an equals sign, followed by a block (delimited by braces) that contains expressions to initialize every field in the class. It's possible to overload constructors, which you accomplish by adding a second constructor with a different number of parameters. If you want to overload with parameters of different types, then you must provide type annotations.

The next example rewrites the first class example you saw, a demonstration of a simple User class, in the explicit style. This makes the example several lines longer:

open System.Web.Security

// give shorte name to password hashing method
let hash = FormsAuthentication.HashPasswordForStoringInConfigFile

// a class that represents a user
// it's constructor takes two parameters, the user's
// name and a hash of their password
type User = class
    // the class' fields
    val name: string
    val passwordHash: string

    // the class' constructor
    new (name, passwordHash) =
        { name = name; passwordHash = passwordHash }

    // hashs the users password and checks it against
    // the known hash
    member x.Authenticate(password) =
        let hashResult = hash (password, "sha1")
        x.passwordHash = hashResult

    // gets the users logon message
    member x.LogonMessage() =
        Printf.sprintf "Hello, %s" x.name
end

Casting

You've already encountered casting, which was discussed briefly in the "Implementing Interfaces" section of this chapter. Casting is a way of explicitly altering the static type of a value by either throwing information away, which is known as upcasting; or rediscovering it, which is known as downcasting. In F#, upcasts and downcasts have their own operators. The type hierarchy starts with obj (or System.Object) at the top, with all its descendants below it. An upcast moves a type up the hierarchy, while a downcast moves a type down the hierarchy.

Upcasts change a value's static type to one of its ancestor types. This is a safe operation. The compiler can always tell whether this will work because the compiler always knows all the ancestors of a type, so it's able to work use static analysis to determine whether an upcast will be successful. An upcast is represented by a colon, followed by the greater-than sign (:>). The following code shows you how to use an upcast to convert a string to an obj:

let myObject = ("This is a string" :> obj)

Generally, you must use upcasts when defining collections that contain disparate types. If you don't use an upcast, the compiler will infer that the collection has the type of the first element and give a compile error if elements of other types are placed in the collection. The next example demonstrates how to create an array of controls, a common task when working with WinForms. Notice that you upcast all the individual controls to their common base class, Control:

open System.Windows.Forms

let myControls =
    [| (new Button() :> Control);
       (new TextBox() :> Control);
       (new Label() :> Control) |]

An upcast also has the effect of automatically boxing any value type. Value types are held in memory on the program stack, rather than on the managed heap. Boxing means that the value is pushed onto the managed heap, so it can be passed around by reference. The following example demonstrates how to box a value:

let boxedInt = (1 :> obj)

A downcast changes a value's static type to one of its descendant types; thus, it recovers information hidden by an upcast. Downcasting is dangerous because the compiler doesn't have any way to determine statically whether an instance of a type is compatible with one of its derived types. This means you can get it wrong, and this will cause an invalid cast exception (System.InvalidCastException) to be issued at runtime. Due to the inherent danger of downcasting, many developers prefer to replace it with pattern matching over .NET types, as demonstrated in Chapter 3. Nevertheless, a downcast can be useful in some places, so a downcast operator, which consists of a colon, question mark, and a greater-than sign (:?>), is available. The next example shows you how to use downcasting:

open System.Windows.Forms

let moreControls =
    [| (new Button() :> Control);
       (new TextBox() :> Control) |]

let control =
    let temp = moreControls.[0]
    temp.Text <- "Click Me!"
    temp

let button =
    let temp = (control :?> Button)
    temp.DoubleClick.Add(fun e -> MessageBox.Show("Hello") |> ignore)
    temp

The preceding example creates an array of two Windows control objects, upcasting them to their base class, Control. Next, it binds the first control to the control identifier; downcasts this to its specific type, Button; and adds a handler to its DoubleClick event, an event not available on the Control class.

Type Tests

Closely related to casting is the idea of type tests. You can bind an identifier to an object of a derived type, as you did earlier when you bound a string to an identifier of type obj:

let myObject = ("This is a string" :> obj)

You can bind an identifier to an object of a derived type, so it is often useful to be able to test what this type is. To do this, F# provides a type-test operator, which consists of a colon followed by a question mark (:?). To compile, the operator and its operands must be surrounded by parentheses. If the identifier in the type test is of the specified type or a type derived from it, the operator returns true; otherwise, it returns false. The next example shows two type tests, one that returns true and another that returns false:

let anotherObject = ("This is a string" :> obj)

if (anotherObject :? string) then
    printfn "This object is a string"
else
    printfn "This object is not a string"

if (anotherObject :? string[]) then
    printfn "This object is a string array"
else
    printfn "This object is not a string array"

First you create an identifier, anotherObject, of type obj, binding it to a string. Then you test whether the anotherObject is a string, which returns true. Next, you test whether it is a string array, which, of course, returns false.

Type Annotations for Subtyping

As shown in Chapter 3, type annotations provide a way of constraining an identifier, usually a parameter of a function, to a certain type. What might seem counterintuitive to an OO programmer is that the form of type annotation introduced in Chapter 3 is rigid; in other words, it does not take into account the inheritance hierarchy. This means that if you apply such a type annotation to an expression, then that expression must have precisely that type statically; a derived type will not fit in its place. To illustrate this point, consider the following example:

open System.Windows.Forms

let showForm (form : Form) =
    form.Show()
// PrintPreviewDialog is defined in the BCL and is
// derived directly the Form class
let myForm = new PrintPreviewDialog()

showForm myForm

When you try to compile the previous example, you receive the following error:

Prog.fs(11,10): error: FS0001: This expression has type
    PrintPreviewDialog
but is here used with type
    Form

One way to call a function with a rigid type annotation on a parameter is to use an explicit upcast at the place where you call the function to change the type so it's the same as the type of the function's parameter. The following line of code changes the type of myForm to match the type of the parameter of showForm:

showForm (myForm :> Form)

Upcasting the argument to showForm is a solution, but it's not a pretty one because it means littering client code with upcasts. So, F# provides another type annotation, the derived type annotation, in which the type name is prefixed with a hash sign. This has the effect of constraining an identifier to be of a certain type or any of its derived types. This means you can rewrite the preceding example to remove the need for explicit upcasts in the calling code, which is a huge benefit to anyone using the functions you define:

open System
open System.Windows.Forms

let showFormRevised (form : #Form) =
    form.Show()

// ThreadExceptionDialog is define in the BCL and is
// directly derived type of the Form class
let anotherForm = new ThreadExceptionDialog(new Exception())
showFormRevised anotherForm

You can use this kind of type annotation to tidy up code that uses a lot of casting. For example, you often need a lot of casting when creating a collection with a common base type, and this can leave code looking a little bulkier than it should (the "Casting" section earlier in this chapter illustrates this point nicely). A good way to remove this repeated casting—as with any commonly repeated section of code—is to define a function that does it for you:

open System.Windows.Forms

let myControls =
    [| (new Button() :> Control);
       (new TextBox() :> Control);
       (new Label() :> Control) |]

let uc (c : #Control) = c :> Control

let myConciseControls =
    [| uc (new Button()); uc (new TextBox()); uc (new Label()) |]

This example shows how to define two arrays of controls. The first, myControls, explicitly upcasts every control; the second, myConciseControls, delegates this job to a function. This is also a good technique to adopt given that the bigger the array, the more effort and code this technique saves you. It is quite common for these arrays to get quite big when working with WinForms.

Defining Delegates

Delegates are the mechanism that both C# and Visual Basic use to treat their methods as values. A delegate basically acts as a .NET object that wraps the method and provides an invoke method so the method can be called. You rarely need to define delegates in F# because it can treat a function as a value, without the need for any wrapper. However, sometimes delegates prove useful, such as when you need to define delegates to expose F# functionality to other .NET languages in a friendlier manner, or you need to define callbacks for directly calling C code from F#.

To define a delegate, you use the keyword delegate, followed directly by the keyword of, and the type of the delegate's signature, which follows the standard F# type annotation.

The next example shows the definition of a delegate, MyDelegate, which takes an int and returns unit. You then create a new instance of this delegate and apply it to a list of integers. As you've already seen in Chapter 3, you implement this functionality in F# in much shorter ways:

type MyDelegate = delegate of int -> unit

let inst = new MyDelegate (fun i -> printf "%i" i)
let ints = [1 ; 2 ; 3 ]

ints
|> List.iter (fun i -> inst.Invoke(i))

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

123

Structs

You define structs in a similar manner to classes. You replace the keyword class with struct. The main difference between a class and struct is the area of memory where the object will be allocated. When used as a local variable or parameter, a struct is allocated on the stack, while a class is allocated on the managed heap. Structs are allocated on the stack, so they are not garbage collected, but automatically deallocated when a function exits. Generally, it's slightly faster to access a struct's fields than a class's; however, it's slightly slower to pass them to methods. That said, these differences tend to be quite small. Because they are allocated on the stack, it is generally best to create structs with a small number of fields to avoid stack overflow. You can't use inheritance when implementing structs, which means structs can't define virtual methods or abstract methods.

The next example defines a struct that represents an IP address. Note the only difference from defining a class is that you use the keyword struct:

type IpAddress = struct
    val first : byte
    val second : byte
    val third : byte
    val fourth : byte
    new(first, second, third, fourth) =
        { first = first;
          second = second;
          third = third;
          fourth = fourth }
    override x.ToString() =
        Printf.sprintf "%O.%O.%O.%O" x.first x.second x.third x.fourth
    member x.GetBytes() = x.first, x.second, x.third, x.fourth
end

The question is this: when should you use a class, and when should you use a struct? A good rule of thumb is to avoid structs, using them only when absolutely necessary, such as when interoperating with unmanaged C/C++ code (see Chapter 13 for more details on this).

Enums

Enums allow you to define a type made up of a finite set of identifiers, with each identifier mapping to an integer. This defines a type that can then take the value associated with any one of the defined identifiers.

You define an enum by giving the names of the identifiers followed by the equals sign and the values of the constants associated with the identifiers. You separate the identifiers that are members of the enum with vertical bars. The following example shows you how to define an enum Scale:

type Scale =
| C = 1
| D = 2
| E = 3
| F = 4
| G = 5
| A = 6
| B = 7

It's quite common to define enums that you intend to combine logically. To do this, choose constants so that each number is represented by a single bit, or the numbers 0, 1, 2, 4, 8, and so on. F#'s binary literals are a great help here because it's easy to see how you might combine the constants:

[<System.Flags>]
type ChordScale =
| C = 0b0000000000000001
| D = 0b0000000000000010
| E = 0b0000000000000100
| F = 0b0000000000001000
| G = 0b0000000000010000
| A = 0b0000000000100000
| B = 0b0000000001000000

The module Enum provides functionality for dealing with enums in F# (you can learn more about this module in Chapter 7).

Summary

You've now seen how to use the three major programming paradigms in F# and how flexible F# is for coding in any mix of styles. In the next chapter, you'll look at how code is organized in F#, as well as how to annotate and "quote" it.

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

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