CHAPTER 6

Type Definitions, Classes and Objects
Chapters 2 to 5 have dealt with the basic constructs of F# functional and imperative programming, and by now we trust you feel familiar with the foundational concepts and techniques of practical, small-scale F# programming. In this chapter we cover additional constructs related to defining types and object-oriented programming. Initially we focus on describing the constructs themselves, and toward the end of the chapter we discuss techniques for deploying and combining these constructs effectively in combination with the core language features we’ve already encountered.

Defining Type Abbreviations
Type abbreviations are the very simplest type definitions:
type index = int type flags = int64 type results = string * TimeSpan * int * int

It is common practice to use lower-case names for type abbreviations. Type abbreviations may be generic:
type StringDictionary<'a> = System.Collections.Generic.Dictionary<string,'a> type ('a,'b) maps = ('a -> 'b) * ('b -> 'a)

Type abbreviations are not “concrete”, as they simply alias an existing type. Type abbreviations are expanded during the process of compiling F# code to the format shared between multiple .NET languages. The difference can, for example, be detected by other .NET languages, and because of this a number of restrictions apply to type abbreviations. For example, you cannot augment type abbreviations with additional members, as can be done for concrete types such as records, discriminated unions and classes. In addition, you cannot truly hide a type abbreviation using a signature (again see Chapter 7).

Defining Records
The simplest concrete type definitions are records. Here’s our first example:
type Person = { Name: string; DateOfBirth: System.DateTime; }

Record values may be constructed simply by using the record labels:

> let bill = { Name = "Bill"; DateOfBirth = new System.DateTime(1962,09,02) } val bill : Person = { Name="Bill"; DateOfBirth = 02/09/1962 }

Records values may also be constructed by using the following more explicit syntax, which names the type should there be a conflict between labels amongst multiple records:
> let anna = { new Person with Name = "Anna" and DateOfBirth = new System.DateTime(1968,07,23) } val anna : Person = { Name="Anna"; DateOfBirth = 23/07/1968 }

Record values are often used to return results from functions:
type PageStats = { Site: string; Time: System.TimeSpan; Length: int; NumWords: int; NumHRefs: int }

This technique works well when returning a large number of heterogeneous results.
let stats site = let url = "http://" + site let html,t = time (fun () -> http url) let hwords = html |> getWords let hrefs = html |> getWords |> List.filter (fun s -> s = "href") { Site=site; Time=t; Length=html.Length; NumHRefs=hrefs.Length }

Here is the type of stats and how F# Interactive shows the results of applying the function:
val stats : string -> PageStats > stats "www.live.com";; { Site="www.live.com"; Time=0.872623628; Length=7728, NumWords=1156; NumHRefs=10; }

Handling Non-unique Record Field Names
Records labels need not be unique amongst multiple record types. Here is an example:
type Person = { Name: string; DateOfBirth: System.DateTime; } type Company = { Name: string; Address: string; }

When record names are non-unique, constructions of record values may need to use object expressions in order to indicate the name of the record type, thus disambiguating the construction. For example, consider the following type definitions:

type Dot = { X: int; Y: int } type Point = { X: float; Y: float }

On lookup, record labels are accessed using the “.” notation in the same way as properties. One slight difference is that in the absence of further qualifying informataion; the type of the object being accessed is inferred from the record label. This is based on that latest set of record labels in scope from record definitions and uses of open. For example, given the above definitions we have:
> let coords1 (p:Point) = (p.X,p.Y) val coords1 : Point -> int * int > let coords2 (d:Dot) = (d.X,d.Y) val coords2 : Dot -> float * float > let dist p = sqrt (p.X * p.X + p.Y * p.Y) // use of X and Y implies type “Point” val dist : Point -> float

The accesses to the labels X and Y in the first two definitions have been resolved using the type information provided by the type annotations. The accesses in the third definition have been resolved using the default intepretation of record field labels in the absence of any other qualifying information.

