How to chain functions returning Validated, Option, Either? (Monad Transformer)

1.1k Views Asked by At

I have simple three functions returning arrow-kt data types

fun validate(input): Validated<Error, Input> = ...
fun fetch(input): Option<Error, InputEntity> = ...
fun performAction(inputEntity): Either<Error, Output> = ...

And want to chain something like this (can use any available function instead of map)

validate(input)
  .map{fetch(it)}
  .map{performAction(it)} 

Only solution I could come up with is to replace Validated and Option with Either and chain using flatMap. Is there any better functional way to make it work without updating the existing functions?

2

There are 2 best solutions below

0
On BEST ANSWER

What @pablisco described is correct, but you can keep it simpler by using some syntax extensions we provide to convert from one type to the other. Note that both options are correct, but Monad Transformers can be a bit convoluted and too powerful, and they're also prone to get removed from Arrow soon, once we finally figure out our delimited continuations style completely. But that is out of scope here. Here is how you could solve it by using the extensions I mentioned:

import arrow.core.*
import arrow.core.extensions.fx

sealed class Error {
  object Error1 : Error()
  object Error2 : Error()
}

data class InputEntity(val input: String)
data class Output(val input: InputEntity)

fun validate(input: String): Validated<Error, InputEntity> = InputEntity(input).valid()
fun fetch(input: String): Option<InputEntity> = InputEntity(input).some()
fun performAction(inputModel: InputEntity): Either<Error, Output> = Output(inputModel).right()

fun main() {
  val input = "Some input"

  Either.fx<Error, Output> {
    val validatedInput = !validate(input).toEither()
    val fetched = !fetch(validatedInput.input).toEither { Error.Error1 /* map errors here */ }
    !performAction(fetched)
  }
}

Hope it was useful

0
On

What you are looking for is called a Monad Transformer. In Arrow, you may have seen them already, they end with a T at the end. Like OptionT or EitherT.

There are some good examples here for EitherT:

https://arrow-kt.io/docs/0.10/arrow/mtl/eithert/

And here for OptionT:

https://arrow-kt.io/docs/0.10/arrow/mtl/optiont/

The idea would be that to choose what your final value is going to be (let's say Either) and using an FX block you can then use EitherT to convert the other types to an Either.