Previous ... Next

Interfaces, subtypes and matching

(1/5)Interfaces, Basis and Details

Interfaces Basis

Interfaces in 42 are quite similar to interfaces in other OO languages. There are however a couple of important differences.

While implementing an interface method, you can avoid the type signature. For example, in the following code, to implement «» inside of «», the types «» and «» are not repeated.

In 42, we say that the method «» implemented in «» is declared by «». Each method is declared at a single point. Method declarations include parameter and return types. A method can be defined (that is, declared and implemented) in the same class; or declared in a (transitively) implemented interface and then just implemented. This means that a class cannot satisfy multiple interfaces declaring methods with the same method selector. For example, this code is ill-typed:
Note that would be bad 42 code anyway; you should define an enumeration (or an alphanumeric) for your cards and use a «» unit of measure for the time, which would make the unavoidable type error more obvious.

Interface diamonds are allowed; that is, the following code is correct:

You can refine the return type of an interface method, by repeating the full type signature with the desired return type. On the other hand, the parameter types cannot be refined.

(2/5) Interfaces and class methods

Interface methods in 42 are all abstract; that is, without bodies. A version of the body will be provided by all classes implementing the interface. This also includes class methods.
For example, consider the following code:

The pattern in the code above encodes the abstract factory pattern in a much simpler way: the binding «» serves the role of an instance of an abstract factory, and can create instances of a specific kind of shape.

In 42 interfaces do not have implemented class methods. Sometimes we want to semantically associate some behaviour with an interface. For example we could check intersections between shapes using the draw method. What we would need, is a traditional (non dynamically dispatched) static method. In 42, static methods are just nested classes with a single class method with the empty name. In 42 adding new classes is very common, so do not be scared of adding a new class just to host a method.
For example

(3/5)Subtyping

Dynamic dispatch is the most important feature of object oriented languages. Subtyping is the main feature supporting dynamic dispatch; for example we can iterate over a list of «» and «» them without the need to explicitly handle in the code all the possible kinds of «» on a case by case basis. Note that modifiers («», «», «»,..)) offer subtyping but not dynamic dispatch.

Interfaces and classes represent two fundamentally alternative compromises between the providers and users of objects:

  • Interfaces allows client code to be implicitly parametric on the behaviour of individual objects. The client code can make no assumption on the specific implementation of the interface.
  • Classes allow client code to rely on their invariants. The user code is forced to pass only a specific kind of implementation.

In simpler terms, if we have a «» interface, and we call «», we will get the right kind of drawing behaviour for that shape. On the other hand, we can not statically predict what kind of shape and behaviour we will execute.
If instead we have a «» class, then we can statically predict what kind of behaviour «» will execute, and that the width and height of the drawn shape are going to be equal, as for the «» invariant. On the other hand, our «» using code can only be used with squares, not any of the other kinds of shape.

(4/5)Matching

It is possible to inspect the runtime type of an object by using matching. We will now provide various examples of matching and explain the behaviour.

We can use an «» to check for the type of a local binding or an expression. We can also check for more then one local binding at a time. When an «» checks for types it cannot have an «» branch. This «» pattern allows us to write complex dispatch in a compact way, without the need of an explicit «».

The syntax «» can also be used in expressions when the type system needs help with figuring out the type, usually because some information will be generated by a decorator at a later stage. Sometimes we may have to write code like the following «»: the method «» is called on the result of «», that we expect to return a «». While «» will check the type at run time, the explicit (sub-)type annotation with «» is checked at compile time, as if we wrote «»

The code below:

is equivalent to:
That is: we can match out the result of any no-arg method into a variable with the same name, plus an optional numeric suffix. This syntax can also be used inside of the «». This is often done for fields. Of course it works also to extract a single field:
As we have seen, matches are a form of syntactic sugar allowing more compact code for extracting subcomponents and taking decisions about their dynamic types.

(5/5)Interfaces, subtypes and matching, summary

Interfaces in 42 serve the same role that they serve in other languages, with a little bit of a twist in the details. The big news is that decorators («» in our examples) can provide the boilerplate implementations for free. This is much more powerful than traits, multiple inheritance or Java 8 default methods, since the implementation can be generated by examining the class.
In the right conditions, matching is a useful tool to reduce code size and complexity. However, dynamic dispatch is still the preferred option in the general case. On the other hand type matching works only on a finite number of hard-coded cases, making it fragile with respect to code evolution.

      Previous ... Next