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
- Introduction to Functional Programming via circuits
- Lexing and Parsing
- Interpreters
- Simple ‘Functional’ Distributed/Concurrent Programming
- Non-Functional bits - Assignment, Exceptions, Objects, Libraries, etc. (this part)
- Advanced stuff - Continuations, Monads, Generative Programming (Language Based programming), etc.
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 :
Post a Comment