execute Fluture task with Sancuary Either

288 views Asked by At

I have a pipe like this

const asyncFn = (x) => {
    return Future.tryP(() => Promise.resolve(x + ' str2'))
};

const pipeResult = S.pipe([
    x => S.Right(x + " str1"), // some validation function
    S.map(asyncFn),
])("X");

pipeResult.fork(console.error, console.log);

I want to do some async operation in asyncFn. The problem is when I have Right as input I can fork it anymore.

When I log the pipeResult I see this:

Right (tryP(() => Promise.resolve(x + ' str2')))

How can I do this?

1

There are 1 answers

1
davidchambers On BEST ANSWER

Either a b and Future a b are both capable of expressing failure/success. When working with asynchronous computations it is usually better to use Future a b rather than Future a (Either b c). The simpler, flatter type requires less mapping: S.map (f) rather than S.map (S.map (f)). Another advantage is that the error value is always in the same place, whereas with Future a (Either b c) both a and b represent failed computations.

We may, though, already have a validation function that returns an either. For example:

//    validateEmail :: String -> Either String String
const validateEmail = s =>
  s.includes ('@') ? S.Right (S.trim (s)) : S.Left ('Invalid email address');

If we have a value fut of type Future String String, how do we validate the email address fut may contain? The first thing to try is always S.map:

S.map (validateEmail) (fut) :: Future String (Either String String)

It would be nice to avoid this nesting. In order to do so, we first need to define a function from Either a b to Future a b:

//    eitherToFuture :: Either a b -> Future a b
const eitherToFuture = S.either (Future.reject) (Future.resolve);

We can now transform an either-returning function into a future-returning function:

S.compose (eitherToFuture) (validateEmail) :: String -> Future String String

Let's revisit our use of S.map:

S.map (S.compose (eitherToFuture) (validateEmail)) (fut) :: Future String (Future String String)

We still have nesting, but now the inner and outer types are both Future String _. This means we can replace S.map with S.chain to avoid introducing nesting:

S.chain (S.compose (eitherToFuture) (validateEmail)) (fut) :: Future String String