Which Monad Transformer to use?

后端 未结 2 1512
南方客
南方客 2021-01-13 17:11

I am trying to write the validate function below so that the validation stops after the first error encountered. The return type of three is different to the ot

相关标签:
2条回答
  • 2021-01-13 17:59

    There's nothing much to add to Travis' answer (as usual), but in the case you're using this in a Play! application, maybe https://github.com/Kanaka-io/play-monadic-actions can provide some help.

    0 讨论(0)
  • 2021-01-13 18:03

    There are two general approaches you can take in a situation like this. The first is to make all your methods return the stack you know you'll be working with (in this case EitherT[Future, Int, ?]), or you can have each individual method return the type that most accurately captures its own effects, and then raise the values you get appropriately when you compose them.

    The first approach can make usage more syntactically convenient if you know exactly what that usage is going to look like, but the latter approach is more flexible, and in my opinion generally the better choice. In your case it'd look something like this:

    import scalaz._, Scalaz._
    import scala.concurrent.Future
    import scala.concurrent.ExecutionContext.Implicits.global
    
    def one(a: String): Disjunction[Int, String] = (a == "one").either("one").or(2)
    def two(a: String): Disjunction[Int, String] = (a == "two").either("two").or(3)
    
    def three(a: String): EitherT[Future, Int, String] = EitherT(
      Future(a == "three").map(_.either("three").or(4))
    )
    
    def validate(a: String) = for {
      e1 <- EitherT.fromDisjunction[Future](one(a))
      e2 <- EitherT.fromDisjunction[Future](two(a))
      e3 <- three(a)
    } yield (e1 |+| e2 |+| e3)
    

    And then:

    scala> validate("one").run.foreach(println)
    -\/(3)
    
    scala> validate("x").run.foreach(println)
    -\/(2)
    

    If for some reason you had a plain old Future that you wanted to use in the for-comprehension, you could lift it into the EitherT[Future, String, A] with .liftM[EitherT[?[_], String, ?]].

    (Note that this method probably isn't terribly useful, since it'll never succeed (a string can't be equal to "one", "two", and "three" at the same time), but at least the composition works out.)

    About how to pick the monad transformer stack more generally: you just turn the types inside out, so that Future[Disjunction[Int, ?]] becomes EitherT[Future, Int, ?], etc. In this case specifically, Future does not have a monad transformer (it's not traversable and it's impossible to implement FutureT without blocking), so you know it has to go on the inside, anyway.

    0 讨论(0)
提交回复
热议问题