Dealing with the asynchronous code has always been a challenge in JavaScript. Callbacks are the classic way, and since then we've gained higher level abstractions and constructs for handling the problem.
This time around we'll discuss Fluture, a Fantasy Land specification compatible alternative to Promises
by Aldwin Vlasblom.
I was born and raised in Rotterdam, the Netherlands. I got into programming at age eleven when I decided to make a website for my RuneScape clan and happened upon Macromedia Flash with ActionScript. At age fourteen I had made several small sites for family members and decided to do my internship at a digital agency.
That's where I was introduced to PHP and realized I want to pursue an education in this area, which lead me to do a course in interactive media design and development.
During my second internship, I became responsible - and was later hired - to maintain the company's internal PHP framework. I loved making API's and abstractions for other developers and was fond of higher order functions.
It's, therefore, no surprise that I was drawn into the JavaScript functional programming world, and ended up creating an API which is based almost exclusively on higher order functions.
There are three approaches to introducing Fluture, depending on the background:
In it's simplest form, it's a function which takes a function and returns it wrapped in an object with the name "fork", perhaps best explained through code:
const Future = fork => ({fork});
Future((rej, res) => res(1)).fork(
console.error, console.log
); // 1
This structure becomes interesting once you add higher order functions like map
:
const Future = fork => ({
fork,
map: f => Future(
(reject, resolve) => {
fork(
reject,
x => resolve(f(x))
)
}
)
});
Future((reject, resolve) => resolve(1))
.map(x => x + 1)
.fork(console.error, console.log); // 2
The idea that you can map
over a Future similarly to how one might map
over an Array
comes from the research into "algebraic data types" brought to JavaScript most prominently by Fantasy Land.
Fluture builds on top of these ideas to add:
The description of Fluture states that it's an alternative to Promises, so it's only natural that people want to compare it. In my article comparing Futures to Promises I write:
On the surface Futures are just like Promises, but with the different behaviors of the
.then
method extracted into three distinct functions, each with a single responsibility.
The then
method of a Promise is massively overloaded: You can give it zero to two arguments, both are mixed types (Nil
and Function
). The return values of the functions are also overloaded: You can return any value, but returning something with a then
-method has a particular meaning. Throwing an error also has special meaning.
Extracting all of these behaviors to separate functions makes it easier to abstract over, and clarifies developer intent, making it simpler to detect mistakes.
I've also written about the differences between Fluture and other libraries which have a similar structure, in my article comparing them. I'll go into this when answering why I developed Fluture.
I got into functional programming some years ago when I discovered Ramda, and from there I became acquainted with Fantasy Land. The first algebraic type I decided to try was a Future. I tried data.task
and Ramda Fantasy.
A little later I was teaching asynchronous functional programming to a small group of developers, and I found that one of the biggest sources of confusion were the bizarre and cryptic error messages one would get out of these Future libraries from making simple mistakes.
I had also accumulated a set of common utilities that I was using with Futures, so I decided to create a Future library which would ship with these utilities and provide understandable error messages. I made sure that the performance remained decent because I did not want people to have to choose between good performance and a pleasant development experience.
I later discovered Sanctuary, with which Fluture shares a lot of its design philosophy. It became another important part of Fluture's design to integrate with Sanctuary nicely.
To learn more about Sanctuary, [read the interview of David Chambers](/blog/sanctuary-interview).
A critique Fluture and other Futures have always received is that they are not stack safe, unlike Promises. Promises execute every action immediately in the next JavaScript tick giving them an inherent stack safety, because every operation waits until the stack is cleared before executing.
Some weeks ago, by combining ideas from Promises, Fluture, and Free Monads, I created a stack safe proof of concept Future which does not use the next-tick-trick. I'm currently working on porting the entire Fluture library to this new architecture. It's already feature complete - it just needs some polishing before being released under version 6.0
in the coming months.
Fluture has earned a place in my personal toolkit when it comes to classic request-response applications, like those you find in web-servers. In this context, I consider it the best solution to the async problem (and the promise problem) that I've used to date, and I don't think that will change soon.
As for other kinds of applications, like the ones you might find running in browsers, I think we are moving towards reactive. Streams are the perfect async abstraction in these environments.
Streams are like Futures, except that they can produce more than one value. For an excellent Stream library I recommend most. And for an interesting way to use it, and think about front-end applications, I would recommend learning about CycleJS.
To learn more about CycleJS, [read the interview of André Staltz](/blog/cycle-interview).
Avoid using object and variable mutation as a feature for the functionality of your code. You are shooting yourself in the foot. Mutation is a means to optimize code.
I admire the works of Brian Cavalier, author of most, creed, and more.
I also think it may be good to interview Irakli Safareli, he has been an invaluable contributor to both Sanctuary and Fluture, and he's been exploring the little-explored field of Free Monads with his project Free.
Lastly, I would like to give a shout-out to Roman Pominov who helped me bring cancellation into Fluture. He authored Kefir - the first Reactive Stream library I got into, and Static Land - an adaptation of Fantasy Land which pushes the community forward.
I think a wildly under-appreciated feature of Monads is Monad Transformers, I've scratched the surface of what they are capable of in my project momi, which implements the core ideas of Express in only a few lines of code by combining two existing Monads. I would like to see their usage grow.
Thanks for the interview Aldwin! It is always amazing to see new solutions to old problems. Sometimes reframing the problem somehow can lead to alternatives.
Check out Fluture GitHub page to learn more about the project.