Inside F#

Brian's thoughts on F# and .NET

The basic syntax of F# – classes, interfaces, and members

Posted by Brian on February 13, 2009

This entry is part of "The Basic Syntax of F#" series:

Today’s blog entry covers the F# syntax for authoring classes, interfaces, and members.  Together with the previous blog entry, this probably covers the most common 90% or so of the language.  Again today I will intentionally err on the side of simplicity, at the expense of total accuracy.

Defining F# types

There are lots of ways to define new types and type names in F#, and most involve declarations with the "type" keyword.  Today I’ll focus on just classes and interfaces; in a future blog entry I’ll describe the syntax for the rest of the kinds of types (records, discriminated unions, enums, type abbreviations, units of measure, structs, delegates, exceptions, and modules).

Classes (sans interfaces or inheritance)

Here is an example skeleton class definition whose only purpose is to demonstrate most of the language syntax involved in defining classes:

type public (*1*) MyClass<‘a> public (*2*) (x, y) (*3*) as this (*4*) =
    static let PI = 3.14
    static do printfn "static constructor"
    let mutable z = x + y
    do  printfn "%s" (this.ToString()) (*5*)
        printfn "more constructor effects"        
    internal new (a) = MyClass(a,a)
    static member StaticProp = PI
    static member StaticMethod a = a + 1
    member internal self.Prop1 = x
    member self.Prop2 with get() = z
                      and set(a) = z <- a
    member self.Method(a,b) = x + y + z + a + b

I’ll discuss each part in turn; the commented numbers are to help call out specific bits in the prose that follows.

A class definition usually starts like this:

type SomeClass(constructor-args) = ...

but there are lots of optional bits that can go before the ‘=’ when defining a class.  Back in the main example,

type public (*1*) MyClass<‘a> public (*2*) (x, y) (*3*) as this (*4*) =

the ‘public’ before (*1*) is the accessibility of the class type itself; new types default to being public.  After the name of the class, if the class is generic, you specify the generic parameters in angle brackets (MyClass<‘a> is a class with one generic parameter; the generic parameter is unused in this example, it just demonstrates the syntax).  The next portion defines the so-called "implicit constructor" (whose meaning is described in more detail below).  The ‘public’ before (*2*) is the accessibility of the implicit constructor (public is the default) and the stuff inside parentheses before (*3*) are the arguments to the implicit constructor.  The "as this" before (*4*) is an optional way to name the current object (you can use any identifier you like, it is lexically scoped to all non-static portions of the class body); the only time you need it is when referring to "this" inside the implicit constructor body (such as on the line marked (*5*) in the main example).

The body of the class conceptually has two portions, ‘let’s and ‘do’s (which run as part of construction), and ‘member’s.  Both portions can have ‘static’ and instance (non-static) parts; non-static is the default.  Names bound by ‘let’s are lexically scoped in the class body (and thus are always "private" to the class).  The non-static ‘let’s and ‘do’s run as the body of the implicit constructor (.ctor), the static ‘let’s and ‘do’s comprise the type’s static constructor (.cctor).  In other words, this code

    static let PI = 3.14 
    static do printfn "static constructor"

runs when something first refers to the enclosing MyClass type, and this code

    let mutable z = x + y
    do  printfn "%s" (this.ToString()) (*5*) 
        printfn "more constructor effects"

runs whenever you create an instance of MyClass.  (As with other .NET languages, it is rare to define a static constructor.)

Members are the public interface to functionality in a class, and come in two main flavors: properties and methods.  Members without arguments are "getter" properties; members with arguments are methods.  From the example:

    static member StaticProp = PI 
    static member StaticMethod a = a + 1 
    member internal self.Prop1 = x 
    member self.Prop2 with get() = z 
                      and set(a) = z <-
    member self.Method(a,b) = x + y + z + a + b

Members can be static or not (non-static is the default).  Non-static (instance) members must declare a self-identifier.  Just as with the optional "as this" from the class declaration, you can use any identifier you like; the identifier is bound to the current object and scoped to the member’s body (in these examples I chose "self").  Members default to being public (though "Prop1" demonstrates that you can add an optional accessibility specifier).  If you want to define a property with a "setter" as well as a "getter", you can use the syntax as in "Prop2". 

To define constructors overloads other than the implicit constructor, use a syntax like this:

    internal new (a) = MyClass(a,a) 

The accessibility specifier is optional (defaults to public, just like other members).  The "body" of the constructor must "call forward" to another constructor (eventually ending up with a call to the implicit constructor).  Pragmatically, this means your implicit constructor must initialize everything, and thus probably takes the most arguments of any constructor overload.

There are a number of features of members I am omitting for simplicity, including named and optional parameters, overloading, events, and fields.  You can read the language spec for more details on these features, or ask questions as a blog comment.

Interfaces

Defining and using interfaces in F# is very straightforward.  An interface is a type that only defines abstract members and/or inherits other interfaces.  Here are a couple examples:

type IFooable =
    abstract member Foo : int -> int

type IQuxable =
    abstract member Qux : unit -> string
    inherit IFooable

"IFooable" is an interface with a method "Foo" that takes an int and returns an int.  "IQuxable" is an interface that inherits "IFooable" and adds another method named "Qux".

To have a class implement an interface, just add an ‘interface…with’ declaration to the end of the class body for each interface you want to implement:

type FooQux() =
    member this.SomeMethod() = printfn "hi"
    interface IQuxable with
        member this.Foo x = x + 1
        member this.Qux() = "qux!"

Here "FooQux" is a class containing a method "SomeMethod" and implementing the "IQuxable" interface.

Note that in F#, when a class implements an interface, the interface implementation is always explicit, which means you need an explicit upcast to call interface methods.  For example

let fooQux = new FooQux()
fooQux.SomeMethod()
let x = (fooQux :> IFooable).Foo 42  // must upcast to call "Foo"

Abstract classes and "virtual" methods

Here is an example of an abstract class:

[<AbstractClass>]
type SomeBase() =
    member this.ConcreteMethod y = y + 1
    abstract member AbstractMethod : int -> int
    abstract member VirtualMethod : int -> int
    default this.VirtualMethod x = x + 1

An abstract class is like a (normal, concrete) class, except it contains some abstract (not implemented) members that a subclass must implement.  What would be a "virtual" method in C# is expressed as an ‘abstract’ method that has a ‘default’ implementation.  An abstract class type must be marked with the "AbstractClass" attribute.

Inheritance

With inheritance come three more F# keywords: ‘inherit’, ‘override’, and ‘base':

type SomeDerived() =
    inherit SomeBase()
    override this.AbstractMethod z = z + 1
    override this.VirtualMethod x =
        1 + base.VirtualMethod x
    member this.OtherMethod() = ()

The ‘inherit’ clause defines the base class of a class, and also calls the base constructor as part of the implicit constructor.  (That is, in this example the zero-argument implicit constructor of "SomeDerived" calls the zero-argument constructor of "SomeBase".)  Abstract members that have no implementation must be overridden by the derived class (or else the derived class can also be marked abstract).  Abstract methods with default implementations can be overridden; to call the inherited method, use the ‘base’ keyword.

Other miscellany

I have intentionally omitted many details to keep this blog entry reasonably short (but still cover all the major common bits of syntax).  Here are a few notable omissions:

  • You can enclose a class body between the tokens ‘class’ and ‘end’.  You can enclose an interface body between the tokens ‘interface’ and ‘end’.  F# normally infers the kind of type being defined, making these tokens unnecessary/redundant.
  • As usual, whitespace matters; though I showed most method/property bodies on a single line of code, more commonly you will have a newline after the ‘=’ and indent the body underneath the declaration, as with "VirtualMethod" in "SomeDerived".
  • I only talked about accessibility (e.g. ‘public’, ‘internal’) and properties (rather than methods) in the first section; you can hopefully extrapolate how to define e.g. an internal virtual property.
About these ads

10 Responses to “The basic syntax of F# – classes, interfaces, and members”

  1. Larry Smith said

    One quick comment — A common data structure has a parent class and a list of child classes, with each child instance having a reference back to its parent. For example, an RSS "Feed" would usually have a list of "Item"s, each Item pointing back to its parent Feed. But since everything must be defined before it\’s used, we have a chicken-and-egg situation. "Feed" must be defined after "Item", so Feed can refer to "Item list". But Item must be defined after Feed, so it can refer to "parent : Feed". And the solution is that oh-so-tiny keyword "and", thus…type Feed (Url: string, Title: string) as this = (* has an Item list *) …andFeedItem (ItemTitle: string, Subjects: string list, PubDate: System.DateTime, Link: string, ParentFeed: Feed) as this = …Larry Smith

  2. Wieslaw said

    There is a way to up cast to interface type shown above.. Is there a way to do this other way round i.e.: having interface object cast it to concrete implementation of this interface?

  3. Brian said

    Yes, see http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!725.entry which shows the downcast operator :?>

  4. Vladimir said

    Hi Brian! Why don\’t you remove spam comments?

  5. Brian said

    I do delete them, about twice a week (I get about 100 spams a week maybe). (Wish Live Spaces were better here!) If you see spam comments more than 4 days old, feel free to send me mail.

  6. Unknown said

    Hi Brian, thanks for your posts. I am a c++ and python programmer, currently learning f# (3 days and enjoying it).I have got two simple questions:1) I thought that "as this" is a name bound to the instance (akin to the this pointer in c++ or to the "self" parameter in python, which is similar in the sense that you can choose any name you like for it). But then in the member definition you use "member self.Prop2". Why don\’t you use member this.Prop2? 2) Is it possible to have multiple inheritance? If it is possible, when you refer to "base" how can it be specified which base is chosen?Thanks,Francesco

  7. Brian said

    Regarding (1), the \’as this\’ has scope over the whole class body, whereas the \’member self.\’ has scope for just that member – I chose different identifiers merely to call out this difference in this short example.Regarding (2), the .Net platform does not allow multiple base classes (one base class, and any number of interfaces).

  8. Unknown said

    Hi Brian. This means that in the member scope the equivalence (self == this) holds true?Regarding 2, thanks for the clarification. That shows that I\’m just learning my way in the .Net platform :-)Francesco

  9. Brian said

    Right, \’self\’ and \’this\’ are two names for the same object in that scope.

  10. […] initialization should be moved to the constructor (the let/do section prior to the members; see this blog for a primer on the F# class/interface […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

Join 30 other followers

%d bloggers like this: