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 not Array.map, it is a transformation function, similarPromise.then
  • .chainRej(Function)- similarly Promise.catchcatches 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 paralleltakes two arguments, the number of concurrently executed Future and an array with Future. To make the behavior parallelthe 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



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é.


Also popular now: