This just isn't functional

It turns out that if one is simply reacting to incoming information from some source of truth, using pure functions to transform that information into a UI is a very natural thing to do. With the increasing popularity of React, lots of programmers are finding themselves having to deal with functional constructs.

When writing functional JavaScript, a person will inevitably think: “Some sort of object-orientedness would be very useful for my program.” Maybe there is some code reuse happening. Maybe, just maybe, you have an inheritance situation on your hands; an honest-to-god taxonomy. If you have gone down this route, you may wind up thinking, “Oh, I now have to use the this keyword.”

Cold sweats. Do you really know what this is? What is it bound to? There are four different ways that this could have been bound. Do you, at this moment, know which applies to your program? The most terrible situation would be a this bound to the global scope. Who knows what has been mutated now?

You might think, “No, my function is well reasoned, and well defined.” Traditionally, in functional programming, bits of state that only exist locally in a function, and are not passed around to other functions, are okay. This is because local state avoids some of the problems associated with stateful code. You might think your constructor is doing just that, but if you are wrong about what kind of this you are dealing with, your local assignment, which was okay by functional programming standards, can suddenly mutate into something outside the local function scope.

functionDog(){this.bark=function(){return"WOOF!";}}// Then a very bad person comes alongvarcat={purring:true};Dog.bind(cat)();cat.bark();// WHAT HAVE WE WROUGHT

Chilling.

Those of you who have not yet been burned by this may not understand what is
going on. Other than the global object, the previous function scope, or an object
that can be passed in by new, the this object in a function can also be explicitly bound this way.

Someone has used the constructor function in a way that was not intended.
Now, the little kitty can bark. It is tragic. The problem with using this
in your constructor is that you need to trust that your users will be calling
your function with new. This constructor is definitely not a pure function. It is possibly side-effecting. If your constructor is is not called with new, it can be mutating.

What alternatives exist?

// Let's try...functionPerson(name){return{name:name,greet:function(other){return"Hello "+other.name+", my name is "+name+".";}};}varkaren=Person("Karen");vartom=Person("Tom");tom.greet(karen);// Hello Karen, my name is Tom.// That's nice, but we can still do this...tom.name="Jeff";tom.greet(karen);// Hello Karen, my name is Tom.tom.name=="Jeff";// true// And now everything is inconsistent!

Our constructor does not use this, so it is safe from one form of side effect,
but it still returns a piece of mutable state. To make matters worse, when it
creates the greet function it uses a variable it closed over, which does not
mutate. Our object is now meaningless.

You might now be thinking, “OK, let’s just use Object.fre–”

Let me stop you there. Object.freeze does not actually address all of
our problems. In functional programming, the way one achieves change is
not through in place mutation, but in the creation of a new object. This
means that functional programming never deletes any old facts, it only
creates new facts. When we change the name of our functional object by
copying it and refreezing, we will not change the functions inside the
object. All those functions will still only have access to the old
version of the object through reference. In an immutable world, the
coupling of functionality and data is extremely difficult to use because
any functionality would have to be rebuilt any time the facts change.

So what do we do?

We use Protocols2. A protocol is a namespace that provides you with a defined set of functions that can be called on anything that implements that protocol. A protocol checks an object’s type metadata at runtime, and selects the right behavior based on that type. Furthermore, a protocol is not directly attached to your object. To call a protocol,
one must pass the object into the protocol. This means we do not have to update functionality whenever we create a new version of an object.

Now we can build ourselves a new tower of object-orientedness. A tower where nothing ever changes and where we are stuck, forever, immutably. It is the functional dystopia we’ve always wanted.

As a side note; this article uses a quick and dirty implementation of protocols.
To follow along, copy the source code and paste it into a Node project. It is already packaged as a Node module. This article will not concern itself with implementation details for purposes of brevity and clarity. If you are interested in implementation details, check the source; it is well commented.

To define a protocol, we call Protocol(protocolName, [fns...]), where protocolName is the name of the protocol, and fns are the names of all the functions that an implementation of a protocol must define.

Consider two protocols: HypeRater and TemperatureRater. Something can be cool in a cool/unpopular way. Something can also be cool in a cool/warm way. We, human beings, can be cool and warm, or unpopular and cool, or cool and cool, or unpopular and warm. Much like someone can be, like, cool cool, someone can be, like, HypeRater.isCool, you know? Someone can be TemperatureRater.isCool, too, and those values can be different.

When we specify what kind of cool we’re talking about, we get a nifty thing called namespacing. Check out the code below.

varHypeRater=Protocol("HypeRater",["isCool","isUnpopular"]);varTemperatureRater=Protocol("TemperatureRater",["isCool","isWarm"]);// Imagine we had a theSun object, which implemented both protocolsHypeRater.isCool(theSun,[]);//=> trueTemperatureRater.isCool(theSun,[]);//=> false

Protocols solve the problem of method clashing in object-oriented programming. An object with multiple base classes might wind up having to implement two equally named methods from those two base classes. Protocols also solve the duck typing problem of “Sure, this object has a .run function, but is it talking about the same kind of running that I need?”

Protocols require an Implementation. An implementation provides a function that implements each method specified in the protocol.

