Using JavaScript to Learn Haskell

Steven Syrek
8 min readAug 8, 2016

--

I remember when I first learned of the existence of Haskell. It was actually around the time that Apple first released Swift. Excited by the prospect of learning an entirely new language and participating in an entirely new language community, I dove right in — and immediately slammed into the hard concrete of Optionals. I was suddenly confronted with implications of type safety I had never thought about before. What should happen if you try to access a value that does not exist? Should the compiler give you an error? A warning? Should your program compile but throw an exception at runtime? Is it the responsibility of the programmer to handle errors or the compiler to refuse to emit unsafe code? One question that had not occurred to me was whether the designers of a language themselves should do something syntacticly to prevent errors. In Swift, optionals were the proposed solution to the conundrum of handling values that might not exist. When accessing a value from a dictionary, for example, Swift does not return the value for a given key but an optional value that includes, as part of the value’s type, the possibility that the value does not actually exist. In order to access the raw value, you have to either forcibly “unwrap” it or use an if let expression to test for the possibility of nil:

Words can hardly express, for the newcomer to Swift, just how annoying this was. In earlier versions of the language, you could rapidly end up with if let pyramids of doom if you had multiple optionals to unwrap. This problem was mitigated somewhat in Swift 1.2 and finally addressed head-on with the addition of the guard statement to Swift 2.0. But by that point, I had already moved away from Swift. Wondering where concepts like optionals came from, I early-on discovered something called “functional programming” and a language called “Haskell” that everybody said is “hard”. Since I am always interested in getting closer to the source of ideas, and rather stubborn about learning things people claim to be difficult, I thought it could only enhance my understanding of Swift if I learned Haskell, too. What I did not expect was to be drawn into the FP world so completely that I lost interest in Swift entirely.

It’s not that Swift isn’t an interesting language. I definitely prefer it to Objective-C, which I never managed to learn in-depth (I blame my early exposure to C++). It’s just that after encountering Haskell and functional programming in its natural habitat, I found Swift to be ultimately much less exciting than I had thought it would, could, or should be. The way I see things, Swift is trying too hard to have it both ways: it combines the type safety of a language like Haskell with the syntactic pizazz of dynamic scripting languages such as Python and Ruby. This latter quality, I believe, is what leads people to refer, incongruously, to certain programming languages as “fun”. As far as language evolution is concerned, Swift is also just another iteration of the ALGOL/C family. Will it also mimic the tendency of C++ to pack in as many features as possible, whether or not they contradict one another? Sure, it supports Unicode, so you can assign values to unicorns and piles of poop, but it does not otherwise seem like a significant advancement to me.

Learning Haskell, on the other hand, was like entering a portal to another universe, one with entirely different laws of physics. Suddenly, I couldn’t reassign variables (what?); or create objects to represent my data (What?); or even perform basic I/O operations, like printing to the screen, in an obvious and straightforward manner (WHAT?). What I could do, however, is write the most elegant, concise, and correct code of my life. Sure, it all looks like algebraic alphabet soup at first. But once you get past the general lack of curly braces and parentheses, once you learn to think in terms of “why” instead of “how” to do things, once you accept the infelicity of languages that model the operations of the machine instead of the operations of problem solving, you begin to embrace the virtues of things like functional purity, composition, and referential transparency and cease to fear the abstractions represented by abstruse terms like functor and monad.

As a means to overcome the hurdles to learning Haskell, which are real enough, I decided to re-implement a selection of its functions in JavaScript. With the ES2015 additions to the language, which I also wanted to learn, this seemed like it would be a more attainable goal that I could accomplish with less syntactic cruft than would have been possible before. Moreover, I was already familiar with JavaScript (unlike Swift), and, since it has higher-order functions and a rather relaxed approach to type safety, I thought I would be able to interpret Haskell code faithfully enough and without having to satisfy the demands of a language (like Swift) with a more cantankerous compiler. To put it another way, JavaScript’s weaknesses as a language are what made it an ideal environment in which to experiment with what I found to be a stronger programming paradigm.

I quickly realized, on beginning this ambitious project, that I could not simply jump in and start writing interesting functions right away. If I wanted my JavaScript code to even approach the elegance of Haskell, I had some plumbing to do first. Haskell functions are automatically curried and are, partly for that reason, composable. In order to establish the same foundation for my code, I started this adventure by writing two utility functions, partial and $. The partial function acts as a kind of currying API by abstracting away the boilerplate code that would otherwise be required to make partial application possible for a JavaScript function:

This function works by taking its parameters — a function followed by all the arguments that function expects — and recursively applying the function to each argument until the function is either fully applied or it runs out of arguments. In the former case, partial simply calls the function and returns whatever value the fully-applied function evaluates to. In the latter case, partial returns a new function partially applied to the given arguments and ready to accept further arguments until it is fully applied.

