A functional Javascript stack (2019)

David Komer
12 min readJun 11, 2019

--

It’s tempting to get poetic about the benefits of functional programming in Javascript. Now that I think of it, one of the nicest things about FP is that it leads to thinking in abstractions and coming up with tasty metaphors (“monads are burritos/sushi/shwarma”). However, the aim of this article is to go through a list of specific tech choices.

There is one overall point that needs to be made at the outset though, and I can’t stress this enough:

These decisions matter MUCH more at scale!

In other words — in many cases I’m not even going to spend time coming up with examples where the alternative to the functional approach is broken. That would be disingenuous — and taken on its own, in small little snippets — there is genuinely nothing wrong with the imperative approach! (in fact — under the hood, the FP abstractions are often built on imperative underpinnings).

The benefits are often only seen in real-world problems, where you’ve got pipelines of like hundreds of lines of interconnected code coming together in an application that depends on user input, async operations, runtime conditions, etc.

And honestly, it takes a bit of a leap of faith, but I’ve been very happy with these decisions and they’ve served me well on real-world applications.

With that preamble out of the way — let’s go!

Expressions > Statements

Consider the following. You want to assign a variable to “hello” if foo equals “a”, “world” if it equals “b”, or “!” if it equals anything else.

Doing this with a switch statement could look something like this:

let result;
switch(foo) {
case "a": result = “hello”; break;
case "b": result = “world”; break;
default: result = “!”; break;
}

Instead, I opt to always use expressions. For example, something like this:

const result = foo === "a" ? "hello"
: foo === "b" ? "world"
: "!";

At a glance, there’s no big benefit… But, what if we didn’t really consider a catch-all case? This happens all the time, in the name of “I’ll get to it later” — we don’t put a final else block in an if-else chain, we leave off the default case, etc. Don’t tell me you’ve never done that :P

When everything is an expression, you have no choice, the code won’t even transpile if you forget to handle that situation!

(note — I happen to like conditionals but there are functional wrappers that can make this approach more palatable like Sanctuary or Ramda’s ifElse)

This has huge ramifications throughout everything, and even leads to the elimination of braces. For example, functions tend to be const f = x => ... as opposed to const f = x => {}. Speaking of functions…

Functions are all unary

Yup. Not a single function expects more than one argument. This follows the Purescript/Haskell/Elm approach and functions are defined like const f = x => y => z

Because functions tend to return functions (that accept one argument), function expressions like const f = … are preferred to function declarations like function f() {...}. It simply makes it less noisy to return functions from functions.

That said, often certain arguments should be grouped together and it seems silly to separate them. Nothing wrong with passing an Object (or Record) or Array as one of the arguments! That’s totally fine — and even if the only arg is an array, it’s still nicer seeing it explicitly typed as such.

No classes / this / bind

This could also fall under the dogma of “favor composition over inheritance” but it also has special significance for working with React (it’s own topic below) and structuring data.

Basically — I prefer storing data in POJO’s and using a type system (specifically Typescript, but sanctuary-def deserves a mention too) to describe any hierarchy (or, actually, subtyping) where needed.

This leads to having functions that operate on data that fulfills a certain contract, rather than having methods on data.

Suddenly this also gets rid of all sorts of scoping issues with this and there’s almost never a need to bind a function to a certain scope.

Option/Maybe instead of null

Tony Hoare, who first introduced the idea of a null value, had this to say:

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

Several modern languages have banished null completely from the language, and in many others it is commonplace to have a “no nulls” policy and use libraries that provide an Option or Maybe wrapper instead.

This is a big win and a hard rule for me. Now… when it comes to some things that have a sane “zero value” like strings (e.g. “”) or numbers (e.g. 0) or… perhaps even objects (e.g. {})… there might be a bit of leniency depending on how the data is being treated.

But no nulls — ever!

Either/Result instead of Exception

Up above in the section about expressions vs. declarations, I didn’t call attention where it crops up most often: error handling.

Error handling is the kind of thing that far, far, far too often gets pushed aside into the “I’ll deal with it later” bin and then gets buried until it it’s too late. Inevitably, the programmer pats themselves on the back for having elegant looking code (due to not accounting for errors) and then some nasty hard-to-diagnose bug resurrects itself, causing all sorts of problems — including the headache of hunting it down.

