Search code examples
javascriptfunctional-programmingmonadsramda.jsramda-fantasy

How to transfer Either.Right to Either.Left?


db.findUser(id).then(R.pipe(
  R.ifElse(firstTestHere, Either.Right, () => Either.Left(err)),
  R.map(R.ifElse(secondTestHere, obj => obj, () => Either.Left(err))),
  console.log
))

If the first test doesn't pass, it will return Either.Left, and the second one won't be called. It will out put:

_Right {value: user}

But if the first one passed but the second one doesn't, it will become:

_Right {value: _Left {value: err}}

I want it to output _Left {value: err} only, how to fix the code or is there any way to transfer the Right to the Left?


Solution

  • What you've noticed there is that map is unable to "flatten" the two Either instances together. To do that, you will want to use chain instead.

    db.findUser(id).then(R.pipe(
      R.ifElse(firstTestHere, Either.Right, () => Either.Left(err)),
      R.chain(R.ifElse(secondTestHere, Either.Right, () => Either.Left(err))),
      console.log
    ))
    

    This pattern of composing series of calls to chain together can also be achieved with composeK/pipeK, where each function that is to be composed must take the form Monad m => a -> m b, i.e. a function that produces some monad (such as Either) from a given value.

    Using R.pipeK, your example could be modified to something like:

    // helper function to wrap up the `ifElse` logic
    const assertThat = (predicate, error) =>
      R.ifElse(predicate, Either.Right, _ => Either.Left(error))
    
    const result = db.findUser(id).then(R.pipeK(
      assertThat(firstTestHere, err),
      assertThat(secondTestHere, err)
    ));