The Salty Economist

Things I Should Have Learned in High School
posts - 56, comments - 0, trackbacks - 0

F# - Classes (In progress)

So, today I decided I would try to figure out how classes work in F#.

I have decided to make a Bank object.  The first thing I tried is this:

#light

type Bank() = class
    nCustomers : int
    nEmployees : int
end

This does not work!  I get the error:

Program.fs(6,5): error FS0010: Unexpected identifier in member definition.

Who knows what that means.

So, I start poking around and find that I need to use the 'val' key word to create a public property.  So the next thing I tried is this:

type Bank() = class
    val nCustomers : int
    val nEmployees : int
end

This also does not work!  I get the error:

Program.fs(6,9): error FS0191: Uninitialized 'val' fields in implicit construction types must be mutable and marked with the '[<DefaultValue>]' attribute. Consider using a 'let' binding instead of a 'val' field

After poking around on Mister Softy, I find I need to do it like this:

type Bank() = class
    [<DefaultValue>] val mutable nCustomers : int
    [<DefaultValue>] val mutable private nEmployees : int
end

So this is why:

(1)  I need the val keyword in conjunction with the mutable keyword in order to make the property public.  That is the default.  Note that my property nEmployees uses the keyword 'private' to make the property private.  The let construct will not work if you want the property to be public.  Let bindings are always private.

(2)  I really do need both the val and mutable keywords.

(3)  In this example, I use the default constructor -- these are the open parentheses in the Bank() statement.  Therefore, my properties are initialized.  In F# this is a no no and is not allowed.  When you do not initialize a variable you need to use the Default attribute to initialize the value to Zero, it that is possible.  From Microsoft:

"The DefaultValue attribute is required on explicit fields in class types that have a primary constructor. This attribute specifies that the field is initialized to zero. The type of the field must support zero-initialization."

So, the above statement gives me a class with one public variable (nCustomers) initialized at zero and one private variable, also initialized at zero.

The following code snippet shows how I instantiate a Bank and assign it a nCustomers:

let BofA = new Bank()

//The following does not work
//let BofA.nCustomers = 4300

BofA.nCustomers <- 4300

Now what if I do want to use a constructor instead of the default constructor.  I can create a constructor thusly:

type Bank = class

    [<DefaultValue>] val mutable nCustomers : int
    [<DefaultValue>] val mutable private nEmployees : int

    new(nCust) = {nCustomers = nCust}

end

In the above snippet, I now create a constructor and use it to assign one of my properties nCustomers.  The above, however, does not work.  I get this error:

Program.fs(10,18): error FS0191: Extraneous fields have been given values

I guess F# can't deal with both a constructor and a default initialization at the same time.  So, I need to write it as such:

type Bank = class

    val mutable nCustomers : int
    [<DefaultValue>] val mutable private nEmployees : int

    new(nCust) = {nCustomers = nCust}

end

I need to take out the attribute [<DefaultValue>] if the property is assigned by the constructor.  I guess that makes sense.

So, now I can instantiate my Bank and then cut the numbers of customers as as such:

let BofA = new Bank(5400)

BofA.nCustomers <- 4300

If I want to initialize both my properties, I would make by constructor as follows: 

type Bank = class

    val mutable nCustomers : int
    val mutable private nEmployees : int

    new(nCust, nEmp) = {nCustomers = nCust; nEmployees = nEmp}

end

You separate your initialization statements with a semi-colon.

You can also set up a class using an implicit constructor as such:

type Bank(nCust : int, nEmp : int) = class

    let mutable _Cust : int = nCust
    let mutable _Emp : int = nEmp
   
    member x.nCustomers with get() = _Cust and set(v) = _Cust <- v
    member x.nEmployees with get() = _Emp and set(v) = _Emp <- v
        
end

This format allows you to put the constructor in the class header.  You then set up get and set statements as needed for your class properties.  Note that you don't need both a get and set statement.  If the property is read only, you would use the syntax:

member x.nCustomers with get() = _Cust

Or, if you limited the property to write only the syntax would be:

member x.nCustomers with set(v) = _Cust <- v

So what is the "x." doodad for?  Well, I can tell you one thing, if you don't use it you will get an invalid syntax error.

The x. really is the class "self" identifier, like Me or this.  Microsoft points out that is great because "Unlike in other .NET languages, you can name the self identifier however you want; you are not restricted to names such as self, Me, or this."

Inheritance

Inheritance works in F#.  Using an explicit constructor we can create a base class (e.g. Bank) and then have another class, say CitiBank inherit the properties and methods of the Bank class:

type Bank = class

    val mutable nCustomers : int
    val mutable nEmployees : int

    new(nCust, nEmp) = {nCustomers = nCust; nEmployees = nEmp}

end

type CitiBank = class
    inherit Bank

    val nDeposits : float

    new(nCust : int, nEmp : int, xDep : float) =
        {inherit Bank(nCust, nEmp);
         nDeposits = xDep}
   
end

You just need to use the key word inherit (lower case)  in the class declaration.  We can then add additional properties (e.g. deposits) to our new class.  Note the syntax of the new constructor for the CitiBank class  -- you basically call the constructor for the base class, passing along the arguments from the constructor of the higher level class.

If you want to use an implicit constructor, this syntax should work:

type Bank(nCust : int, nEmp : int) = class

    let mutable _Cust : int = nCust
    let mutable _Emp : int = nEmp
   
    member x.nCustomers with get() = _Cust and set(v) = _Cust <- v
    member x.nEmployees with get() = _Emp and set(v) = _Emp <- v

        
end

type FedBank(nCust : int, nEmp : int, xDep : float) = class
    inherit Bank(nCust, nEmp)

    let mutable _Dep : float = xDep
    member x.nDeposits with get() = _Dep and set(v) = _Dep <- v

end

Again, you call the constructor for the base class in the constructor for the higher level class.

Adding a Method

So, now let's add a method called 'AddDeposit'.  We use the member keyword and then define our function.  The function takes a float and adds it to the existing deposit balance.  The function doesn't return anything.

type FedBank(nCust : int, nEmp : int, xDep : float) = class
    inherit Bank(nCust, nEmp)

    let mutable _Dep : float = xDep
    member x.nDeposits with get() = _Dep and set(v) = _Dep <- v

    member x.AddDeposit(aDeposit : float) =
        _Dep <- _Dep + aDeposit
        ()  
       
end

I need to use the 'mutable' syntax because the deposit balance will be constantly changing.

We can now see if it works:

let BofA = new FedBank(5400, 56, 32212.21)

let dep0 = BofA.nDeposits

BofA.AddDeposit(5000.0)

let dep1 = BofA.nDeposits

This does work.

Note that if I had wanted to return the current deposit balance, I would have written my function:

    member x.AddDeposit(aDeposit : float) =
        _Dep <- _Dep + aDeposit
        _Dep 

 

Print | posted on Saturday, September 5, 2009 10:07 AM |

Powered by:
Powered By Subtext Powered By ASP.NET