What if I told you that you could have as much logical branching as you want, and deeply nested call stacks, and even async operations — where it’s guaranteed at compile time that not only will you never miss an error, but you can handle all the errors in precisely one place (and by the way — often flatten that nesting)?

Sounds too good to be true?

OK, spoiler alert: it’s the magic of “monads” which has a gazillion articles out there and is too deep of a topic to summarize in a paragraph— but if this idea is new to you, I highly recommend reading and watching as much stuff as you can to start absorbing what it’s like to program in a world with “monadic” error handling. It will completely change your life as a programmer (not an exaggeration). Railway-Oriented Programming is one of my favs!

Flutures > Promises

I’m already seeing the pitchforks collecting at the gate. Happens every time I diss Promises (and async/await), and I get that. Promises took the crazy convoluted async callback hell and made the lives of Javascript programmers so much better, and nobody wants their savior insulted.

But… Promises have issues (some would say, Broken Promises). For one — they conflate expected errors (“bad password”) with unexpected errors (“network down”) by magically turning all exceptions into rejections (see error handling above). For another — the magic “then” has proprietary rules to distinguish between a value and an inner promise, making some code harder to reason about. For a third — they are eager, disallowing the ability to postpone the executing of a promise until a later date. Taken by its own any one of these are not so bad especially when coded with like a best practices guide — but combined, and without anything checking that discipline — it gets messy.

The Fluture API is just soo much better in every possible way. I mean, literally, every way, it’s not even worth listing because the answer to “are Futures better for [x]” is a definite “yes”. For a real-world use case — I wrote a little wrapper around it to deal with loading xhr requests, and immediately got the ability to get an upload progress indicator on a cancelable fetch request which _never_ misses reporting an error, will throw an exception only when something exceptional happens, and can be nested in a monadic pipeline just like an Option or Either. Ahhh it feels nice. Not going into the detail of that specific example since it’s not the point of this article, but my point is that the author of Fluture built something that puts Promises to shame.

Typescript

Not going to spend a lot of words on this… but I like Typescript. However, I also don’t entirely love Typescript.

Bottom line is that Typescript is amazing, but sometimes it feels like a hack — so I spend quite a bit of time specifying types and it is a HUGE saver when refactoring — but I do bail out to any here and there.

Besides the signatures, aliases, and interfaces — I also use the enums pretty often, though that’s toned down a bit due to xstate, which I’ll get into below, and I go back and forth between proper enums and simpler union types.

Monads and Functors

I’ve actually been talking about this all along — and kinda want to go in a different direction with React and state management stuff — but before switching gears, something should be said about this general topic.

Whenever I see examples from people who are more knowledgeable about FP than me, I am completely impressed by how elegant their solutions are. It often uses Typeclasses and Category things in order to achieve that elegance.

Functional programming abstractions that use this knowledge are even mathematically provable to be correct and bug-free programs!

Specifically in terms of JS — I absolutely love Sanctuary — and the community is amazing too. It’s a brilliant library that allows you to have real type classes, typechecked at runtime, and compose all sorts of functional programming thingiemabobbers. Same can be said for Fantasyland overall.

In my latest project though — I shied away from that a bit and tend to want a bit more of native interop with Typescript (which currently can’t support everything Haskell/Purescript can in this regard)… and it also worked out just great :)

There is work underway on all fronts to support more Haskell-like types (HKT’s specifically) in Typescript and I’ll definitely re-investigate this before starting my next project.

For now though, I’ve switched more to like a Rust/Elm-ish approach of like calling each objects specific map() and passing things along more in a fluent style, and fp-ts let me do that while also getting Typescript hints, so I’m using that and loving it. My only complaint is that it doesn’t have as much documentation or community support — but it’s an incredible library. YMMV — but I’d definitely suggest strongly looking at Sanctuary, or FP-TS if you are a Typescript user, for all your monad/functor/etc. needs!

Functional utility libraries

Partial.lenses, Ramda, Sanctuary, … there’s a bunch of stuff out there, and these are inevitably the things I say “oh I won’t need that…” almost immediately followed by npm install --save....

Given the brilliance of the library authors and their familiarity with functional abstractions, the funny thing is that there’s a lot of overlap and it sometimes feels like they are all solving all the problems (including eachothers).

