Convert Fluture Future to folktale Result

436 views Asked by At

I have the following sample code to fetch a uuid:

const Result = require('folktale/result')
const Future = require('fluture')

Future.prototype.flatMap = Future.prototype.chain

const fetch = Future.encaseP(require('node-fetch'))
const message = err => `ERROR: ${err.message}`

function getUuid () {
  return fetch('https://httpbin.org/uuid')
}

const future = getUuid()
  .map(response => {
    const responseJson = response.json.bind(response)
    const getResponseBody = Future.encaseP(responseJson)
    return getResponseBody()
  })
  .map(obj => obj.uuid)

const result = future.fold(Result.Error, Result.Ok).fork(x => x, x => x)
console.log(result) // [Function: Sequence$cancel]
result.map(
  x => {
    return message(x)
  },
  x => {
    console.log(x)
    return x
  }
)

The result is not a mappable type and throws an error when I invoke result.map:

TypeError: result.map is not a function
2

There are 2 answers

0
Zbigniew Zagórski On

fork executes future and expects that you handle results in callbacks you've provided in fork arguments, and more importantly returns cancel function, not next Future as you expect my applying .map on its result.

Change the code to:

const cancelToken = future
    .fold(Result.Error, Result.Ok)
    // if you wanna map result, do it here:
    // .map(...)
    .fork( // fork consumes the fluture and ends execution chain
        ok => message(x), 
        err => { console.log(err); }
    );
// call cancelToken() if you want to cancel it
0
Avaq On

It is impossible to convert a Future to a synchronous data structure, as it would have to block the thread. The only way to get at the result of a Future is to provide a callback.

In the following line:

const result = future.fold(Result.Error, Result.Ok).fork(x => x, x => x)

You've made the assumption that the return value of the callbacks you've provided to fork would determine the return value of the entire expression, and therefore be assigned to the result constant.

The problem, of course, is that these callbacks will be called eventually, in some later JavaScript tick, when the asynchronous work is done. This means that by the time one of the callbacks runs, the result variable will have long been assigned and probably also read.

Instead, fork returns an unsubscribe function. So the actual value you've assigned to result is that unsubscribe function.

You can call it to signal to the Future that you're no longer interested in the eventual result, allowing the Future to clean up.


There are two things you might do instead:

  1. Don't convert your Future to a Result, but convert Results to Futures instead. Futures are supersets of Result. They represent success and failure, as well as async and I/O. Since your program is asynchronous, and you cannot block the thread in Node, you'll most likely end up with a Future as your primary type at the edge of your program (similarly to how you end up with an async function at the entry point when you're using Promises).
  2. Provide the remainder of your program inside the callback. You've already folded the rejection branch away into the Result type, so you can safely use value from Fluture to provide a single callback:

    future.fold(Result.Error, Result.Ok).value(result => result.map(...))