Functional JS: Creating a Command Loop
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:
- making an HTTP request,
- caching data locally,
- asking for the time,
- calling functions via
- generating a random number
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?
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:
Ok that looks like a lot. Let’s go over that in depth, one portion at a time:
Symbols, Err, and Type Registry
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 is a constructor function that builds an object that can be matched in the
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.
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:
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
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!
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:
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.
We have defined some internal functions for our command loop, two of which will be available on the returned object (
command— this function is a part of the Command Loop’s exposed interface. It allows us to push a command into the internal queue and trigger a call to process the queue. It accepts a Pair of
[tag, cmd]which are a string and Command object respectively.
subscriptions— the other publicly exposed function of Command Loop. It allows us to pass a configuration object to define what types of responses we will be listening for on the ‘pure’ end of the application
process— this function is internal only and is the meat of the Command Loop’s functionality. It pulls the last item off of the internal queue and sends it through an internal constant, the
CMD_PATTERNwhich we have yet to define.
CMD_PATTERNwill be the pattern that
matchwill match our Command instances against. When the
matchis complete and has yielded a result
emitis called, letting the listeners know that their request has been answered.
listen— sets up the internal
listenersobject with an entry. When
emitis called with a
tagthat’s registered on
listeners, the function located at that key will be called with emit’s data parameter.
emit— iterates through listeners at the provided
tagand calls registered functions with
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.
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
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,
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:
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
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(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?!
We isolate our impure actions within the configuration object passed to
subscriptions. The keys of the object correspond to the tag passed to
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
Effect. In the
subscriptions portion, we can check when
iterations is beyond a certain point and respond to it by cancelling the interval call.
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
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!