One area I’d specifically like to get more familiar with are lenses in particular (provided by various libraries — but let’s say partial.lenses)… definitely only scratched the surface so far.

XState for state management

Moving in a bit of a different direction now, toward more of like the “framework” side of things than “approach”…

XState is a framework built on the idea of Statecharts — which have been reliably used for a long time in industries as crucial and different as aeronautics, medical devices, and game development.

Statecharts even have an official spec approved by the w3c called SCXML!

What XState does is implement statecharts through a JS API which is powerful and easy to reason about. Like many of the topics here, going into specific examples would require articles of their own — but at the end of the day, it gives you a guarantee that you will never be processing an event that isn’t fitting for the current state of the app, and that state transitions are not only explicit — but defined through a simple static definition.

If that doesn’t make sense — imagine if you have a “login” button that triggers an async request. You want prevent that button from firing another request when one is already active. Sure you can handle it on the UI side by disabling the button on the dom — but then you’ve scattered the logic across boundries and lose some separation of concerns. (okay fine — this approach is might be fine, but bear with me and keep in mind the “scalability” point at the top of the article). What XState allows you to do is to set it up so that your login button can have explicit states of, say, loading and waiting. Then, a LOGIN event can be handled only when its in the waiting state and suddenly entire classes of bugs vanish into thin air. You can also set it up to organically transition into a loaded state and/or do some side effect… like causing a React tree re-render… which brings me to…

React w/ Hooks

There isn’t really much to say about this, but I’ll say it anyway :)

Is React awesome? YES! Is it the most pure framework out there? No. Is it the fastest? No. Is it the easiest to reason about? No.

But! It is ubiquitous and great and such a fundamental part of a modern web app — that my perspective is if I’m not using React, I might as well just go full FP and use Purescript or go full performance and use Rust/WASM.

So React it is — and hooks are a welcome addition!

No classes (see above) — everything is a function (and no this/bind nonsense either).

I don’t use Redux because I don’t need it. With XState for state management (e.g. reducer on steroids) and the Context API — everything is covered, and more! (tip: XState even has its own hook useMachine)

Separation of concerns

This is kindof a generic topic but I kept it here because it has specific implications for React w/ XState. In short — code should be separated into different files based on whether it is the definition of a state machine, a logical/container component, a view component, pure functions, context definitions, etc.

There’s some flexibility on that so I don’t want to suggest an absolute or specific approach — but “separation of concerns” in file structure is important to me. In React, there’s a specific divide with components-

Logical / Container components

These components are the bridge between the input, the state management (e.g. xstate) and the output (e.g. view components, next section).

They use hooks extensively — though as it happens, it’s often just a bus for shuttling things back and forth between the input/state/output. Often there isn’t actually much logic in the components themselves!

Oh one more thing — these components do not use JSX. At all.

JSX just feels… weird and clunky. Javascript has first class support for nesting things in function calls, and passing props. I don’t even get what problem JSX is trying to solve! Simply import {createElement as el} from "react" and use el() for what it is — an actual function call!

There is one exception though…

View components are different

JSX does make sense to me as something like a dynamic HTML processing language thing, and for that purpose its amazing. View components should be 100% declarative expression of props (no state) and don’t even contain any complex logic (besides conditional rendering).

More specifically, to my mind, view components are conceptually where side effects happen. In fact it should be possible to swap out a DOM renderer for like a WebGL renderer with absolutely zero changes in the Logic/Container components.

For example — a view component will typically receive an onClick handler via a prop and pass that along as-is to the final dom-esque JSX. It will not define the logic of that onClick handler, even if it’s as simple as a call to log or whatever. Again — this seems like a trite example, but keep in mind the “scaling” point at the beginning of the article.

Also — React gives special meaning to null and doesn’t understand a Nothing/None type of a Maybe. Therefore the view components typically do receive null values so that its easy to play nicely with idiomatic and typical React. In my eyes this is fine because , just to emphasize — the view components are effectively a side effect at the end of the pipeline, and at that point it should be focused on performance and have no possibility for errors anyway. Thankfully it’s easy to fold() an Either, Maybe, or Future into a null when we really need to — at the IO boundary :)

That’s all for now!

Projects mentioned:

Additional unsorted resources:

--

--