A Taste of What's New in F# 1.1

The first "technology preview" relese of F# 1.1 will be released in the next few days.  This is an exciting time for the project, and we're sure you'll enjoy the additions we've made to the language, the libraries and the interactive development environment. 

One of the really great new additions is "F# Interactive", a top-level interactive environment for F#, which lets you use F# as a type-safe, scalable interactive scripting language.  This is really revolutionizing the way I work with F# - I've used top level environments before, but never one that has such a rich set of foundation libraries, combined with type-safety, type inference, dynamic linking, support for reflective operations and efficient execution. However, I'm going to save a detailed blog entry on this until later, and am also looking forward to having the interactive environment embedded in Visual Studio.

The major addition to the F# language itself in this release is what we're calling the "F# Object and Encapsulation Extensions".  This combines what I've found to be the best features of the .NET object oriented paradigm with the functional programming model that lies at the core of F#.  This means that F# has become a mixed functional/imperative/object-oriented programming language.  If you're an object-oriented programmer, you will find the class description language succinct.  For example, here's a signature for a control from one of the F# Interactive DirectX scripts we've been working on:

type

Canvas3DControl =
  class    inherit Control
new : unit ->Canvas3DControl
memberDevice : Device
    member Scene : List<SceneItem> with get,set
    member Render : unit -> unit
end

For orientation, "new" declares a constructor, "Device" is a readonly property, Scene is a get/set property and Render is a method.  The implementation of the class is essentially as follows:

type

Canvas3DControl =
  class
    val device: Device;
val mutable items : List<SceneItem>;
new() =
{ inherit Control();
device = new Device(...);
items = [] }
    member x.Device = x.device
    member x.Scene
      with get() = x.items
      with set(v) =
x.items <- v;
x.device.Reset(...)
    member x.Render() =
      x.device.BeginScene();
      List.foreach x.itemsD (fun f -> f.Render());
x.device.EndScene();
x.device.Present();
end

You can see from this that implementations of classes have essentially all their types inferred.  Signatures are optional, so this means you can rapidly prototype classes without having to write all those pesky types (The types are statically checked, of course, but type inference just means the compiler works out the types most of the time).  Classes can also include static members, interface implementations, virtual method slot declarations and override declarations.

But we've not only added OO classes to F# - we've brought the goodness of functional programming and the OO paradigm together.  One way in which we've done this by allowing type augmentations.  For example, you can augment any F# type definition with members, regardless of whether the F# type is implemented as a class, record or discriminated union.  In practical terms this means the F# programmer can "dress up" F# types in .NET clothes, and furthermore can make use of types as a basic organising structure within his/her code.  For example, here's the (partial) signature of one of the new types we've added to the F# library:

type

BigNumber 
with    staticmember (+): num * num -> num
    staticmember (*): num * num -> num
    staticmember (-): num * num -> num
    staticmember (/): num * num -> num
    interfaceIComparable
interfaceIStructuralHash
memberDetails : Math.Rational
  end

Note the signature shows that this is an abstract type (not abstract in the C# sense, but rather a "good ol' ADT").  The abstract type comes equipped with static member operators.  Underneath a BigNumber could be anything; class, record or discriminated union.  In this case it is the latter:

type

BigNumber =
  // | I of nativeint
| Z of Math.BigInt
| Q of Math.Rational

This discriminated union says that a BigNumber is implemented as either a Rational or a BigInt, with a comment indicating that we plan to add small machine-sized integers as a representation in the future.  Discriminated unions give internal safety to your code (the compiler tells you if you haven't covered all the cases), and augmentations give you the value of the "dot" notation in external presentation.  

[ Aside: The signature also above mentioned two interfaces - these are for F# comparison and hashing, which we override because the default F# comparison and hashing functions don't guarantee the natural term order we need for this type.  I'll also mention that the F# module containing BigNumber comes with extra F# values for performing additional algorithmic operations on BigNumbers.  Finally a member property Details is shown in the signature.  It is becoming standard in F# code to use a property named "Details" for the "projection idiom", i.e. to represent a member which projects out the logical contents of a type, regardless of representation.  It turns out that BigNumbers are really rationals, and that the Z alternative is only an optimization, hence the Details property returns a rational.   ]

A little more of the implementation is shown below: note how augmentations of F# types can appear after the main definition, though they are currently required to appear in the same file as the original type. 

typeBigNumberwithmember x. Details =
      match x with
| Z z ->Math.Rational .of_z z
|
Q q -> q

interfaceSystem . IComparablewith  member x. CompareTo (y:obj) = compare x (y :?> BigNumber )
   endend

It is very common for types to be defined at the beginning of a file, operator augmentations in the middle and final "member" augmentations to be given at the end. Augmentations mean we achieve the conceptual clarity of an interface organised around the dot-notation, while nearly always maintaining the definition-before-use property that is so characteristic of F#/ML programming.  This is essential, as it is the basis for using F# as a scripting language, and is the property that ensures that F# code is essentially free from the null-pointer problems. 

We're really happy with how the object and encapsulation extensions have turned out - it makes it OO programming very pleasant indeed, and puts added code structuring and dot-notation mehanisms at the disposal of existing F# programmers.   We've worked hard to ensure that members, classes and augmentations interact well with other aspects of F#, e.g. generics, type inference and scripting.  We look forward to your feedback, and feel confident you'll enjoy using the new release when it comes out.