DEV Community

Discussion on: May 15th, 2020: What did you learn this week?

Collapse
 
vonheikemen profile image
Heiker

I finally understood one of the use cases for fantasy-land/ap. Maybe it has more but I haven't seen them yet. These things are not common on javascript so it took me a while. So it turns out that ap doesn't do much by itself, you have to combine it with fantasy-land/map. People actually do this on a helper function they call liftA2 (yes there is a liftA3, liftA4 and so on).

The closest thing to a useful real world example I could think of was something like this.

// Somewhere in a utils.js file

const Task = {}

Task.ap = function(Fn, data) {
  return data.then(value => 
    Fn.then(fn => fn(value))
  );
}

Task.liftA2 = function(fn, A, B) {
  const curried = a => b => fn(a, b);
  return Task.ap(A.then(curried), B);
}

// the thing you're actually doing

function fetch_name(url) {
  return fetch(url).then(r => r.json()).then(res => res.name);
}

function get_name(id) {
  console.log('fetch character');
  return fetch_name('https://swapi.dev/api/people/' + id);
}

function get_planet(id) {
  console.log('fetch planet');
  return fetch_name('https://swapi.dev/api/planets/' + id);
}

function whoami(name, place) {
  return `I am ${name} from ${place}`;
}

Task.liftA2(whoami, get_name(1), get_planet(1))
  .then(console.log);

This should show.

fetch character
fetch planet
I am Luke Skywalker from Tatooine

So I took a regular function that takes two arguments (whoami) and made it work in the context of a Promise, this what they call lift. The A2 part is because it works with two arguments (I guess). Now, for this pattern to be actually useful you would have to design your own data structure that follows some of the rules in fantasy-land.