The $ function works like the Haskell composition operator:

All this function (in the guise of an infix operator) does is take two functions, f and g and compose them, which means returning a new function that applies f to the result of g applied to the argument x. Here’s my JavaScript version:

Since the dot is already spoken for in JavaScript, I chose to represent my own, comparable operation with the bling — it is, at least, a nod to Haskell’s own right-associative binding operator, also $. Note that it works similarly to partial insofar as, in the absence of an x argument, it returns a new function that expects one and, given an x, it simply evaluates the composition of f and g. Also: one-liner!

With this groundwork laid, I was able to begin construction of the rest of the project’s edifice. I want to present here just a few, very basic examples of what is possible with functional programming by showing some easy to read Haskell functions along with my own JavaScript versions. In Haskell, boolean operators are just functions like any others. They may be defined as infix functions for the sake of clarity (and consistency with other languages), but this is only syntactic sugar, and it isn’t even mandatory. This is how the basic boolean operators AND, OR, and NOT are defined in Haskell:

The first line of each function is called a “function signature.” The signature expresses the types of the function’s parameters and return values. The arrows indicate function applications. For the sake of simplicity, you could regard the final type as the actual return type of the function and the others as arguments. For example, && takes two arguments, a boolean and another boolean, and returns a boolean. That part should be straightforward. What is actually happening, however, is that && takes a single boolean argument and returns a new function that is partially applied to that argument. This new function also takes a single boolean argument. When it is applied to that argument, the function as a whole is then fully applied, and the result reduces to a boolean value, True or False. The body of the function performs pattern matching to determine this final value. If the first argument is True, the final value will be the same as the value of x. If the first argument is False, however, then it doesn’t matter what the second argument is, because the function will always evaluate to False in that case. The underscore indicates that the second value does not matter. The patterns in the other functions ought to be self-explanatory along these lines. No need for extra syntax, like if let expressions and guard keywords here!

My JavaScript versions are not quite as easy on the eyes, but they have the same meaning. Unfortunately, without Haskell’s built-in currying and type checking, I could not avoid exposing some of the plumbing I installed to replicate them:

The important parts of these functions, two lines in each one, are identical to their Haskell counterparts. The bulk of them, however, are given over to type checking (which I arguably could have left out of these examples) and the affordance for partial application. In the future, I may want to replace the rather clunky type checking with something invisible, built using the new Proxy and Symbol APIs. I may even be able to do the same for partial application, trapping function calls with Proxy and doing all the Haskell-y stuff behind the scenes. In the meantime, I think it’s salutary to see how these things can be implemented with less complicated code as well as the challenges of adapting a strongly-typed paradigm to a weakly-typed language.

For my final two examples, I will show off partial application and function composition together. Haskell defines two functions, even and odd in terms of one another:

Each function takes a value of type a, which must be some kind of integer, and returns a boolean. Note again the patterns: if n is an even integer, then even n returns True. In this function, you can also see the remainder or modulo function rem used as an infix operator, which is what the backticks signify. For odd, you could obviously write something like n `rem` 2 > 0, but instead even is composed with the not function from above. It seems like such a small fragment of code, but why not reuse it? If a value is even, then it is not odd. And vice-versa. This sort of code reuse enabled by composition is fundamental to the functional programming philosophy, even if the size of the code seems trivial. Ideally, most functions should be as small as possible. To build up larger operations, piece the smaller ones together. Now, let’s see what this looks like in JavaScript, as written by me:

Although I had to implement them as two, separate functions, the meaning again remains the same. Note that my odd function uses the composition function, $, I defined above. Note also the use of partial application. The $ function takes not and returns a new function that applies not to even, which also returns a new function that expects a value, a. If you’re used to function arguments all living happily together in comma-separated lists inside of parentheses, this format will no doubt strike you as strange. But it works to transform the higher-order functions of JavaScript into even more powerful curried functions, which are as easy to pass around as raw values themselves. What you might also notice, however, is that these two functions are also fundamentally unlike their Haskell equivalents. In fact, they are fundamentally broken. See if you can figure out why, and then consider the implications for writing clear, concise, pure functional code in a language that is not designed to support it.

If you found this brief excursion into Haskell-style JavaScript intriguing, please check out my complete library of Haskell functions translated into JavaScript, maryamyriameliamurphies. They’re fully-documented and might even turn out to be useful to you, whether as pedagogical aids or models for functional production code.

--

--

Steven Syrek

High functioning, mostly functional programmer. Words here, codes at github.com/sjsyrek.