Lazy functions in javascript
Hello!
I thought here to tell you how you can create and use lazy functions in JavaScript using the Fluture library. This will be a brief overview of how to create functions, how to handle errors and a little bit about parallelism. Functional programming brains will not soar! I promise!
Fluture
Fluture is a library developed by Aldwin Vlasblom that implements Future. Future is an alternative to Promise, which has a much more powerful API that allows you to implement cancellation (cancellation), safe "recursion", "error-free" execution (using Either) and another small cart of steep features.
I think it's worth telling you about the methods (monads) that I will use in the examples below.
.of(Any)
- creates Future from the passed value.map(Function)
- no, it is notArray.map
, it is a transformation function, similarPromise.then
.chainRej(Function)
- similarlyPromise.catch
catches a mistake.fork(Function, Function)
- starts execution of the future
Creating a lazy function
For myself, I highlighted two main approaches to creating lazy functions in Fluture. The first approach is that we create a function that accepts source data and returns a ready-to-run Future. The second approach is that we create a Future with all the described transformations, and then transfer data to it.
Unclear? Let's take an example! We have this function
const multiply10 = x => x * 10;
Now make it lazy using the first approach.
const multiply10 = x => x * 10;
const lazyMultiply10 = (x) =>
Future
.of(x) // Создаем Future из значения
.map(multiply10); // Теперь наша функция тут
lazyMultiply10(2).fork(console.error, console.log);
// -> 20
Too cumbersome, is not it? Let's try to write more succinctly, using the second approach.
const multiply10 = x => x * 10;
const lazyMultiply10 = Future.map(multiply10);
const value = Future.of(2); // Оборачиваем наше значение в Future
lazyMultiply10(value).fork(console.error, console.log);
// -> 20
Already better, but still cumbersome. It should be more compact!
const lazyMultiply10 = Future.map(x => x * 10);
lazyMultiply10(Future.of(2)).fork(console.error, console.log);
// -> 20
In fact, these approaches are not mutually exclusive and can be used together.
const lazyMultiply10 = Future.map(x => x * 10);
const someCalculation = a =>
Future
.of(a)
.map(v => v + 1)
.chain(v => lazyMultiply10(Future.of(v));
someCalculation(10).fork(console.error, console.log);
// -> 110
Error processing
Error handling in Future is practically the same as error handling in Promise. let'sremember Imagine a function that makes a request to a third-party, not very stable API.
const requestToUnstableAPI = query =>
request({
method: 'get',
uri: `http://unstable-site.com/?${query}`
})
.then(res => res.data.value)
.catch(errorHandler);
Same function but wrapped in Future
const lazyRequestToUnstableAPI = query =>
Future
.tryP(() => request({
method: 'get',
uri: `http://unstable-site.com/?${query}`
}))
.map(v => v.data.value)
.chainRej(err => Future.of(errorHandler(err));
In fact, error handling can be made more flexible. For this we need the structure of Either, and this is a little beyond the scope of my review.
Parallelism
To work with parallelism in Future, two methods are implemented race(Futures[])
(similar Promise.race
), parallel(n, Futures[])
and both(Future, Future)
, but it is a special case parallel
.
The method parallel
takes two arguments, the number of concurrently executed Future and an array with Future. To make the behavior parallel
the same as the method Promise.all
, you need to set the number of executables as Infinity
.
Here, too, without examples we can not do.
const requestF = o => Future.tryP(() => request(o));
const parallel1 = Future.parallel(1);
const lazyReqs = parallel1(
[
'http://site.com',
'http://another-site.com',
'http://one-more-site.com',
]
.map(requestF)
);
lazyReqs.fork(console.error, console.log);
// -> [Result1, Result2, Result3]
Promise compatibility
In JavaScript from Promise, there is no way to go, and hardly anyone will be happy if your method returns some strange Future. To do this, Future has a method .promise()
that will start the execution of the Future and wrap it in Promise.
Future
.of(10)
.promise();
// -> Promise{value=10}
Links
- Fluture library repository
- Article on Medium by Aldwin Vlasblom "Broken Promises"
- Fantasy-land specification
Here, perhaps, everything that I wanted to tell you. If the topic is interesting, let me know, I will tell you more about error handling. And yes, do not scold me much, this is my first post on Habré.