Using Mutable Record Fields
Record fields may be labelled mutable. This is usually done for records that implement the internal state of objects, a topic we’ll discuss in the next chapter. This means that record fields can be updated using the <- operator, i.e. the same syntax used to set a property.
type ConnectionStats = { mutable TotalLength: int; mutable NumPages: int; } let fetch cstats url = let page = http url cstats.NumPages <- cstats. NumPages + 1 cstats.TotalLength <- cstats.TotalLength + page.Length page

The function fetch takes two arguments: one a mutable “tracker” record used to accumulate statistics and the other the URL to access. Programming with mutable data structures is covered in more detail in Chapter TECHNIQUES.
> let cstats = { TotalLength = 0; NumPages = 0; } val it : ConnectionStats = { TotalLength = 0; NumPages = 0 } > ( fetch cstats "http://www.smh.com.au"; fetch cstats "http://www.theage.com.au "; cstats);; val it : ConnectionStats = { TotalLength = 8372; NumPages = 2 }

Finally, record types may also support members (e.g. properties and methods) and give implicit implementations of interfaces, as discussed further below. This makes them a very powerful device for implementing object-oriented abstractions.

Cloning Records
Records support a convenient syntax to clone all the values in the record, creating a new value, with some values replaced. Here is a simple example:
type Point3D = { X: float; Y: float ; Z : float } let p1 = { X=3.0 ; Y=4.0 ; Z=5.0 } > let p2 = { p1 with Y=0.0; Z=0.0 } val p2 : Point3D = { X=3.0 ;Y=0.0 ;Z=0.0 }

The definition of p2 is identical to:
let p2 = { X=p1.X; Y=p2.Y; Z=0.0 }

This expression form does not mutate the values of a record, even if the fields of the original record are mutable.

Using Records as Abstract Values
Records can be used to model simple abstract entities simply by making the fields of the record type function values:
open System.Drawing type Shape = { Scale: float -> unit; Draw: Graphics -> unit }

Record values are implemented using record expressions:
let Rect(top,left,bottom,right) = let state = ref (new Rectangle(top,left,bottom,right)) let scale{n} = let r = !state in state := new Rectangle(truncate r.Top,...) let draw(g:Graphics) = ... { Scale=scale; Draw=draw } let Circle(top,left,bottom,right) = let state = ref (new Rectangle(top,left,bottom,right)) let scale{n} = let r = !state in state := new Rectangle(truncate r.Top,...) let draw(g:Graphics) = ... { Scale=scale; Draw=draw }

let r1 = Rect(...) let r2 = Circle (...) let f = new Form() let t = new Timer() t.Interval <- 10 f.Paint.Add(fun evArgs -> ...)

The above is an example of using records to define “abstract values”. Abstract values are simply values that might in theory have many different possible underlying implementations, which means that both the code and data associated with a value is not immediately known from the static type of the value. We’ve already seen several examples of this, e.g. function values of types such as int -> int are a very simple kind of abstract value. Well-designed abstract types are often compositional.

Defining Discriminated Unions
The second kind of concrete type definition is a discriminated union. Here is a very simple example:
type Route = int type Make = string type Model = string type Transport = | Car of Make * Model | Bicycle | Bus of Route

Each alternative of a discriminated union is called a “constructor”. Values can be built simply by using the constructor much as if it were a function:
> let nick = Car("BMW","360");; val nick : Transport > let don = [ Bicycle; Bus 8 ];; val don : Transport list > let james = [ Car "Ford Fiesta"; Bicycle ];; val james : Transport list

Constructors can also be used in pattern matching:
let averageSpeed (tr: Transport) = match tr with | Car _ -> 35 | Bicycle -> 16 | Bus _ -> 24

Several of the types we’ve already met are defined as discriminated unions. For example, the 'a option type is defined as follows:
type 'a option = | None | Some of 'a

Discriminated unions may include recursive references (the same is true of records and other type definitions). This is frequently used when representing structured languages via discriminated unions:
type Proposition = | True | And of Proposition * Proposition | Or of Proposition * Proposition | Not of Proposition

Recursive functions are required to define the semantics of this kind of type. For example:
let rec eval (p: Proposition) = match p with | True -> true | And(p1,p2) -> eval p1 && eval p2 | Or (p1,p2) -> eval p1 or eval p2 | Not(p1) -> not (eval p1)

Indeed, the F# type of immutable lists is defined in this way:
type 'a list = | ([]) | (::) of 'a * 'a list

A broad range of tree-like data structures are very conveniently represented as discriminated unions. For example:
type Tree<'a> = | Tree of 'a * Tree<'a> * Tree<'a> | Tip

Recursive functions can be used to compute properties of trees:
val size: Tree<'a> -> int let rec size tree = match tree with | Tree(_,l,r) -> 1 + size l + size r | Tip -> 1

Here is an example of a constructed tree term and the use of the size function:
> let small = Tree("1”,Tree("2”,Tip,Tip),Tip);; val small : Tree<string> = Tree ("1",Tree ("2",Tip,Tip),Tip) > size small;; val it : int = 2

Symbolic manipulation based on trees is discussed in detail in Chapter SYMBOLIC. Like records, discriminated union may support members and give implicit implementations of interfaces. We examine this more closely later in this chapter.

Note: Discriminated unions are a powerful and important construct, and are crucial when modeling a finite, sealed set of choices, and they are a perfect fit for many constructs that arise in applications and symbolic analysis libraries. However, they are, by design, non-extensible, except where that extensibility is represented by other mechanisms such as having each discriminant carry additional function values. Subsequent modules may not add new discriminants to a particular discriminated union. This is deliberate: types such as options and lists are powerful partly because they put a limit on which possibilities exist. Extensibility must be defined through alternative techniques, including the use of records of functions, interfaces and classes.

Using Discriminated Unions as Records
Discriminated union types with only one discriminant make for an effective way to implement record-like types:
type Point3D = Vector3D of float * float * float let origin = Vector3D(0.,0.,0.) let unitX = Vector3D(1.,0.,0.) let unitY = Vector3D(0.,1.,0.) let unitZ = Vector3D(0.,0.,1.)

These are particularly effective because they can be decomposed using patterns in the same way as tuple arguments:
let length (Vector3D(dx,dy,dz)) = sqrt(dx*dx+dy*dy+dz*dz)

This technique is most useful for record-like values where there is some natural order on the constituent elements of the value (as above), or where the elements have different types.
> let bill = { Name = "Bill"; DateOfBirth = new System.DateTime(1962,09,02) } val bill : Person = { Name="Bill"; DateOfBirth = 02/09/1962 }

Defining Multiple Types Simultaneously
Multiple types may be involved in a mutually recursive collection of types, including record types, discriminated unions and abbreviations:
type node = { Name : string; Links : link list } and link = | Dangling | Link of node

Defining Classes and Members
We now look at how to define simple object-oriented classes. In this section we will only look at how to define concrete classes and members, rather than the use of classes as abstract values and within inheritance hierarachies. Concrete classes are ones that carry data and which have a fixed set of members. We discuss abstract values and inheritance hierarchies in later in this chapter. In their simplest form, class types are similar to records, though the notation is slightly different. For example, a vector class can be implemented as follows:
type Vector2D = class val DX: float val DY: float new(dx,dy) = { DX=dx; DY=dy } end > let v1 = new Vector2D(1.0, -1.0);; val v1 : Vector2D = { DX = 1.0; DY = -1.0 }

Constructors are introduced by the new keyword, and all class values are ultimately constructed through constructors. These can enforce sophisticated checks and can be composed with the construction semantics of any inherited class. (In contrast, records are ultimatley constructed by record expressions that initialize all the fields of the record, and records do not support inheritance.) For example, the following defines a vector type that both checks its arguments are positive and pre-computes the length of the vector.
type PositiveVector2D = class val DX: float val DY: float val Length: float new(dx,dy) = if dx < 0.0 || dy < 0.0 then failwith "not positive"; { DX=dx; DY=dy; Length=sqrt(dx*dx+dy*dy) } end

Constructors must initialize all fields of the class but can be preceded by a sequence of checks as above. They may also include a block that gets executed after the initialization of the fields of the class by adding then followed by an expression at the end of the constructor.
type Vector2D = class val DX: float val DY: float val mutable computedLength: float option new(dx,dy,precompute) = { DX=dx; DY=dy; Length=None } then if precompute then x.Length <- Some(sqrt(dx*dx+dy*dy)) end

The use of then is is particularly important in the context of constructs with two-phase initialization semantics such as System.Windows.Forms.Control and its related extensions. Class, record and union types may include all the kinds of members described in Chapter 2, including static methods, instance methods, static properties and instance properties. In this section we look at how to define a rich set of members on your types.

Method Members
Class types may include all the kinds of members described in Chapter 2, including static methods, instance methods, static properties and instance properties. Method members are defined using the keyword member followed by a method name and an argument list. For example:
type Vector2D = class val DX: float val DY: float new(dx,dy) = { DX=dx; DY=dy } member v.Invert() = { DX= -v.DX; DY = -v.DY } member v.Scale(k) = { DX= k * v.DX; DY = k * v.DY } static member Create(dx,dy) = new Vector2D(dx,dy) static member Create' dx dy = { DX= dx; DY = dy } end

Static members do not act on a target value, and are often used for functions that create values of the containing type. Looking at the two Create methods, we see that method members can take arguments in either “tupled” or “iterated” form. However it is conventional to take all arguments to methods in tupled form, as this ensures that the members appear in a form that is most natural when used from other .NET languages.

Property Members
Classes can also include the definitions of property members:
type Vector2D = class val DX: float val DY: float new(dx,dy) = { DX=dx; DY=dy } member v.Length = sqrt(v.DX * v.DX + v.DY * v.DY) end > let v2 = new Vector2D(3.0, -4.0);; val v2 : Vector2D = { DX = 3.0; DY = -4.0 } > v2.Length;; val it : float = 5.0

In the implementation of the Length property the identifier v stands for the Vector2D value on which the property is being defined. In many other languages this is called this or self. In F# you may name this parameter as you see fit. The implementation of properties such as Length and Angle is executed each time the property is invoked, i.e. properties are syntactic sugar for function calls. We can see this if we add a side-effect to the implementation:
type MyType = class new() = { } member v.PropertyWithSideEffect = printf “Computing!\n”; 1 end > let x = new MyType();; val x : MyType = { } > x.PropertyWithSideEffect;; Computing! val it : int = 1 > x.PropertyWithSideEffect;; Computing! val it : int = 1

Setter Properties
The properties shown above are “read-only”, and effectively a shorthand notation for a “get” function– indeed if you inspect the underlying compiled CIL code for the above you will see a function called get_Length. Propeties may also have an associated “set” method, and a longer syntax is used to specify both set and get operations for the property. In the following we define a mutable representation of a complex number where adjusting the Angle property rotates the vector while maintaining its overall length:
type MutableVector2D = class val mutable DX: float; val mutable DY: float; new (dx,dy) = { DX=dx; DY=dy } member v.Length = with get () = sqrt(v.DX*v.DX+v.DY*v.DY) and set len = let angle = v.Angle v.DX <- cos(angle)*len; v.DY <- sin(angle)*len member c.Angle = with get () = atan2(v.DY,v.DX) and set angle = let len = v.Length v.DX <- cos(angle)*len; v.DY <- sin(angle)*len end

Note that implementations of one member may use other members, e.g. the implementation of the setter property Angle uses the getter property Length, and vice-versa. The members of an augmentation thus form a potentially-mutually recursive set of values.

Indexer Properties
Like methods, properties may also take arguments: these are called “indexer” properties. The most commonly defined indexer property is called Item, and the Item property on an value v is accessed via the special notation v.[i]. As the notation suggests these are used to implement the lookup operation on collection types. In the following example we implement a sparse vector in terms an underlying sorted hash table:
open System.Collections.Generic type MutableSparseVector = class val elems: SortedDictionary<int,float>; new() = { elems = SortedDictionary<_,_>() } member v.Add(k,v) = v.elems.Add(k,v) member v.Item with get i = if v.elems.ContainsKey(i) then v.elems.[i] else 0.0 and set i v = v.elems.Replace(i,v) end > let v = new MutableSparseVector () val v : MutableSparseVector > v.[3];; val it : float = 0.0 > v.[3] <- 547.0;; > v.[3];; val it : float = 547.0

