Functional JS: Creating a Command Loop

Handling Impure Actions in a Pure Application

Ross
Ross
Oct 25, 2020 · 10 min read
Image for post
Image for post
Photo by Héctor J. Rivas on Unsplash

If you’ve been studying up on Functional Programming in JS, you’ve probably heard about pure and impure functions, and side-effects. If not, I’ll quickly recap — a pure function is one that when given the same parameter any number of times, for each of those invocations it will produce the same result. Pure functions should also have a tangible result. Impure functions on the other hand may produce a different result when given the same parameters. Something non-static is happening behind the scenes in these impure functions. Those non-static factors are called side-effects. Functional programmers always say, ‘avoid impure functions, avoid side-effects’, etc. The only problem is, eventually you’ll need to incur side-effects. This is because the meat of an application is often the side-effecting portion. Consider these actions:

These are all examples of side-effects. You’re probably doing many of these regularly in your code. So how can you keep code pure?

One way that pure functional languages like Haskell handle side-effects is by handling them apart from the main ‘pure’ application. So today I’m going to attempt to create a Command Loop to handle impure portions of our application code. I’ll be implementing the Command Loop in JavaScript. Reader beware, I will be making highly opinionated choices throughout this article related to functional programming!

Before we dive right into creating our Command Loop, let’s talk about some other features that functional languages employ. Specifically I’d like to talk about Pattern Matching and Sum Types. These features greatly improve the experience a developer will have with the Command Loop pattern which we will be implementing.

Let’s start with Sum Types. A Sum Type is an object Type who’s instances have specific structures called variants. They call them Sum Types because you can determine the number of variants that can exist by summing the number of variants defined. Because there is a finite and known number of variants per Sum Type, they play well with the other concept I mentioned, Pattern Matching. If we know what can exist, we can write code to account for the variants. Imagine a function that knows what variants can exist on a given type instance, and then determines the specific variant type and performs a function based on that variant’s data. That’s exactly what we’re going to write to start our Command Loop journey. I call this function match. It will take an instance of a Sum Type object and a pattern to match against:

Image for post
Image for post
Enabling Pattern Matching in JS

Ok that looks like a lot. Let’s go over that in depth, one portion at a time:

Symbols, Err, and Type Registry

The TAG, MATCH, and TYPE variables are special Symbol type objects. You can key objects with Symbols, so I use them to hide implementation details that I don’t want the end user messing with. TYPE will represent the type name of any variant that we construct from a certain Sum Type. TAG is the specific variant name for any single instance of a Sum Type. MATCH will be the key we hide our match function implementation. err is just a simple abstraction for throwing an Error, and the REGISTERED_TYPES variable will hold onto the type names we will later define when we create the Sum Type constructor function.

Matchable

Matchable is a constructor function that builds an object that can be matched in the match function. Matchable is the base type by which we will build all of our Sum Type instances. Thus, I’ll be using it as a mix-in object when we write the function to define Sum Type variants. Notice pvt in it’s parameters. pvt will be used to pass on data specific to a variant back to the match function. Passing it back from the constructor to the match function actually prevents it from being visible to the outside world, so once we create an instance it’s data becomes immutable.

Another important thing to look at in the Matchable function is how we hide our match function behavior behind the MATCH Symbol. This way match can match against any instance without having to know about how match is implemented. Our instance-specific match takes a pattern (which will be an object with keys that correspond to the variant names expected), and optionally some data. The data is just so that we can inject custom data into the match function for special internal cases. Inside our match implementation, we look to see if our instance’s TAG is in the pattern. If it is we call the function in the pattern passing that function our instance as a parameter. If our instance’s TAG isn’t in the pattern, it will look for the WILD value (which is _). If neither our instance TAG nor the WILD value is present in the pattern, we will throw an error.

Match

The match function itself checks to ensure that our pattern doesn’t contain unexpected values, or non-tagged instances. Then it just looks to the instance’s implementation of of match located at the MATCH key and calls it with the given pattern and injected data, if any.

Alright that wasn’t so bad. Now we have the pieces we need to create our Sum Type constructor. Let’s do that now:

Image for post
Image for post
Defining the Sum Type constructor.

More heavy duty stuff. Let’s break it down once more. register_type is an internal function that modifies the REGISTERED_TYPES object to include the names of variants registered to a specific type. SumType is a special constructor that builds extensible Sum Type variant objects and registers their names to the central registry. SumType is fairly complicated so let’s go through it in detail.

SumType takes two parameters, the name of the type that we’re creating, and the constructors object which will hold the definitions for our Sum Type variants. The first thing that we do in the SumType function is declare a few const type variables. T will be our result’s namespace. Each variant constructor that we create will be placed inside of T, which will eventually serve as the return value of SumType. tags is an object which simply aggregates the tag names for each variant to make it easy to pass them to register_type. We deconstruct our constructors object with Object.entries. Then we iterate over the results. This is the bulk of the SumType function. We place the tag name of the constructor into the tags object (aggregating the tag names). Then we create a constructor function called result to serve as the real constructor that gets called when we instantiate a variant of a Sum Type. It’s this result constructor function that we mix-in Matchable to, allowing us to sink the private data from our constructors definition into the final instance MATCH call. Finally, we place a function at T[tag] that returns a new result and set that function’s prototype to result's prototype. Phew! Let’s see how we use it to create Sum Types!

