const Task = require('data.task') const Either = require('fantasy-eithers') // data.task actually uses `ap` with reversed // arguments, as the spec originally did, so // this is an "old-fashioned" lift2. const lift2 = (f, a, b) => a.map(f).ap(b) const queens = [ 'Alyssa', 'Katya', 'Willam' ] // Let's pretend this does some AJAX... const getById = id => new Task((rej, res) => queens[id] ? res(queens[id]) : rej('Out of range')) const append = x => xs => [... xs, x] // insideOut :: Applicative f // => [f a] -> f [a] const insideOut = (T, xs) => xs.reduce( (acc, x) => lift2(append, x, acc), T.of([])) // paralleliseTaskArray :: [Int] -> Task e [User] const paralleliseTaskArray = users => insideOut(Task, users.map(API.getById)) Array.prototype.traverse = function (T, f) { return this.reduce( // Here's the map bit! vvvv (acc, x) => lift2(append, f(x), acc), T.of([])) } const sequence = (T, xs) => xs.traverse(T, x => x) // [ 'Alyssa', 'Katya' ] ;[0, 1].traverse(Task, getById) .fork( e => console.log('ERROR') , x => console.log(x) ) // toChar :: Int -> Either String Char const toChar = n => n < 0 || n > 25 ? Either.Left(n + ' is out of bounds!') : Either.Right(String.fromCharCode(n + 65)) // Right(['A', 'B', 'C', 'D']) console.log( [0, 1, 2, 3] .traverse(Either, toChar)) // Left('-2 is out of bounds!') console.log( [0, 15, 21, -2] .traverse(Either, toChar))