Skip to content

About the author of Daydream Drift

Tomasz Niezgoda (LinkedIn/tomaszniezgoda & GitHub/tniezg) is the author of this blog. It contains original content written with care.

Please link back to this website when referencing any of the materials.

Author:

The Unrewarding JavaScript Functional Programming

Published

I like how functional programing, erm... functions. But JavaScript just isn't ready for it.

FP Is NOT Programming With Functions

Instead, FP superseeds it. This is huuuuuuuuge and I wasn't aware of this for a long time. In fact, I'd go so far as to say programming with functions is not FP. Without monads, immutability and other FP concepts - which is more than just programming with functions - it's impossible to realise the true potential of FP. These concepts go by many names and can be realized in a variety of ways. For example, Folktale's Maybe monad can be similarly implemented in ReasonML using pipe and type. Without these "helpers", I wrote OOP code that had functional elements and the benefits of FP didn't surface. And there are benefits! But they require more commitment to FP than just treating functions as first-class citizens.

JavaScript is definitely expressive enough to allow all FP concepts to be implemented. However, what I found was that the language isn't comfortable and concise to write FP code in, compared to programming languages which are FP focused. It's far too easy to switch paradigms by accident. The fact JS allows writing code in multiple paradigms makes me lazy. It also exposed my lack of knowledge about FP while I was learning it. My implementations would often be only partialy FP and break some fundamental concepts, like immutability or statelesness. These sometimes must be broken for practical reasons. But it should be an exception to the rule and not standard practice.

Libraries Roundup

The best combination of FP libraries I've found after playing around with FP in ES6 JS is the following:

They all provided some important tools for writing my code. But the end result was code which wasn't markedly better than imperative code I wrote before. On top of that, I had to move slower to make sure I was following FP rules. Mistakes were often difficult to debug due to too many anonymous functions and overusing the point-free style. I was definitely trying too hard to make my code as FP as possible and, apparently, many developers fall in the same trap. But due to a lack of restrictions in JavaScript, I was allowed to do so.

Code sample:

/*
- excessive use of point-free style and pipe/chain
- spread and rest syntaxes should be avoided
- trying too hard to use functions from libraries to support piping between them instead of creating my own functions
- map function does something different depending on the data structure it's applied to, decreases readability
- Result.try(...) required to adapt JS to FP
- pipe(...) under handlers key is called at the end ( (requireContext) ), 
  but the flow of function execution is top to bottom.
  This is a JS syntax limitation and makes code difficult to analyze.
*/

chain((options) => Result.try(() => {
  const { requireContext } = options
  return {
    ...options,
    handlers: pipe(
      tapLog(`Requiring all handlers.`),
      requireAll,
      tapLog(`Required all handlers (some could've failed).`),
      tapLog(`Removing requires that failed.`),
      filter(pipe(
        tap((maybeError) => maybeError.mapError(console.error)),
        (maybeError) => maybeError.map(stubTrue).mapError(stubTrue)
      )),
      map(map(unpackDefault)),
      filter(map(isFunction)),
      tap((requiresLeft) => console.log(`There are ${requiresLeft.length} callable handlers left.`))
    )(requireContext)
  }
}))

There are also a few different standards for writing FP JavaScript. A FP library may not be fully compatible with other libraries and require some adapting. Fantasy Land seems to be the most spread standard. Some libraries implement many standards simultaneously to "fit" multiple usecases.

While FP libraries help a lot, there's no way to avoid using regular npm packages in projects. These rarely support FP outside of providing functions to the client, i.e. the programmer. Adapting regular JavaScript code to work in FP situations is a tedious process. Nowadays, using external libraries, even for simple tasks, is incredibly common. What I found, was that I spent a fair amount of time creating adapters. They also occupied a significant portion of the files I wrote in FP. Worst part, though, is that they don't add value to the project - no type checking, improved performance, clearer code or anything. They're just a baseline thing that has to be done before being able to write anything useful.

Takeaways

I'm not completely giving up on FP, but taking a break from functional JavaScript. As of now, JavaScript isn't ready for full on functional programming. There's really no ideal and single approach to writing functional JavaScript. I appreciate that many great developers are trying their best to release libraries which help ease the pain of writing functional code in JS. But it's not enough to convert me to a full-time FP developer and promote FP in JavaScript.

I'm currently looking into an alternative, called ReasonML. After checking out ClojureScript and Elm, I believe Reason is the easiest one to get into for JS programmers and hits a lot of the right notes. Its syntax encourages writing functional code and provides syntax and escape hatches to connect with any JavaScript, including npm libraries. It can be used alongside JavaScript and gradually replace code written in it.