F Sharp Programming/Interfaces

An object's interface refers to all of the public members and functions that a function exposes to consumers of the object. For example, take the following:

This class contains several public, private, and internal members. However, consumers of this class can only access the public members; when a consumer uses this class, they see the following interface:

Notice the,  ,  ,  , and   members are inaccessible to the outside world, so they are not part of this object's public interface.

All interfaces you've seen so far have been intrinsically tied to a specific object. However, F# and many other OO languages allow users to define interfaces as stand-alone types, allowing us to effectively separate an object's interface from its implementation.

Defining Interfaces
According to the F# specification, interfaces are defined with the following syntax:


 * Note: The interface/end tokens can be omitted when using the #light syntax option, in which case Type Kind Inference (§10.1) is used to determine the kind of the type. The presence of any non-abstract members or constructors means a type is not an interface type.

For example:

Using Interfaces
Since they only define a set of public method signatures, users need to create an object to implement the interface. Here are three classes which implement the ILifeForm interface in fsi:

Typically, we call an interface an abstraction, and any class which implements the interface as a concrete implementation. In the example above,  is an abstraction, whereas ,  , and   are concrete implementations.

Its worth noting that interfaces only define instance members signatures on objects. In other words, they cannot define static member signatures or constructor signatures.

What are interfaces used for and how to use them?
Interfaces are a mystery to newbie programmers (after all, what's the point of creating a type with no implementation?), however they are essential to many object-oriented programming techniques. Interfaces allow programmers to generalize functions to all classes which implement a particular functionality, which is described by the interface, even if those classes don't necessarily descend from one another.

For example, the,  , and   classes defined above contain behavior that is shared, which we defined in the   interface. As the code shows, how the individual classes talk or eat is not defined, but for each class that implements the interface we know that they can eat and speak and have a name. Now we can write a method that accepts just the interface  and we need not worry about how it is implemented, if it is implemented (it always is, the compiler takes care of that) or what type of object it really is. Any other class that implements the same interface, regardless of its other methods, is then automatically supported by this method as well.

Note that in F#, interfaces are implemented explicitly, whereas in C# they are often implemented implicitly. As a consequence, to call a function or method that expects an interface, you have to make an explicit cast:

You can simplify this by letting the compiler help find the proper interface by using the  placeholder:

Implementing Interfaces with Object Expressions
Interfaces are extremely useful for sharing snippets of implementation logic between other classes, however it can be very cumbersome to define and implement a new class for ad hoc interfaces. Object expressions allow users to implement interfaces on anonymous classes using the following syntax:

Using a concrete example, the .NET BCL has a method called, where   exposes a single method called. Let's say we wanted to sort an array on an ad hoc basis using this method; rather than litter our code with one-time use classes, we can use object expressions to define anonymous classes on the fly:

Implementing Multiple Interfaces
Unlike inheritance, its possible to implement multiple interfaces:

Its just as easy to implement multiple interfaces in object expressions as well.

Interface Hierarchies
Interfaces can extend other interfaces in a kind of interface hierarchy. For example:

When users create a concrete implementation of, they are required to provide implementations for all of the methods defined in the  ,  , and   interfaces.


 * Note: Interface hierarchies are occasionally useful, however deep, complicated hierarchies can be cumbersome to work with.

Generalizing a function to many classes
This program has the following types:

This program outputs the following:

Processing...

Handling lifeform 'Fido' Woof! Yum, doggy biscuits!

Handling lifeform 'Monkey!!!' Ook ook Bananas!

Handling lifeform 'Ninjas have no name' Ninjas are silent, deadly killers Ninjas don't eat, they wail on guitars because they're totally sweet

Done.

Using interfaces in generic type definitions
We can constrain generic types in class and function definitions to particular interfaces. For example, let's say that we wanted to create a binary tree which satisfies the following property: each node in a binary tree has two children,  and , where all of the child nodes in   are less than all of its parent nodes, and all of the child nodes in   are greater than all of its parent nodes.

We can implement a binary tree with these properties defining a binary tree which constrains our tree to the  interface.


 * Note: .NET has a number of interfaces defined in the BCL, including the very important . IComparable exposes a single method, , which should return 1, -1, or 0 when the   is greater than, less than, or equal to   respectively. Many classes in the .NET framework already implement IComparable, including all of the numeric data types, strings, and datetimes.

For example, using fsi:

Simple dependency injection
Dependency injection refers to the process of supplying an external dependency to a software component. For example, let's say we had a class which, in the event of an error, sends an email to the network administrator, we might write some code like this:

The  method creates an instance of , so we can say that the   class depends on the   class.

Let's say we're testing our  class, and we don't want to be sending emails to the network admin all the time. Rather than comment out the lines of code we don't want to run while we test, its much easier to substitute the  dependency with a dummy class instead. We can achieve that by passing in our dependency through the constructor:

Now, we create a processor and pass in the kind of FailureNotifier we're interested in. In test environments, we'd use ; in production, we'd use   or.

To demonstrate dependency injection using a somewhat contrived example, the following code in fsi shows how to hot swap one interface implementation with another: