People often ask me what functional programming is all about and why is it useful. In an attempt to answer this question in a way that is relevant to object oriented programmers it struck me it might be useful to look at what people find useful in modern object oriented programming. Once we know what programmers like about OOP, specifically what problems it helps them solve that structured programming didn’t, we can look again at FP and look at how it approaches these problems.
While we could fall back on a traditional definition of what OOP is all about, the three tenet of OOP - encapsulation, inheritance, and polymorphism, I don’t think this would be very helpful. Why? Well, it’s just my opinion, but I don’t think they describe well what modern OOP is all about. We could look at design principles that are commonly used in OO, here SOLID would be a good candidate. But, we’re not going to do this because I believe SOLID and the like are more about describing how OO systems should be build rather than describing what the languages themselves do. Instead I’m going to lay out what I think modern OOP is all about.
It should also be noted that these ideas draw mainly on my experience with C#, which is the main OOP language that I use. Not all OOP languages work in an exactly the same way, so one could probably argue each language has its own ideas about what OOP is all about. Alan Kay once said “I invented object oriented programming, I wasn’t thinking of C++” and as C# takes more ideas from C++ than it does from Smalltalk one could argue that we’re not actually talking about OOP at all. Still I hope that C#, and perhaps Java developers too – since Java has a similar OOP model, will be able see some merits in my list of important features of OOP.
Objects have member methods which have a special syntax for calling them
What I mean by this is in structured programming, we would have wrote:
While in OOP we write:
It may seem a small change, but I believe it’s a very important one. Ultimately it’s just syntactic sugar, when the method is JIT it will be implemented as method call with “myPhrase” becoming the first parameter – the hidden this reference passed into every member method. But just because it’s syntactic sugar it should be dismissed as unimportant. The code is now more readable as our mind is now focused on the value “myPhrase” and operation we’re performing on it. It also helps make API’s more discoverable, as values now have associated methods, pressing dot, in the right development environment, will reveal the list of commonly used methods for this value. Finally, frameworks like LINQ and anything calling itself “a fluent” api simple would not work without this one small intervention.
What’s the functional take on this topic?
F# lets you add member methods to types, so everything that’s good about member methods in C# is also good in F#. However, this isn’t something very “functional” it’s just a nice feature of F#. Functional languages aim to give a flexible syntax and often let you definite custom operators (this can be considered functional as it has its roots in lambda calculus). F# defines several function combination operators, the most commonly used being the pipe forward operator: |>. Its definition is:
and it lets you inverse the order in which the function and it’s parameter appear. Which allows you to write:
so now a function (or method) can appear before the value it’s being pass, this can be seen as similar way to which member methods appear after they’re first parameter in object oriented programming. Interestingly the pipeforward operator can also be used to create chains in the same way you chain functions together in LINQ. For example in LINQ you might write:
Which is very similar to writing the following pipe line in F#
Object are values and so can be passed as parameters along with their associated methods
As we’ve already noted objects have methods. We can create instance of an object, which is a value, and can be passed to methods/functions/constructors as a parameter. Calling a method on an object we received as a parameter will call the method defined in the class which defined the object. Now the clever bit, if the instance passed as a parameter to the method inherits from the specified type (or the specified type is an interface) then method from the class defining the parameter may not be called but rather a method from the derived class maybe called instead. Effectively OO programing gives a two ways to delay which the decision of which is method implementation is called till runtime. As an OO programmer you’re probably very familiar with both these techniques but let’s do a quick recap. If we define a method:
We can call this method in the classic way:
Or we could call it using a derived class:
Now, if AMemberMethod is virtual and MyDerived class defines new version the version defined in MyDerived class will be called instead of the version in MyBase class.
The story for an interface is somewhat similar, if a little simpler. We define a method that takes an interface:
Then we must first create an implementation of our interface before we can call the method. We know that it is the methods on this implementation that will be called by the method “AMethod”, so this gives us a way to control what code will be executed during a call to “AMethod”.
Understanding these two concepts is a key part of becoming a success OO programmer since all OO design patterns, or at least all that I can think of, are based on these two concepts. It’s key in refactoring you’re code to avoid repetition (using the “hole in the middle pattern” design pattern). The powerful concept of “Inversion of Control” (IoC) makes use of these OO techniques and they are also heavily used in the unit testing and TDD worlds.
What’s the functional take on this topic?
F# can do interfaces and classes and methods so you can do everything can do in C# in F#, but that’s not really the point. In functional programming a function is a value so it can be passed to a function (or method) as a parameter. This allows you to achieve exactly what you can do in OO programming but with a much smaller cost, no need to think about interfaces or inheritance hierarchies we can just take a function as a parameter directly. If want delay the decision about which function is called till runtime you simple take a function as a parameter:
So we have a similar way to implement the “hole in the middle” pattern. This allows us to do what I like to call “Abstraction of Control Flow”, a technique that allows us to wrap of commonly used control flow in library functions. Classic examples of this are F# Seq or List libraries.
It’s true that C# now provides something very similar to this in the form of LINQ, but F# goes a bit further as the C# libraries are based on delegates where F# libraries are based on function types. So what’s the difference? Delegates are nominally typed whereas F# functions are structurally typed. This basically means for delegates it’s the name of the delegate the counts whereas for F# functions it’s the structure, its parameters and its return type, that counts (later versions C# compiler does quite a bit simulate structural type, but there are still places where the nominal nature of delegates peeks out at you).
Object provide encapsulation of complexity
When OO programmers talk about encapsulation they are often referring to encapsulation of mutable state rather than encapsulation of complexity. I think this is a shame as OO languages are rather good at encapsulating complexity while rather bad at encapsulating mutable variables.
Let’s try and justify the second part of that statement first. Imagine you have class that contains a list, it’s the job of this class to maintain this this of items and occasionally clients need know the current state of this list. To implement the requirement that clients occasionally need to know the state of the list one might be tempted to write:
But this is almost certainly not what you wanted to do. Here you have broken the encapsulation of the class and exposed a private member. Now client code has a reference to your list and is free to add or delete members. Worse in multi-thread scenarios you maybe on a different thread so even a simple operation like enumerating the list could cause an error if the class managing the list is modifying it. Probably the safest solution is to copy the list before giving it to the client:
It’s not just lists that are vulnerable to this problem; any object graph that is mutable suffers from the same problem. You can never return a private member from a member function without making a copy unless the private member is immutable. As I’ve often seen project’s with a lot of code that does follow this rule it’s led me to believe that encapsulation by keyword the private doesn’t work well. It’s something that seems like a good idea in the theory but is difficult to put into practice.
However, I do believe that objects provide great encapsulation of complexity. A good example of this is to look at library design. When your design a library it useful to use objects to represent the concepts you want client code to be able manipulate. You can see this by look at almost any non-static class in the .NET BCL, whether it be immutable like System.String or mutable like System.Net.Sockets.NetworkStream. Without these classes representing a set of characters or reading and writing to the network would be difficult requiring us to use array as buffers and make low level API calls. These classes wrap up these low level tasks into higher level concepts that are easier for the user to understand. If you will, they present a simpler interface, a simpler way to get things done, instead of having to write my own search and replace algorithm, I can call the strings Replace method.
What’s the functional take on this topic?
F# has a module system and also allows you to create classes. However, F#, and other FP languages in general, offers another form of encapsulation that OO languages. F# allows you to define values and functions that are local to a function. It is then up to the implementer of the function to decide whether or not to return function or value thus whether or not to expose it to the outside world. This can make quite a cute way to encapsulate mutable state:
Here we define a function that will create us a counter and returns us three functions for accessing this counter. This allows us to encapsulate the counter and choose how calling clients will have access to it.
This is useful as you can create a hierarchy of encapsulation since local functions will have access to the other values their parent functions defines but their parent function will not have access to the values that they define. For example:
In this snippet code in “innerFunc” could access x but code from “func” could not access the identifier k.
We’ve looked at what I consider the three most important concepts in OO programming and how these concepts are related to functional programming. I hope that if you consider yourself an OO programmer that this will help you understand where FP is coming from.