Classes
This post and the next will cover the basics of creating and using classes and methods in F#.
Just like all other data types in F#, class definitions start with the type
keyword.
The thing that distinguishes them from other types is that classes always have some parameters passed in when they are created – the constructor – and so there are always parentheses after the class name.
Also, unlike other types, classes must have functions attached to them as members. This post will explain how you do this for classes, but for a general discussion of attaching functions to other types see the post on type extensions.
So, for example, if we want to have a class called CustomerName
that requires three parameters to construct it, it would be written like this:
type CustomerName(firstName, middleInitial, lastName) =
member this.FirstName = firstName
member this.MiddleInitial = middleInitial
member this.LastName = lastName
Let’s compare this with the C# equivalent:
public class CustomerName
{
public CustomerName(string firstName,
string middleInitial, string lastName)
{
this.FirstName = firstName;
this.MiddleInitial = middleInitial;
this.LastName = lastName;
}
public string FirstName { get; private set; }
public string MiddleInitial { get; private set; }
public string LastName { get; private set; }
}
You can see that in the F# version, the primary constructor is embedded into the class declaration itself — it is not a separate method. That is, the class declaration has the same parameters as the constructor, and the parameters automatically become immutable private fields that store the original values that were passed in.
So in the above example, because we declared the CustomerName
class as:
type CustomerName(firstName, middleInitial, lastName)
therefore firstName
, middleInitial
, and lastName
automatically became immutable private fields.
You might not have noticed, but the CustomerName
class defined above does not constrain the parameters to be strings, unlike the C# version. In general, type inference from usage will probably force the values to be strings, but if you do need to specify the types explicitly, you can do so in the usual way with a colon followed by the type name.
Here’s a version of the class with explicit types in the constructor:
type CustomerName2(firstName:string,
middleInitial:string, lastName:string) =
member this.FirstName = firstName
member this.MiddleInitial = middleInitial
member this.LastName = lastName
One little quirk about F# is that if you ever need to pass a tuple as a parameter to a constructor, you will have to annotate it explicitly, because the call to the constructor will look identical:
type NonTupledConstructor(x:int,y: int) =
do printfn "x=%i y=%i" x y
type TupledConstructor(tuple:int * int) =
let x,y = tuple
do printfn "x=%i y=%i" x y
// calls look identical
let myNTC = new NonTupledConstructor(1,2)
let myTC = new TupledConstructor(1,2)
The example class above has three read-only instance properties. In F#, both properties and methods use the member
keyword.
Also, in the example above, you see the word “this
” in front of each member name. This is a “self-identifier” that can be used to refer to the current instance of the class. Every non-static member must have a self-identifier, even it is not used (as in the properties above). There is no requirement to use a particular word, just as long as it is consistent. You could use “this” or “self” or “me” or any other word that commonly indicates a self reference.
When a class is compiled (or when you over hover the definition in the editor), you see the “class signature” for the class. For example, for the class definition:
type MyClass(intParam:int, strParam:string) =
member this.Two = 2
member this.Square x = x * x
the corresponding signature is:
type MyClass =
class
new : intParam:int * strParam:string -> MyClass
member Square : x:int -> int
member Two : int
end
The class signature contains the signatures for all the constructors, methods and properties in the class. It is worth understanding what these signatures mean, because, just as with functions, you can understand what the class does by looking at them. It is also important because you will need to write these signatures when creating abstract methods and interfaces.
Method signatures such as are very similar to the signatures for standalone functions, except that the parameter names are part of the signature itself.
So in this case, the method signature is:
member Square : x:int -> int
And for comparison, the corresponding signature for a standalone function would be:
val Square : int -> int
Constructor signatures are always called new
, but other than that, they look like a method signature.
Constructor signatures always take tuple values as their only parameter. In this case the tuple type is int * string
, as you would expect. The return type is the class itself, again as you would expect.
Again, we can compare the constructor signature with a similar standalone function:
// class constructor signature
new : intParam:int * strParam:string -> MyClass
// standalone function signature
val new : int * string -> MyClass
Finally, property signatures such as member Two : int
are very similar to the signatures for standalone simple values, except that no explicit value is given.
// member property
member Two : int
// standalone value
val Two : int = 2
After the class declaration, you can optionally have a set of “let” bindings, typically used for defining private fields and functions.
Here’s some sample code to demonstrate this:
type PrivateValueExample(seed) =
// private immutable value
let privateValue = seed + 1
// private mutable value
let mutable mutableValue = 42
// private function definition
let privateAddToSeed input =
seed + input
// public wrapper for private function
member this.AddToSeed x =
privateAddToSeed x
// public wrapper for mutable value
member this.SetMutableValue x =
mutableValue <- x
// test
let instance = new PrivateValueExample(42)
printf "%i" (instance.AddToSeed 2)
instance.SetMutableValue 43
In the example above, there are three let bindings:
privateValue
is set to the initial seed plus 1mutableValue
is set to 42- The
privateAddToSeed
function uses the initial seed plus a parameter
Because they are let bindings, they are automatically private, so to access them externally, there must be a public member to act as a wrapper.
Note that the seed
value passed into the constructor is also available as a private field, just like the let-bound values.
Sometimes, you want a parameter passed to the constructor to be mutable. You cannot specify this in the parameter itself, so the standard technique is to create a mutable let-bound value and assign it from the parameter, as shown below:
type MutableConstructorParameter(seed) =
let mutable mutableSeed = seed
// public wrapper for mutable value
member this.SetSeed x =
mutableSeed <- x
In cases, like this, it is quite common to give the mutable value the same name as the parameter itself, like this:
type MutableConstructorParameter2(seed) =
let mutable seed = seed // shadow the parameter
// public wrapper for mutable value
member this.SetSeed x =
seed <- x
In the CustomerName
example earlier, the constructor just allowed some values to be passed in but didn’t do anything else. However, in some cases, you might need to execute some code as part of the constructor. This is done using do
blocks.
Here’s an example:
type DoExample(seed) =
let privateValue = seed + 1
//extra code to be done at construction time
do printfn "the privateValue is now %i" privateValue
// test
new DoExample(42)
The “do” code can also call any let-bound functions defined before it, as shown in this example:
type DoPrivateFunctionExample(seed) =
let privateValue = seed + 1
// some code to be done at construction time
do printfn "hello world"
// must come BEFORE the do block that calls it
let printPrivateValue() =
do printfn "the privateValue is now %i" privateValue
// more code to be done at construction time
do printPrivateValue()
// test
new DoPrivateFunctionExample(42)
One of the differences between the “do” and “let” bindings is that the “do” bindings can access the instance while “let” bindings cannot. This is because “let” bindings are actually evaluated before the constructor itself (similar to field initializers in C#), so the instance in a sense does not exist yet.
If you need to call members of the instance from a “do” block, you need some way to refer to the instance itself. This is again done using a “self-identifier”, but this time it is attached to the class declaration itself.
type DoPublicFunctionExample(seed) as this =
// Note the "this" keyword in the declaration
let privateValue = seed + 1
// extra code to be done at construction time
do this.PrintPrivateValue()
// member
member this.PrintPrivateValue() =
do printfn "the privateValue is now %i" privateValue
// test
new DoPublicFunctionExample(42)
In general though, it is not best practice to call members from constructors unless you have to (e.g. calling a virtual method). Better to call private let-bound functions, and if necessary, have the public members call those same private functions.
A method definition is very like a function definition, except that it has the member
keyword and the self-identifier instead of just the let
keyword.
Here are some examples:
type MethodExample() =
// standalone method
member this.AddOne x =
x + 1
// calls another method
member this.AddTwo x =
this.AddOne x |> this.AddOne
// parameterless method
member this.Pi() =
3.14159
// test
let me = new MethodExample()
printfn "%i" <| me.AddOne 42
printfn "%i" <| me.AddTwo 42
printfn "%f" <| me.Pi()
You can see that, just like normal functions, methods can have parameters, call other methods, and be parameterless (or to be precise, take a unit parameter)
Unlike normal functions, methods with more than one parameter can be defined in two different ways:
- The curried form, where parameters are separated with spaces, and partial application is supported. (Why “curried”? See the explanation of currying.)
- The tuple form, where all the parameters as passed in at the same time, comma-separated, in a single tuple.
The curried approach is more functional, and the tuple approach is more object-oriented. Here is an example class with a method for each approach:
type TupleAndCurriedMethodExample() =
// curried form
member this.CurriedAdd x y =
x + y
// tuple form
member this.TupleAdd(x,y) =
x + y
// test
let tc = new TupleAndCurriedMethodExample()
printfn "%i" <| tc.CurriedAdd 1 2
printfn "%i" <| tc.TupleAdd(1,2)
// use partial application
let addOne = tc.CurriedAdd 1
printfn "%i" <| addOne 99
So which approach should you use?
The advantages of tuple form are:
- Compatible with other .NET code
- Supports named parameters and optional parameters
- Supports method overloads (multiple methods with the same name that differ only in their function signature)
On the other hand, the disadvantages of tuple form are:
- Doesn’t support partial application
- Doesn’t work well with higher order functions
- Doesn’t work well with type inference
For a more detailed discussion on tuple form vs. curried form see the post on type extensions.
A common pattern is to create let-bound functions that do all the heavy lifting, and then have the public methods call these internal functions directly. This has the benefit that the type inference works much better with functional-style code than with methods.
Here’s an example:
type LetBoundFunctions() =
let listReduce reducer list =
list |> List.reduce reducer
let reduceWithSum sum elem =
sum + elem
let sum list =
list |> listReduce reduceWithSum
// finally a public wrapper
member this.Sum = sum
// test
let lbf = new LetBoundFunctions()
printfn "Sum is %i" <| lbf.Sum [1..10]
For more details on how to do this, see this discussion.
Unlike normal let-bound functions, methods that are recursive do not need the special rec
keyword. Here’s the boringly familiar Fibonacci function as a method:
type MethodExample() =
// recursive method without "rec" keyword
member this.Fib x =
match x with
| 0 | 1 -> 1
| _ -> this.Fib (x-1) + this.Fib (x-2)
// test
let me = new MethodExample()
printfn "%i" <| me.Fib 10
As usual, the types for a method’s parameters and return value can normally be inferred by the compiler, but if you need to specify them, you do so in the same way that you would for a standard function:
type MethodExample() =
// explicit type annotation
member this.AddThree (x:int) :int =
x + 3
Properties can be divided into three groups:
- Immutable properties, where there is a “get” but no “set”.
- Mutable properties, where there is a “get” and also a (possibly private) “set”.
- Write-only properties, where there is a “set” but no “get”. These are so unusual that I won’t discuss them here, but the MSDN documentation describes the syntax if you ever need it.
The syntax for immutable and mutable properties is slightly different.
For immutable properties, the syntax is simple. There is a “get” member that is similar to a standard “let” value binding. The expression on the right-hand side of the binding can be any standard expression, typically a combination of the constructor parameters, private let-bound fields, and private functions.
Here’s an example:
type PropertyExample(seed) =
// immutable property
// using a constructor parameter
member this.Seed = seed
For mutable properties however, the syntax is more complicated. You need to provide two functions, one to get and one to set. This is done by using the syntax:
with get() = ...
and set(value) = ...
Here’s an example:
type PropertyExample(seed) =
// private mutable value
let mutable myProp = seed
// mutable property
// changing a private mutable value
member this.MyProp
with get() = myProp
and set(value) = myProp <- value
To make the set function private, use the keywords private set
instead.
Starting in VS2012, F# supports automatic properties, which remove the requirement to create a separate backing store for them.
To create an immutable auto property, use the syntax:
member val MyProp = initialValue
To create a mutable auto property, use the syntax:
member val MyProp = initialValue with get,set
Note that in this syntax there is a new keyword val
and the self-identifier has gone.
Here’s a complete example that demonstrates all the property types:
type PropertyExample(seed) =
// private mutable value
let mutable myProp = seed
// private function
let square x = x * x
// immutable property
// using a constructor parameter
member this.Seed = seed
// immutable property
// using a private function
member this.SeedSquared = square seed
// mutable property
// changing a private mutable value
member this.MyProp
with get() = myProp
and set(value) = myProp <- value
// mutable property with private set
member this.MyProp2
with get() = myProp
and private set(value) = myProp <- value
// automatic immutable property (in VS2012)
member val ReadOnlyAuto = 1
// automatic mutable property (in VS2012)
member val ReadWriteAuto = 1 with get,set
// test
let pe = new PropertyExample(42)
printfn "%i" <| pe.Seed
printfn "%i" <| pe.SeedSquared
printfn "%i" <| pe.MyProp
printfn "%i" <| pe.MyProp2
// try calling set
pe.MyProp <- 43 // Ok
printfn "%i" <| pe.MyProp
// try calling private set
pe.MyProp2 <- 43 // Error
At this point you might be confused by the difference between properties and parameterless methods. They look identical at first glance, but there is a subtle difference – “parameterless” methods are not really parameterless; they always have a unit parameter.
Here’s an example of the difference in both definition and usage:
type ParameterlessMethodExample() =
member this.MyProp = 1 // No parens!
member this.MyFunc() = 1 // Note the ()
// in use
let x = new ParameterlessMethodExample()
printfn "%i" <| x.MyProp // No parens!
printfn "%i" <| x.MyFunc() // Note the ()
You can also tell the difference by looking at the signature of the class definition
The class definition looks like this:
type ParameterlessMethodExample =
class
new : unit -> ParameterlessMethodExample
member MyFunc : unit -> int
member MyProp : int
end
The method has signature MyFunc : unit -> int
and the property has signature MyProp : int
.
This is very similar to what the signatures would be if the function and property were declared standalone, outside of any class:
let MyFunc2() = 1
let MyProp2 = 1
The signatures for these would look like:
val MyFunc2 : unit -> int
val MyProp2 : int = 1
which is almost exactly the same.
If you are unclear on the difference and why the unit parameter is needed for the function, please read the discussion of parameterless methods.
In addition to the primary constructor embedded in its declaration, a class can have additional constructors. These are indicated by the new
keyword and must call the primary constructor as their last expression.
type MultipleConstructors(param1, param2) =
do printfn "Param1=%i Param12=%i" param1 param2
// secondary constructor
new(param1) =
MultipleConstructors(param1,-1)
// secondary constructor
new() =
printfn "Constructing..."
MultipleConstructors(13,17)
// test
let mc1 = new MultipleConstructors(1,2)
let mc2 = new MultipleConstructors(42)
let mc3 = new MultipleConstructors()
Just as in C#, classes can have static members, and this is indicated with the static
keyword. The static
modifier comes before the member keyword.
Members which are static cannot have a self-identifier such as “this” because there is no instance for them to refer to.
type StaticExample() =
member this.InstanceValue = 1
static member StaticValue = 2 // no "this"
// test
let instance = new StaticExample()
printf "%i" instance.InstanceValue
printf "%i" StaticExample.StaticValue
There is no direct equivalent of a static constructor in F#, but you can create static let-bound values and static do-blocks that are executed when the class is first used.
type StaticConstructor() =
// static field
static let rand = new System.Random()
// static do
static do printfn "Class initialization!"
// instance member accessing static field
member this.GetRand() = rand.Next()
You can control the accessibility of a member with the standard .NET keywords public
, private
and internal
. The accessibility modifiers come after the member
keyword and before the member name.
Unlike C#, all class members are public by default, not private. This includes both properties and methods. However, non-members (e.g. let declarations) are private and cannot be made public.
Here’s an example:
type AccessibilityExample() =
member this.PublicValue = 1
member private this.PrivateValue = 2
member internal this.InternalValue = 3
// test
let a = new AccessibilityExample();
printf "%i" a.PublicValue
printf "%i" a.PrivateValue // not accessible
For properties, if the set and get have different accessibilities, you can tag each part with a separate accessibility modifier.
type AccessibilityExample2() =
let mutable privateValue = 42
member this.PrivateSetProperty
with get() =
privateValue
and private set(value) =
privateValue <- value
// test
let a2 = new AccessibilityExample2();
printf "%i" a2.PrivateSetProperty // ok to read
a2.PrivateSetProperty <- 43 // not ok to write
In practice, the “public get, private set” combination that is so common in C# is not generally needed in F#, because immutable properties can be defined more elegantly, as described earlier.
If you are defining classes that need to interop with other .NET code, do not define them inside a module! Define them in a namespace instead, outside of any module.
The reason for this is that F# modules are exposed as static classes, and any F# classes defined inside a module are then defined as nested classes within the static class, which can mess up your interop. For example, some unit test runners don’t like static classes.
F# classes which are defined outside a module are generated as normal top-level .NET classes, which is probably what you want. But remember that (as discussed in a previous post) if you don’t declare a namespace specifically, your class will be placed in an automatically generated module, and will be nested without your knowledge.
Here’s an example of two F# classes, one defined outside a module and one defined inside:
// Note: this code will not work in an .FSX script,
// only in an .FS source file.
namespace MyNamespace
type TopLevelClass() =
let nothing = 0
module MyModule =
type NestedClass() =
let nothing = 0
And here’s how the same code might look in C#:
namespace MyNamespace
{
public class TopLevelClass
{
// code
}
public static class MyModule
{
public class NestedClass
{
// code
}
}
}
Now that we have defined the class, how do we go about using it?
One way to create an instance of a class is straightforward and just like C# – use the new
keyword and pass in the arguments to the constructor.
type MyClass(intParam:int, strParam:string) =
member this.Two = 2
member this.Square x = x * x
let myInstance = new MyClass(1,"hello")
However, in F#, the constructor is considered to be just another function, so you can normally eliminate the new
and call the constructor function on its own, like this:
let myInstance2 = MyClass(1,"hello")
let point = System.Drawing.Point(1,2) // works with .NET classes too!
In the case when you are creating a class that implements IDisposible
, you will get a compiler warning if you do not use new
.
let sr1 = System.IO.StringReader("") // Warning
let sr2 = new System.IO.StringReader("") // OK
This can be a useful reminder to use the use
keyword instead of the let
keyword for disposables. See the post on use
for more.
And once you have an instance, you can “dot into” the instance and use any methods and properties in the standard way.
myInstance.Two
myInstance.Square 2
We have seen many examples of member usage in the above discussion, and there’s not too much to say about it.
Remember that, as discussed above, tuple-style methods and curried-style methods can be called in distinct ways:
type TupleAndCurriedMethodExample() =
member this.TupleAdd(x,y) = x + y
member this.CurriedAdd x y = x + y
let tc = TupleAndCurriedMethodExample()
tc.TupleAdd(1,2) // called with parens
tc.CurriedAdd 1 2 // called without parens
2 |> tc.CurriedAdd 1 // partial application