From Functions to Functors: programming languages evolution towards code reuse in the large .

What this post is not.

This blog post does not aim to generate yet another flame war on programming languages, something along the lines of:

All of your problems are there because you keep using PHP, why don’t people just switch to OCaml and stop complaining…”

What this post is actually about.

This post stems from the consideration that often the choice of language opens opportunities (or sometimes poses constraints) on the design choices software engineers and programmers face during development. In particular, I am convinced that some often overlooked features, namely enhanced module systems like the one used in SML and OCaml, can help reuse code on a larger scale than what is currently achievable in most languages, with lesser need to resort to complex design patterns.

Why reuse code?

One might say that one of the main qualities of  a good programmer is her ability to maximise the amount of reusable code she writes.

The advantages are obvious:

  • Reusing code means writing less code, which equals less development time.
  • Reusing code avoids tedious tasks, like copy-paste, or even worst, rewriting from scratch.
  • Most importantly, duplicate code is a nightmare when an error or bug is detected, because doing the same correction in multiple places increases the chance of neglecting something (plus, it’s utterly boring ).

All the issues deriving from lack of code reuse can somewhat be neglected in small scale software (I have done some programming tutoring, and heard very often the phrase: “Why should I use polymorphism, my code  works perfectly!!”, usually referred to some ugly switch statement). However, as the scale increases, they can seriously hinder the successful development of a project.
It’s therefore better to be aware of the DRY principle [1]:

Don’t repeat yourself!

 

From pattern to feature: how programming languages evolve.

Let me make this clear straight away: respecting the DRY principle does not depend on the programming language.
In fact, look at this C code, does it look familiar to you OO programmers?

//Definition of generic stack
typedef struct{
void* value;
size_t size;
Stack* next;
} Stack;

void new_Stack(Stack*);
void push(Stack*,void*,size_t);
void pop(Stack*,void*,size_t);
void delete_Stack(Stack*);

The truth is that very good programmers can (almost) always design  good software, and often what is considered to be a good solution will be “standardised” in a design pattern; so that patterns will naturally arise from good practices.

Nevertheless, even when a pattern is well known, how it is applied is an entirely different matter, and still requires considerable effort in the design phase. So, while is always possible to write good code, how easily one can do so is influenced by other factors, and here is were language features weigh in.

As an example consider the Command pattern [2] implemented in Java , which involves defining an interface, and several classes to implement it, in order to encapsulate the information necessary to call a method.
However, if you are using a language with first-order functions, you don’t need to take any design choice to deal with this problem, but just pass the function to the method that needs to call it.

So, languages tend to absorb patterns by exposing additional features, turning what used to be complex design problems into trivial tasks.

A key concept towards code reuse: code parameterisation.

When considering any Assembly listing, one will probably see plenty of code that looks the same, made of registry manipulation and conditional branches. Some recognizable patterns  in the use of goto were encoded as a feature in higher level languages, giving birth to the while loop. Soon enough patterns started to emerge in the use of while as well, and the for loop was born.

A huge leap forward was the discovery that code could be parameterised. Since the introduction of functions, there has been a constant evolution that allowed more and more behaviour to be expressed concisely using some carefully parameterised piece of code, starting from procedures and leading to OOP programming.

This powerful process, however, seems to have stopped, and for many programmers object polymorphism is the top level of generalisation that can be achieved using a language feature. If one wants to generalise further, one needs to resort to carefully chosen design patterns.

Reusing code on a larger scale: SML-like Functors

If one looks at the hierarchy of an OO program, one will probably see methods, encompassed by classes, encompassed by modules ( packages, for those who come from a Java background). We have also seen how the key to code reuse is parameterisation, and the higher we apply it in the hierarchy, the larger the scale we will be able to abstract and, ultimately, reuse code on.

The idea is then simple, why don’t we parametrise modules? Well, turns out we already can.

SML and OCaml are functional languages of the ML family, sporting one of the most powerful module systems ever conceived. This module system is specifically intended for programming in the large, in that it expresses the architecture of the software, where each module provides abstraction through a descriptive interface for a group of types  and expressions[3].

In this module system, a functor [4] is a function that maps modules to other modules.  That is, it allows to create a module parameterised by other modules, with two remarkable effects:

  1. The reusability of a parameterised module is greatly enhanced since its functionality can be applied to similar, not just identical, domains.
  2. High reusability is achieved at the module level using a feature of the language, without the need to use complex design patterns, just as with functions and polymorphism at a smaller scale.

I love it but I need my own language!

Notwithstanding its expressive power, this module system has not been widely adopted, partly due to the limited popularity of SML and OCaml. Good news is, you don’t need either to benefit from it. Xavier Leroy [5] has been able to come up with an SML-like module system, implemented as a module parameterised by a base language and its type-checker, showing how it could be used by many different languages, not necessarily close to the ML semantics. Moreover, the fact that this module system is a module itself serves as a further statement of functors’ capabilities.

Conclusion

Design patterns lie just outside the scope of what we can solve using programming languages features, and require considerable effort in the design phase.
Functors provide a powerful means to specify and  glue  reusable modules together, and, if more widely adopted, can be a stepping stone towards code reuse in the large.

References

[1] Clark, Allan. “Design Patterns.” SAPM Lecture Slides, University of Edinburgh,2014

[2] “Command pattern“, Wikipedia definition.

[3] Fiore , Marcelo. “SML modules.”  Concepts in Programming lLanguages Lecture Slides .    Cambridge University, 2011.

[4] “Functors-Paremeterized Modules. Data Structures and Functional Programming  Lecture Slides . Cornell University.

[5]Leroy, Xavier. “A modular module system.Journal of Functional Programming 10.3 (2000): 269-303.