Overloaded Operators
Types may also be augmented with overloaded operators.
open System.Collections.Generic type Vector2D class val DX: float val DY: float new(dx,dy) = { DX=dx; DY=dy } static member (+) ((v1:Vector2D),(v2:Vector2D)) = new Vector2D(v1.DX + v2.DX, v1.DY + v2.DY) static member (-) ((v1:Vector),(v2:Vector)) = new Vector2D(v1.DX - v2.DX, v1.DY - v2.DY) end

> let v1 = new Vector2D(3.0,4.0);; val v1 : Vector2D = { DX=3.0; DY=4.0 } > v1 + v1;; val it : Vector2D = { DX=4.0; DY=8.0 }

Operator overloading in F# works by defining values that map uses of an operator through to particular static members on the static types involved in the operation. Defining these mappings is non-trivial: for example, the F# library includes the following definition for the (+) operator:
let inline (+) x y = (^a: (static member (+) : ^a * ^b -> ^c) (x,y))

This means that while static members can in principle make use of any operator names, by default only certain operators mapped through to these static members. You are strongly encouraged to use certain operators for certain purposes, e.g., it is recommended the overloaded operator $* be used for multiplying an object such as a vector or matrix by a scalar value. The F# Informal Language Specification contains a description of the predefined operators and their suggested purposes.

Augmenting Record and Union Types
The member notation is not limited to traditionally OO constructs such as classes and interfaces: records and discriminated unions may be equipped with additional non-abstract members. In the example below we define the type Vector as a simple record, augmented with a property Length.
type Vector2D = { DX: float; DY: float } with member v.Length = sqrt(v.DX * v.DX + v.DY * v.DY) end

Here the identifier v stands for the value on which the property is being defined – in many other languages this is called this or self, while in F# you may name this parameter as you see fit.
> let x = { DX = 3.0; DY = -4.0 };; val x : Vector2D = { DX = 3.0; DY = -4.0 } > x.Length;; val it : float = 5.0

Discriminated unions and classes may also be given members via augmentations, e.g.:
type Tree<'a> = | Tree of 'a * Tree<'a> * Tree<'a> | Tip with member t.Size = match x with | Tree(_,l,r) -> 1 + l.Size + r.Size | Tip -> 1

end

Using Augmentations
The with ... end construct is called an “augmentation”. Augmentations may be given subsequent to the definition of a type, though it is important to note that “additional” augmentations must be given within the same file or interactive declaration as the type definition itself. For example a source code file could later contain:
type Vector2D with member v.Angle = atan2(v.DX,v.DY) end

The Angle member will then be in scope and available for use.

Note: augmentations are an effective technique for equipping simple type definitions with “basic” object-oriented functionality. However, it is often necessary to include an extra supporting class or module to contain the remaining non-trivial functionality associated with a type. For example, the module Microsoft.FSharp.Collections.List contains extra rich functionality associated with the F# list type. Like all modules under this namespace this may be opened simply by using open List.

Interfaces, Abstract Members and Object Expressions
We finish our tour of type definitions with interfaces, which can be thought of as records of function values, with the added advantage that you can mix interface types together using interface inheritance. Like other constructs discussed in this chapter, interfaces are often used or encountered when using or designing .NET frameworks that meet the .NET Framework design guidelines. In addition some interfaces such as IEnumerable are used throughout F# programming. New interface types are declared in the following way:
type IPerson = interface abstract Name : string abstract DateOfBirth : System.DateTime abstract GetChildren() : unit -> IPerson list end

Note the use of the keyword abstract. Abstract member values are members whose implementation may vary in implementations the interface type. Here Name and DateOfBirth are both property member, while GetChildren is a method member. Interfaces can be implemented using object expressions. Here is a simple example:

> let rhiannon = { new IPerson with member x.Name = "Rhiannon" member x.DateOfBirth = new System.DateTime(1997,10,07); member x.GetChildren() = [] } val rhiannon : IPerson > let anna = { new IPerson with member x.Name = "Anna" member x.DateOfBirth = new System.DateTime(1968,07,23); member x.GetChildren() = [ rhiannon ] } val anna : IPerson

It is .NET convention to prefix the name of all interface types with “I”. However, the use of interfaces as abstract object values is pervasive in F# OO programming so this convention is not always followed. Interfaces may contain all the full range of instance members discussed in the previous section. For example… We will seee many examples of more complex interface definitions later in this chapter, including examples of how classes, records and discriminated union types may implement interfaces.

Using Interfaces as Abstract Values
The F# libraries and the .NET Framework define a multitude of abstractions that model common repeating idioms in programming. One example that is used frequently in F# programming is IEnumerable<'a>, defined in the System.Collections.Generic namespace. Here the “I” stands for “interface”. Here’s the definition of the IEnumerable abstraction and the related IEnumerator using F# notation: 1
type IEnumerator<'a> = interface abstract Current : 'a abstract MoveNext : unit -> bool end type IEnumerable<'a> = interface abstract GetEnumerator : unit -> IEnumerator<'a> end

