Sunday, December 29, 2013

An introduction to Functional Programming in F# -- Part 5: Objects and Exceptions

This session marks a pause for breath between the introductory parts where we concentrated on doing interesting work with mostly simple immutable lists and with algebraic datatypes, and the last part which will be all heavy lifting, to look at other ways of representing compound data, and how the functional world interacts with the more familiar parts of the .net framework. Also, this week, no exercises either.

I'm keeping this part in mainly for completeness' sake, as a transcription of the original seminar material from '08; rather than for any great insights it offers. In particular, unlike the previous sessions there won't be any of the two steps to comprehend -- first, how the program works, and then with increasing familiarity, why you wouldn't actually implement things in quite that way in a real program. It'll be more like a shopping list instead.


Topics covered in the series


Assignment

Actually we covered assignment and mutability last time.


Tuples

We used tuples -- collections of a fixed number of items, defined by the types of each item, way back in the first session. Recall

The value returned by this function is a 2-tuple of the Bits type -- written Bits * Bits; but in general the types in a tuple will be heterogeneous.

Tuples are usually best employed as ad hoc datastructures; while there are standard functions

for extracting members of a 2-tuple, getting at parts of longer tuples will involve destructuring pattern matching.


Records

A record is more like a class -- or, really, a 'C'-style struct -- in having a series of named fields. We have actually used records in their simplest form, in some of our previous examples

because functions are just values, we can put them into a record just as well as other types. We can be more object like if we mix scalar and function values in a record:

which defines a 2D point type with a displacement-vector behaviour.

Records themselves are immutable, so we can only create new records based off old ones e.g.

There is a copy but replace one or more fields syntax

but this of course violates the contract that the move function displaces its target by the x,y value of the point. However, types can have methods

and with the move member working on the current rather than creation time value of the coordinates, we have a class-like structure, albeit with public fields. And most of the time this is sufficient, if the type itself is not exposed.


Objects and interfaces

When interfacing with .net libraries, we need to define actual objects or interfaces. Our previous point behaviour can be expressed as an interface

which we can implement as

Note that F# syntax requires us to explicitly implement the interface; as a consequence, we also need to cast our concrete implementation explicitly to the interface type when we wish to refer to it as such.

There is an alternative syntax which removes the explicit new member as a constructor, and instead makes the class body act as a constructor function:

The two are similar, but not quite the same; for this second example vx and vy are not (as the result of pasting the code into the interactive prompt shows) instance variables, as they were before, but are in locals in the constructor function.

Alternatively, we can define an abstract base class, rather than an interface. This requires an attribute annotation, rather than a keyword:

Note that this time we don't have to cast anything to the base class in order to invoke the move method. A Point' is an AbstractPoint.


Exceptions

These have the sort of behaviour that we are familiar with

  • Exceptions do not affect the types of functions that throw them.
  • Exceptions “propagate up the call stack” until a handler is found (or to top level).

There are two built-in exception mechanisms

or we can just raise a standard exception type

New exception types can be declared by subclassing, or by ML syntax

Exceptions are handled much like they are in C#/C++/Java...

where the with construct actually allows pattern matching over the exception (so can be the equivalent of multiple catch clauses)

There is a related construct; a finally clause can also be used with try as in C#, to add some code which is always executed:

though there is no portmanteau try ... catch ... finally as there is in C#.

Note that because we are in the .net framework here, exceptions are expensive operations, unlike in OCAML, where they are sufficiently cheap as to be a standard control flow construct.

No comments :