tempImpl is a valid implementation for any protocol with two
methods, isCool and isHot. tempImpl implements these two
functions by using the function isCool. This function takes two
arguments, a record thiz and an array of arguments args.
In the call TemperatureRater.isCool(theSun, [1,2,3]), theSun
would be bound to thiz and [1,2,3] would be bound to args.

The final step in being able to use our protocols is to create
a sort-of-like-a-class thing that is able to implement protocols.
We will be using something called a Record3. A record is
an immutable object that declares a set of required fields and
protocol implementations.

To obtain a record constructor, one should call Record(typeName, fieldDeclarations, implementations). typeName is the name of your record, fieldDeclarations declare the fields all records of that type must define, and implementations is a map of protocol names to their implementations. This corrals a record into a very neat space. The fields which it is guaranteed to have are pretty well defined. The protocols for which it can be used are very well defined. Let us take a look at an example.

Our Human constructor is created by the Record call. We tell
it that we have a "Human" for the name of the record.
A Human has three fields, ["name", "temperature", "theFonz?"].
The Human implements two protocols, HypeRater and TemperatureRater.
For the HypeRater, it provides its own implementation. For the
TemperatureRater, it reuses the already defined tempImpl.

What are some of the properties of our records and what can we do with
them? First things first: to create a new Human, we would call
the function returned to us by our Record call, the function we
have stored in our Human variable. Human(["Sonali", 22, true]) would
create a new human, whose name is Sonali, whose temperature is 22, who
is the Fonz (in spirit). How would we access her name?

That depends on implementation4. The implementation I have provided
with this article creates records using Mori5. Mori provides us with persistent data structures. These data structures use something called structural sharing to make copies and updates at a very low cost. If we were using Object.freeze we would be copying the entire object every time we wanted to create an update. With persistent data structures, we share as much of the old data structure as possible.

Our use of persistent data structures mean we cannot use the traditional
JavaScript forms of property access, because we are not using traditional
JavaScript data structures. Thus, we will use assoc and get to interact with
our object.

varmori=require('mori');varvec=mori.vector;// mori vectors are like immutable arraysvarget=mori.get;// get allows us to search inside an objectvarassoc=mori.assoc;// assoc allows us to create new versions of our objectsvarsonalisName=get(sonali,"name");// sonalisName === "Sonali"varsonalisTemp=get(sonali,"name");// sonalisTemp === 22varsonalisFonz=get(sonali,"theFonz?");// sonalisFonz === true// assoc will create a copy of sonali, where "name" is now equal to "Sal"varsal=assoc(sonali,"name","Sal");varsalsName=get(sal,"name");// salsName === "Sal"varsonalisName=get(sonali,"name");// sonalisName === "Sonali"

As you can see, assoc is not mutative. It creates a copy. This means that you can feel free to pass copies of your objects around to anywhere in your program, and not fret about it getting mangled deep inside some callbacks. This property makes working with others a breeze.

One final advantage that records have over vanilla JavaScript objects is that records can have field declarations that are generated programmatically. Let’s try and implement a tic-tac-toe board.

When creating two-dimensional boards, the most common strategy is to create a two-dimensional array. This can make some things confusing. For one, when doing things like mapping across that array, we have to zip up and down in one direction. If we are just iterating across the array, we need to access two things. This means you would have to use two for loops to do stuff. In one, you would access a row, and in the other, you would dig inside the row for the proper cell. As human beings, we do not think about two dimensional boards in terms of “get row, and then dig inside the row.” We think about boards in terms of coordinates.

You might be asking me to wait. We can use a flat representation of a board instead, and by doing that, and utilizing modulos and other tricks, we can flatten
a two dimensional array into a single dimension.
If we try to use just one array, so that we can use a single for loop, we exacerbate the problem of representation; that is, we make our representation of
our board very different from how we think about it in our minds so that we can utilize our old data structure. No one thinks about two dimensional boards
as a single list of cells, where one can access things inside of of it using modulo.

That’s pretty beautiful! As you can see, we can access fields
by getting the exact cell that we want. We access things in
exactly one get. We can iterate over all the keys in the
board my using mori.map(mori.keys(myBoard), ...) where ...
represents some sort of function. No two for loops required.
Getting a new object with something changed is just as
easy. You do it in one fell swoop using assoc.

But that’s relatively simple.

Typing all that stuff out for tic-tac-toe was doable because we only had nine
different entries, but what if I want a chess board? We can do it!
Here, instead of building that array of cells by hand, we’ll generate
it.

Well, I promised you a dystopic tower of well defined functional dispatch, and you’ve got it! While I do not recommend adopting it for any serious codebase (because this library is not tested or optimized), I encourage you to try it out for small projects. Good representation of data is a key aspect of getting lots of power out of functional programming, and without constructs like this, you might find functional programming in JavaScript to be relatively bulky because of all the copy-on-write involved. Feel free to check out the source code. If you like the style, ask library providers to give you these tools so that you can use it in your own codebase!

Sal is a Brazilian artist, programmer, and LISP aficionado. He enjoys building tools to empower artists and programmers to express their visions more concisely and rapidly. He also enjoys exploring unconventional approaches to programming in common paradigms. When he's not building tools and making art he enjoys cats and explaining things in too much detail for way too much time. You can see his art at http://sisyphus.rocks. He attended the Fall 2, 2015 batch of the Recurse Center.