Image for post
Image for post
The Result Sum Type.

Finally, we can build a Sum Type that has matching capabilities. Now we can see the beauty of what we’ve just enacted. We can construct our Sum Types using our newly defined SumType function. We specify the shape of our variants’ data with simple arrow functions that return objects. Then when we call match, that private data is injected into our pattern’s selected function.

Way back at the beginning of this article I was discussing impurity in code. Let’s imagine a Sum Type whose variants represent the impure actions that we can take in our code:

Image for post
Image for post
The Command Type.

I call this type Command. It consists of variants to represent things like getting and setting local storage (Retrieve, Cache), generating a Random number, sending an asynchronous request (Fork), and receiving a Response. We also have the ability to set an Interval and perform a general side Effect (like logging to the console).

This guy is going to be the backbone of our Command Loop. Let’s finally start thinking about how to define the Command Loop. There are two aspects to account for — sending and receiving. We will want a way to push Commands into the loop, and a way to subscribe to the responses of the loop.

Image for post
Image for post

We have defined some internal functions for our command loop, two of which will be available on the returned object (command and subscriptions).

We’re getting close to our goal now. But we have another hurdle — we still need to define the match pattern CMD_PATTERN that we’ll be sending our Commands through. Remember Result that we defined earlier? We’ll be using it heavily here.

Image for post
Image for post

Cache and Retrieve are simple enough to implement. We just call the impure code as we normally would. Except its all neatly separated on it’s own impure track. Everything that we return is neatly wrapped up in a Result type. We also handle the default case using _. Now let’s implement some others

Image for post
Image for post

Interval Random and Effect are all very simple as well. Interval sets an interval and returns a function to clear it. Random gets a random number and gives it back to our application. Effect takes any impure, non-returning function and calls it.

Now the tricky ones, Fork and Response. We’re going to have to go back to the drawing board for this one. Let’s define a type to encapsulate an Asynchronous action:

Image for post
Image for post

I’m not going to explain Async in detail because it’s beyond the scope of this article, but know that it’s like a Promise, except to make it start its internal process, you must call its .fork method, providing an on-error function and an on-success function as parameters. You can use chain to wrap other Asyncs into the call or transform the expected result with map. I’ve also included a function called fetch that wraps an XMLHttpRequest into the Async’s internal fork. That’s the function that I’m concerned about for implementing our Fork Command.

Image for post
Image for post

Now we will be expecting that the data passed into Fork will be of the Async type. We can now fork it, calling command again to send the value back to the pure side of the application as a Response variant. Since we’re wrapping the result of our Async call in a Result (look how we have Response(Ok(x)) and Response(Err(x)) for the success and error paths of .fork) we can just return the result. Response only gives us a convenient way to give back the result of our impure asynchronous code to the pure side of the application in a controlled fashion.

Now the moment of truth. Will it work?!

Image for post
Image for post

We isolate our impure actions within the configuration object passed to subscriptions. The keys of the object correspond to the tag passed to command. When emit is called internally, it passes the result of our impure code (if any) back to our pure application, where we can handle it within subscriptions. As seen in the Interval example, you can nest commands within commands. IntervalIter will log to the console and increment iterations using Effect. In the subscriptions portion, we can check when iterations is beyond a certain point and respond to it by cancelling the interval call.

Using the Fork Command, we can make an asynchronous HTTP request, and on the subscriptions side, we can respond in the Response function. In this case we fire a new Effect command to log the results. Notice how we omit the String parameter of the (String, Command) pair using the ,. We can do this for commands that we don’t care about subscribing to — they simply wont emit a result.

Now whenever we want to separate our effects we can use this tiny bit of code to do so. It doesn’t seem that tiny, but I call it tiny because we’ve managed to include all this functionality, including the Async type and several others, in just about 200 lines of code. That’s really not a lot considering how useful match and SumType can be together. Using just this code and vanillaJS we could accomplish a pretty polished separation of pure and impure code. But since it just separates side-effects from your code, we could probably toss the Command Loop into something like a React application, too.

Here’s a pen to hack at:

Hope you enjoyed another Functionally oriented article, and until next time FP on!

The Startup

Medium's largest active publication, followed by +773K people. Follow to join our community.

Ross

Written by

Ross

Programming maniac, #JavaScript zealot. I'm crazy about #FunctionalProgramming and I love Rust.

The Startup

Medium's largest active publication, followed by +773K people. Follow to join our community.

Ross

Written by

Ross

Programming maniac, #JavaScript zealot. I'm crazy about #FunctionalProgramming and I love Rust.

The Startup

Medium's largest active publication, followed by +773K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store