Trustbit

View Original

Introduction to Functional Programming in F# – Part 10

Introduction

In this post we are going to see how we can utilise some of the object programming features that F# offers. F# is a functional-first language but sometimes it is beneficial to use objects, particularly when interacting with code written in other, less functional, .Net languages or when you want to encapsulate some internal data structures and/or mutable state.

F# can do pretty much anything that C#/VB.Net can do with objects. We are going to concentrate on the core object programming features; class types, interfaces, encapsulation and equality.

Setting Up

Open VSCode and add a new folder. Add three new files (FizzBuzz.fs, RecentlyUsedList.fs and Coordinate.fs).

Solving the Problem

We will start in fizzbuzz.fs where we will be implementing FizzBuzz using object programming.

Add a module to the top of the page:

See this content in the original post

Class Types

Now we are going to create our first class type:

See this content in the original post

Points of interest:

  • The brackets () after the type name are required. They can contain argument as we will see soon.

  • The member keyword defines the accessible members of the type.

  • The underscore is just a placeholder - it can be anything. It is convention to use _, __ or this.

Now that we have created our class type, we need to instantiate it to use it.

See this content in the original post

We don't use 'new' to create an instance of FizzBuzz. You only use 'new' when using an object that implements IDisposible and then we would use 'use' instead of 'let' to create a code block.

At the moment, we can only use '[(3, "Fizz");(5, "Buzz")]' as the mapping but it is easy to pass the mapping in through the constructor;

See this content in the original post

Notice that we don't need to assign the constructor argument to a binding to use it.

Now we need to pass the mapping in as the argument to the constructor in the doFizzBuzz function:

See this content in the original post

We can move the function code from the member into the body of the class type as a new inner function:

See this content in the original post

You cannot access the new calculate function from outside the class type. You don't have to do this but I find that it makes the code easier to read, especially as the number of class members increases.

Interfaces

Interfaces are very important in object programming as they define a contract that an implementation must offer. Let's add an interface to our fizzbuzz example:

See this content in the original post

Now we need to implement the interface in our FizzBuzz class type:

See this content in the original post

Nice and easy but you will see that we have a problem; The compiler has highlighted the Calculate function call in our doFizzBuzz function.

See this content in the original post

There is an error because F# does not support implicit casting, so we have to upcast the instance to IFizzBuzz:

See this content in the original post

An alternative would be to upcast as you use the interface function:

See this content in the original post

Just because you can, it doesn't mean you should

The code above is designed to show how to construct class types and use interfaces. If you find yourself constructing interfaces for single functions, ask yourself if you really, really need the extra code and complexity or whether a simple function is enough.

Next we move on to a more complex/realistic example - a recently used list.

Encapsulation

We are going to create a recently used list as a class type. We will encapsulate a mutable collection within the type and provide an interface for how we can interact with it. The recently used list is an ordered list with the most recent item first but it is also a set as each value can only appear once in the list.

First we need to create an interface in RecentlyUsedList.fs:

See this content in the original post

By looking at the signatures, we can see that IsEmpty and Size are read-only properties and Clear, Add and Get are functions. Now we can create our class type and implement the IRecentlyUsedList interface:

See this content in the original post

A ResizeArray is the F# synonym for a standard .Net mutable generic list. Encapsulation ensures that you cannot access it directly (unless you use reflection etc) via the public interface.

Let's test our code in FSI (run each line seperately):

See this content in the original post

Now let's add a maximum size (capacity) to our IRecentlyUsedList interface:

See this content in the original post

You will notice that the compiler is complaining that we haven't implemented all of the interface, so let's fix it, add the capacity as a constructor argument, and add code to the Add function to ensure the oldest item is removed if we are at capacity when adding a new item:

See this content in the original post

All done. Let's test a recently used list with capacity of 5 using FSI:

See this content in the original post

Encaspulation inside class types works really nicely. Now we move on to the issue of equality. Most of the types in F# support structural equality but class tyes do not.

Equality

Let's create a simple class type to store a GPS Coordinate in Coordinate.fs:

See this content in the original post

To test equality, we can write some simple checks we can run in FSI:

See this content in the original post

To support non-referential equality, we need to override GetHashCode and Equals, implement IEquatable and if we are going to use it with other .Net laguages, we need to handle = using op_Equality and allow null literals. This is an example of what we need:

See this content in the original post

We have used two built-in functions (hash and isNull) and we use pattern matching in Equals(obj) to see if the obj can be cast as a GpsCoordinate.

If we test this, we get the expected equality:

See this content in the original post

We have only scratched the surface of what is possible with F# object programming. We will look at other features in a future post. If you want to find out more about this topic (plus many other useful things), I highly recommend that you read Stylish F# by Kit Eason.

Conclusion

In this post we have started to look at object programming in F#. In particular, we have looked at scoping/visibility, encapsulation, interfaces and equality. The more you interact with the rest of the .Net ecosystem, the more you will need to use object programming.

In the next post we will primarily look at recursion.

If you have any comments on this series of posts or suggestions for new ones, send me a tweet (@ijrussell) and let me know.