An abstract type such as IEnumerable<int> can be implemented by an object expression or by calling a library construction function such as IEnumerable.unfold. The above interfaces are defined in a library component that was implemented in another .NET language (in this case C#), though here we have used the corresponding F# syntax. Some other very useful predefined F# and .NET abstractions are:
In reality IEnumerator<’a> also inherits from the non-generic interface System.Collection.IEnumerator. For clarity we’ve ignored this here. See the F# library code for full example implementations of the IEnumerrator type.
1

* System.IDisposable. An abstraction representing values that own explicitly reclaimable resources. Not normally passed around as first-class values, since disposability is really a property of implementations of abstractions. However often used when building partial implementations of abstractions that automatically execute the disposal logic at an appropriate point. * System.IO.Stream. A fundamental I/O abstraction representing a readable or writeable stream of bytes. * Microsoft.FSharp.Control.IEvent. An abstraction representing ports into which you can plug “event listeners”, i.e. callbacks. Some other entity is typically responsible for “raising” the event and calling all listeners. In F#, .NET events become values of this type or the related type Microsoft.FSharp.Control.IDelegateEvent, and the module Microsoft.FSharp.Control.IEvent contains many very useful combinators for manipulating these values. Like all modules under this namespace this may be opened simply by using open IEvent.

Interface Inheritance
Interfaces may be arranged in a hierarchy, which gives one way to classify abstractions. For example, the .NET Framework includes a hierarchical classification of collection types: IEnumerable<T> is refined by ICollection<T> is refined by IList<T>. Here are the definitions of these types in F# syntax: … Hierarchical classification through interface inheritance is an important technique but can be difficult to use well. This is because in practice most collections of related items don’t have a “canonical” classification, so any particular classification hierarchy and naming scheme is biased, even if useful. For example, the .NET Framework collection type hierarchy doesn’t model whether collections are read-only or not: the classification scheme is still useful, but there are many other classification schemes which you could imagine for the same set of concepts. While hierarchical modeling is useful, it must also be used with care, as poorly-designed hierarchies often have to be abandoned late in the software development life-cycle. For many applications it is adequate to use existing classification hierarchies, in conjunction with new non-hierarchical record and interface types where new abstract concepts arise.

Advanced Uses of Object Expressions
Object expressions must give definitions for all unimplemented abstract members and may not add other additional members. Instead, local state is typically allocated outside the object expressions, e.g.
open System.Text let CharCountOuputSink() = let nchars = ref 0 { new TextOutputSink() with member x.WriteChar(c) = (printf “char %d\n” !nchars;

nchars:= ! nchars + 1) }

Classes and Inheritance
Abstract values can be difficult to implement from scratch: for example, a Graphical User Interface (GUI) component must respond to many different events, often in regular and predictable ways, and it would be very tedious to have to re-code all this behaviour for each component. This makes it essential to support the process of creating partial implementations of abstract values, where the partial implementations can then be completed and/or customized. F# supports this in a number of ways, one of which is to use classes. Classes generalize records with some important additional facilities: abstract member values, inheritance and an implicit construction syntax. As we saw with interfaces, abstract member values are members whose implementation may vary in different implementations of a type. Classes also let you give default implementations to abstract members: extensions and implementations of the class acquire any default behavior and can modify it. For example, consider the following class:
type TextOutputSink = class abstract WriteChar : char -> unit abstract WriteString : string -> unit default x.WriteString(s) = s |> String.iter (fun c -> x.WriteChar(c)) end

This class defines two abstract members WriteChar and WriteString, but gives a default implementation for WriteString in terms of WriteChar. However, extensions are still free to override and modify the implementation of WriteString (e.g. some implementations of I/O will be able to implement block operations much more efficiently than via individual WriteChar calls). Classes thus let you build types that represent partial or complete implementations of abstractions. Before we delve into classes too deeply, we note that there are other ways to achieve partial implementations of abstractions in F#. In particular, the use of object expressions and function values lets you model abstractions as records or interfaces and partial implementations as generator functions:
type ITextOutputSink = interface abstract WriteChar : char -> unit abstract WriteString : string -> unit end

Before we delve into classes too deeply, we note that there are other ways to achieve partial implementations of abstractions in F#. In particular, the use of object expressions and function values lets you model abstractions as records or interfaces and partial implementations as generator functions:
let simpleTextOutputSink(writeCharFunction) = { new TextOutputSink() with

member x.WriteChar(c) = writeChar(c) member x.WriteString(s) = s |> String.iter (fun c -> x.WriteChar(c)) }

This form of programming is recommended wherever possible, as it gives greater compositionality and flexibility.

Inheriting classes
Classes may be sub-classes of some existing class type, introduced by the inherit keyword. Like classes themselves, subclasses take careful design and are best used when modeling significant new abstract concepts or fragments of default behaviour that form part of the external interface to a library or framework. For example, the following extension adds two additional abstract members WriteByte and WriteBytes, and a default implementation for WriteBytes, an initial implementation for WriteChar, and overrides the implementation of WriteString to use WriteBytes. The implementations of WriteChar and WriteString use the .NET functionality to convert the Unicode characters and strings to bytes under the System.Text.UTF8Encoding, documented in the .NET Framework class libraries.
open System.Text type ByteOutputStream = class inherit TextOutputStream abstract WriteByte : byte -> unit abstract WriteBytes : byte[] -> unit default x.WriteChar(c) = x.WriteBytes(UTF8Encoding.GetBytes([|c|]) default x.WriteString(s) = x.WriteBytes(UTF8Encoding.GetBytes(s) default x.WriteBytes(b) = b |> Array.iter (fun c -> x.WriteByte(c)) end

The majority of “leaf” extensions of classes can be implemented using object expressions, e.g.
open System.Text let StringBufferOuputSink (buf : StringBuffer ) = { new TextOutputSink() with member x.WriteChar(c) = buf.Add(c) }

Here is an example of the use of this function interactively:
> let buf = new System.Text.StringBuffer() val buf : StringBuffer > let c = StringBufferOuputSink(buf);; val c : TextOutputSink > ["Incy"; " "; "Wincy"; " "; "Spider"] |> List.iter (fun s -> c.WriteString(s));; > buf.ToString();; val it : string = "Incy Wincy Spider"

Object expressions must give definitions for all unimplemented abstract members and may not add other additional members. Instead, local state is typically allocated outside the object expressions, e.g.
open System.Text

let CharCountOuputSink() = let nchars = ref 0 { new TextOutputSink() with member x.WriteChar(c) = (printf “char %d\n” !nchars; nchars:= ! nchars + 1) }

This function implements an OutputSink that counts characters, displaying the character count to the output stream. Object expressions can also be used to model entire families of leaf classes by accepting function parameters, helping to establish a link between OO programming and functional programming. For example, the following implements a TextOutputSink in terms of any function writeChar that provides an implementation of the abstract member.
let MakeOuputStream(writeChar) = { new TextOutputSink() with member x.WriteChar(c) = writeChar(c) }

This construction function uses function values to build an object of a given shape. Here the inferred type is:
val MakeOutputStream : (char -> unit) -> TextOutputSink

Classes and Construction
Implicitly Constructed Classes
TBD

Recap: Objects from an F# Perspective
One of the key advances in programming has been the move toward the use of “abstract” values rather than “concrete” values for large portions of modern software. In this chapter we’ve seen how to define types, and how to use types to model abstract values. The primary techniques used to build abstract values in F# are as follows: * Function types and values. These are used both as abstractions themselves and as a building block to form other abstractions; * Record types and values. Records can be used as both “abstract” entites (e.g. records of function values) and “concrete” entites containing (possibly mutable) data. It is often important to distinguish between these roles. * Interface types and object values. Interface types are similar to record types. They are often implemented by object expressions, and have the added advantage that they may be arranged in hierarchies. Other types can be declared to implement interface types, as long as a default implementation of the implementation is provided.

* Class types and object values. Like records, class types can be used to both implement and define abstractions. In the .NET libraries many important abstractions are modeled as class types, e.g. the classes Control and Form in the System.Windows.Forms namespace. A class can be thought of as the combination of an abstraction (i.e. its members plus its explicit interface implementations) and an implementation. The implementation may partial (e.g. with unimplemented abstract members) or configurable (e.g. with mutable properties). * Module signature types and module implementations. The signatures of modules and type definitions are a form of abstraction, though they are typically associated with only one implementation. In a statically typed language such as F# it’s not too surprising that abstract values are modeled using types. Indeed, often all the functionality provided by a value is represented by its static type. However, in some cases further functionality on a value may be discoverable by using runtime type tests. For example, you may be able to “discover” that a value provides an implementation of an abstraction by performing a runtime type test.

Note: In OO languages implementing abstractions in multiple ways is commonly called “polymorphism”, which we will call “polymorphism of implementation”. Polymorphism of this kind is present throughout F#, and not just with respect to the OO constructs such as classes and interfaces. Somewhat confusingly, in functional programming “polymorphism” is used to mean “generic type parameters”, an orthogonal concept dsicsused in Chapter 2.

F# and Mutation
Sometimes OO programming is presented primarily as a technique for controlling the complexity of mutable state. However, many of the other traditional concerns of objectoriented (OO) programming are orthogonal this. For example, higher-level programming techniques such as interfaces, inheritance and patterns such as publish/subscribe stem from the OO tradition, and techniques such as functions, type abstraction and functorial operations such as “map” and “fold” from the value-oriented tradition. None of these techniques have any fundamental relationship to mutation and identity: for example interfaces and inheritance can be used very effectively in the context of value-oriented programming. Much of the success of F# lies in the way that it brings the techniques of OO programming and valueoriented programming comfortably together.

F# and Implementation Inheritance
In OO programming it has ben traditional to arrange partial implementations of fragments using implementation inheritance. However, this technique tends to be much less significant in F# because of the flexibility that functional programming and F# object expressions provide for defining and sharing implementation fragments. For example, the Microsoft.FSharp.Collections.IEnumerable module (opened using open IEnumerable) provides several partial implementations of the IEnumerable<T> abstraction, but does not

use implementation inheritance at all. This is because partial implementations of objects can be encoded in a number of ways: * Through construction functions and methods that take function values as arguments * Through values that implement abstractions and which can be customized by setting mutable properties * Through hierarchies of classes Nevertheless, hierarchies of classes are important in domains such as GUI programming and the technique is used heavily by .NET libraries written in other .NET languages. For example, System.Windows.Forms.Control, System.Windows.Forms.Form and System.Windows.Forms.RichTextBox are part of a hierarchy of visual GUI elements.