What about an asynchronous Either?

Lucas Sunsi Abreu
magnetis backstage
Published in
5 min readMay 4, 2018

How the JS Promise design overlaps with the Either abstraction

Does this mean Promise to you as well? Unsure if it's a regional thing

Today, a few years after Promises took over our JavaScript day-to-day life, we look back and miss the good old callback days.

Remember how easy it was to just connect to a database, then reading something from it only to make a few calculations and save it back? Those three indentation levels on your last pseudo error handling bit. Those loyal if statements checking the truthiness of a solemn error variable, only it doesn't know exactly what to do when it invariably comes loaded with some bad news.

Yeah, no one misses those days. Callbacks are fine as a low-level construct, but when you want to coordinate many of them efficiently and safely, you'll most likely end up in the bad place everyone kept telling you about.

Enter Promises

Yeah, we had them in our JS lives way before the official inclusion in our beloved ECMAScript 2015. Libraries like bluebird and q had already implemented their versions of this amazing abstraction that would change our lives forever and everyone who was 'in the know' was using them before it was cool.

Nowadays Promises are ubiquitous and we dread the minutes we spend writing callbacks on that legacy code we have to support once a week or even using that renegade library with Peter Pan syndrome. (Even setTimeout seems weird to me, to be honest.)

We all know what Promises look like in the wild, but what I find particularly interesting are the similarities they have with the more formal Either type. If you know yourself a Haskell, you are most likely familiar with it. For Rustacians, it's ever-present but with another name. If you use Elixir, you are probably used to a kind of it, using tuples, atoms, and some syntax. But if you write some JavaScript, you live and breathe Either without even knowing.

Either… what?

Either is just a type that represents one of two things. That's it. It could be an int or a string. It could be an apple or a signed copy of your favorite album. It could Either be something or another something.

If it seems too simplistic, that's because it is.

The formal definition you'll most likely see is something along the lines of Either a b = Left a | Right b. The a and b can be anything, they're parameters. The | means it's either one or the other. It's either the Left one with a value like a, or the Right one with a value of type b. Can it be both? Can it be neither? Can it be Left with b? Right with a? No. (:

Ok, Either then. So what?

You might have realized this definition fits the Promise semantics pretty well. It has two kinds of result, two pipelines of processing, and it only ends up in one of them. Never none and never both.

In this sense, the resolve and reject functions on the Promise module are just simple constructors. Might we even call them… Left and Right?

I know, I know. It's a rather weak parallel and it doesn't mean much alone, but bear with me, there's more.

'map' isn't just for lists

Either works as functors, which means you can apply a transformation on the underlying data without touching the containing structure. In other words, you can map values into other values, without losing the Either container.

Since you don't know which of the two possible values the Either has, you usually have two mapping functions: one that operates on the left value and one that operates on the right one. Thankfully, the common names are helpful in this case: mapLeft / mapRight.

How does this translate to JS Promise? When stuff goes right, you can chain transformations on the data calling then. When stuff goes wrong, you can deal with the error chaining catch calls.

Now if you're following me on this, you might be thinking: you could map to other Promises in JS. What happens to the Either then?

Since you're used to promises, you know you don't end up in situations like this one. Nested promises aren't a thing, you don't have to worry about both of them. They are collapsed into one, right?

'flatMap' isn't just for lists either

Turns out that's a monadic property and it can be found in the Either type as well. There's a function that also maps the values, but, instead of mapping them just inside the container, it will let you map to another container and will merge those two containers into one.

In Either terms, you can map a value into another Either. The function will leave you with just one Either that resulted from the combination of both. In Promise terms, you can map a value to another Promise, and you'll be left with one Promise that combines both of them.

This (very powerful) function is usually called flatMap or andThen. In JS, it's called then (or catch). The same function that works like mapRight works as flatMap, depending on what you return. So the above code's semantics would actually be:

Why isn't it called Either instead of Promise then?

Fair question. I'm making a case that the Promise semantics we see in current JavaScript borrows a lot from the semantics of the Either abstraction. They aren't quite the same though, especially because of a simple overlooked fact until now: Either has nothing to do with asynchronous workflows, and that's all the JS Promise is about.

They came and took the land once ruled by callbacks. Synchronous workflows are easy and we needed no promises for that. They came to help with the hard bit, which is async workflows!

Yes, that's right. The thing is, Promise is a known abstraction as well. It just isn't as sophisticated as the JS implementation. Promise (in general) is a simple abstraction that represents a delayed value. In its simplest form, it doesn't support mapping, chaining computation or even error handling. It is just a value that will be there at some point but isn't right now. Whoever needs the value, will have to wait for it.

Waiting usually means dereferencing it or blocking the thread until the value is ready. In modern JS, we have a handy dereferencing function coded into a keyword many people love: await.

A JS Promise is, in fact, a Promise.

Thing is, it isn't just a Promise, it is something more. It's almost like they are a Promise that resolves to an Either. Or an Either with a Promise on each end.

JS Promises rock!

All in all, I really love the JS Promise abstraction and implementation, and it's not just because everyone was traumatized by working with callbacks.

The concept takes the simplistic and powerful Promise async primitive, throws in some error handling mechanics, and spices it up with powerful mapping functions that can be chained together. It then gets packaged with a ridiculously simple API, and there you go.

What we end up with is much cleaner code that makes asynchronous computations a lot easier to read while also making errors easier to manage and harder to ignore.

It's classy and precise.

--

--