Click here to Skip to main content
15,890,186 members
Articles / Programming Languages / F#

F#20 : Creating Types / Adding Members

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
24 Apr 2014CPOL5 min read 14.8K   2   1
We now start the OO leg of our F# journey, where we will look at how to create classes (generic classes too) and use OO things like inheritance/interfaces, and we shall also look at how to use events within our classes, and how to use reflection to do meta programming against our classes instances.

We now start the OO leg of our F# journey, where we will look at how to create classes (generic classes too) and use OO things like inheritance/interfaces, and we shall also look at how to use events within our classes, and how to use reflection to do meta programming against our classes instances.

Creating Types

We have already seen a few example of types being created on our journey so far, is may have passed you by which is fine, so here are a few ground rules when creating custom types

  • Types MUST live in a module
  • Types that do not contain any constructors/members at all, that are just marker classes MUST use the class/end class syntax

Let’s have a look at these 2 points in a bit more detail shall we.

So for point 1, what does that really mean? So if I have some code like the example shown below, is it valid? The answer is no….And why is the answer no? Well it is due to the fact the Person type is declared within the main method, which is not valid. As previously stated a type must be declared in a module, so to fix this we simply need to move the type to a dedicated module. That would fix point 1.

image

The next point from the above list means that you are not really allowed to do this, this is not a valid type definition, as it doesn’t contain any members

image

This is easily rectified using the class/end class syntax, which can be used just like this. Remember you ONLY need this if you want to create some sort of marker class, I don’t know how often you would like to do this, but if you do this is how you would do it

image

Constructors

In F# there is always a primary constructor which follows the types name which takes the arguments that are used to create an instance of the type. The primary constructor may contain let and do bindings, which according to MSDN work this way:

The let and do bindings in a class definition form the body of the primary class constructor, and therefore they run whenever a class instance is created. If a let binding is a function, then it is compiled into a member. If the let binding is a value that is not used in any function or member, then it is compiled into a variable that is local to the constructor. Otherwise, it is compiled into a field of the class. The do expressions that follow are compiled into the primary constructor and execute initialization code for every instance. Because any additional constructors always call the primary constructor, the let bindings and do bindings always execute regardless of which constructor is called.

Fields that are created by let bindings can be accessed throughout the methods and properties of the class; however, they cannot be accessed from static methods, even if the static methods take an instance variable as a parameter. They cannot be accessed by using the self identifier, if one exists.

http://msdn.microsoft.com/en-us/library/dd233205.aspx

Types may also have other constructors that MUST call the primary constructor, this is easily done using the new keyword which may be an empty constructor, or may take arguments.

Here is an example of a type that has 3 constructors

// The class's primary constructor takes two arguments: firstName and lastName,
//both of type 'string'.
type public Person(firstName : string, lastName : string) =
    /// The fullName of the Person, computed when the object is constructed
    let fullName = String.Format("{0} {1}",firstName, lastName)
 
    //2nd constructor
    new() = Person("", "")
    //3rd constructor
    new(firstName : string) = Person(firstName, "")
 
    // 'this' specifies a name for the object's self identifier.
    // In instance methods, it must appear before the member name.
    member public this.FirstName = firstName
    member public this.LastName = lastName
    member public this.FullName = fullName

Which can be used as follows:

let p1 = new Person("sacha","Barber")
let p2 = Person("sacha","Barber")
let p3 = new Person()
let p4 = new Person("Steve")
 
do printfn "p1 = FirstName=%A, LastName=%A, FullName=%A" p1.FirstName p1.LastName p1.FullName
do printfn "p2 = FirstName=%A, LastName=%A, FullName=%A" p2.FirstName p2.LastName p2.FullName
do printfn "p3 = FirstName=%A, LastName=%A, FullName=%A" p3.FirstName p3.LastName p3.FullName
do printfn "p4 = FirstName=%A, LastName=%A, FullName=%A" p4.FirstName p4.LastName p4.FullName

Which when run gives the following output

image

Dependant Types

In one of the posts we already covered we saw how to deal with dependant types, that is Type A depends on Type B, and Type B depends on Type A. Here is an example of the problem:

image

In order to do that we can just loose the dependant type keyword, and replace it with the and keyword, which if you recall was done like this:

type Person(manager :Manager) =
    member this.Manager = manager
and Manager(name:string) =
    let underlings = new List()
             
    member this.Underlings =  underlings

For more information on this particular subject you can read the other post :

sachabarbs.wordpress.com/2014/04/09/f-15-code-organization-modules-files-types/

Adding Members

To add members to a type you just need use the self identifier followed by the period then a name. Members belong to an instance, not the type. Here is a trivial example of a “PrettyPrint” method, which has been added to our on going example

// The class's primary constructor takes two arguments: firstName and lastName,
//both of type 'string'.
type public Person(firstName : string, lastName : string) =
    /// The fullName of the Person, computed when the object is constructed
    let fullName = String.Format("{0} {1}",firstName, lastName)
 
    //2nd constructor
    new() = Person("", "")
    //3rd constructor
    new(firstName : string) = Person(firstName, "")
 
    // 'this' specifies a name for the object's self identifier.
    // In instance methods, it must appear before the member name.
    member public this.FirstName = firstName
    member public this.LastName = lastName
    member public this.FullName = fullName
 
    member this.PrettyPrint() =
        printfn "FirstName %s, LastName %s" this.FirstName this.LastName |> ignore

Which we can use like this:

let p1 = new Person("sacha","Barber")
printfn "let p1 = new Person(\"sacha\",\"Barber\")"
printfn "p1.PrintyPrint()"
do p1.PrettyPrint()

Which gives us this result:

image

Static Members

You may also add static members to a class which is done as follows:

Just like in any other .NET language a static member belongs to the type and not the instance, so to use it we simply use the type name followed by the member name. Here is the same demo class we have been building up, with a static “LastNameFormat” method which returns a tuple of the passed in firstName and LastName reversed:

// The class's primary constructor takes two arguments: firstName and lastName,
//both of type 'string'.
type public Person(firstName : string, lastName : string) =
    /// The fullName of the Person, computed when the object is constructed
    let fullName = String.Format("{0} {1}",firstName, lastName)
 
    //2nd constructor
    new() = Person("", "")
    //3rd constructor
    new(firstName : string) = Person(firstName, "")
 
    // 'this' specifies a name for the object's self identifier.
    // In instance methods, it must appear before the member name.
    member public this.FirstName = firstName
    member public this.LastName = lastName
    member public this.FullName = fullName
 
    member this.PrettyPrint() =
        printfn "FirstName %s, LastName %s" this.FirstName this.LastName |> ignore
 
    static member LastNameFormat(f: string, l: string) = (l,f)

Which we make use of like this:

let p1 = new Person("sacha","Barber")
 
printfn "let p1 = new Person(\"sacha\",\"Barber\")"
printfn "Person.LastNameFormat = %A"  (Person.LastNameFormat(p1.FirstName, p1.LastName))

Which gives this result:

image

Adding Properties

There are 2 main areas that we need to cover when talking about properties:

  • Get only properties
  • Get and (possibly private) set

For immutable properties the syntax is simple we can simply do something like this:

type foo(name) =
    member this.Name = name

For mutable properties it gets a little more complex, where we have the following sort of syntax:

type foo(name) =
    // myName is private
    let mutable myName = name
 
    member this.MyName
        with get() = myName
        and set(value) = myName <- value

TIP : To make a set private you can use “private set” instead

From VS2012 onwards F# supports auto properties, which you can use to simplify your code, here are some examples of how you can use that.

type foo(name: string, age:int) =
 
    //immutable property
    member val MyName = name
 
    //mutable property
    member val Age =  age with get, set

Access Modifiers

In F# you do not have the full gamut of access modifiers you may be used to if you are coming from C#, you are limited to:

public : indicates the entity can be accessed by all callers

internal : indicates the entity can be accessed only by the same assembly

private : indicates the entity can be accessed only from the enclosing type of module

Access modifiers can be applied to modules, types, methods, value definitions, functions, properties and explicit fields

  • Access modifiers are generally put in front of the name of the entity. If no access modifier is used the default is public except in the case of let bindings in a type, which are always private to the type.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
GeneralMy vote of 5 Pin
John Donaghan5-May-14 21:48
John Donaghan5-May-14 21:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.