Async computation with Validation in Scala using Scalaz

Being writing a completely async library to access a remote service (using Play2.0), I'm using Promise and Validation to create non-blocking call, which has a type presenting fail and valid result at once.

Promise comes from Play2-scala, where Validation comes from scalaz.

So here is the type of examples of such functions

  • f :: A => Promise[Validation[E, B]]
  • g :: B => Promise[Validation[E, C]]
  • So far, so good, now if I want to compose them, I can simple use the fact that Promise present a flatMap , so I can do it with a for-comprehension

    for (
       x <- f(a);
       y <- g(b)
    ) yield y
    

    Ok, I took a shortcut to my problem here because I didn't reused the Validation results within the for-comprehension. So if I want to reuse x in g , here is how I could do

    for (
       x <- f(a); // x is a Validation
       y <- x.fold(
          fail => Promise.pure(x),
          ok => g(ok)
       )
    ) yield y
    

    Fair enough, but this kind of boilerplate will go to pollute my code over and over again. The problem here is that I've a kind of two-levels Monadic structure like M[N[_]] .

    At this stage, is there any structure in f° programming that enables working with such structure by skipping easily the secong level:

    for (
       x <- f(a); //x is a B
       y <- g(b) 
    ) yield y
    

    Now, below is how I achieved something similar.

    I created kind of Monadic structure that wraps the two level in one, let say ValidationPromised which pimped the Promise type with two methods:

    def /~> [EE >: E, B](f: Validation[E, A] => ValidationPromised[EE, B]): ValidationPromised[EE, B] = 
        promised flatMap { valid => 
            f(valid).promised
        }
    
    def /~~>[EE >: E, B](f: A => ValidationPromised[EE, B]): ValidationPromised[EE, B] = 
        promised flatMap { valid => 
            valid.fold (
                bad => Promise.pure(KO(bad)),
                good => f(good).promised
            )
        }
    

    This allows me to do such things

          endPoint.service /~~>                                   //get the service
          (svc =>                                                 //the service
            svc.start /~~> (st =>                                 //get the starting elt
              svc.create(None) /~~>                               //svc creates a new elt
              (newE =>                                            //the created one
                newEntry.link(st, newE) /~~>                      //link start and the new
                (lnk => Promise.pure(OK((st, lnk, newE))))        //returns a triple => hackish 
              ) 
            )
          )
    

    As we can see /~~> is pretty similar to flatMap but skips one level. The problem is the verbosity (that's why "for-comprehension" exists in Scala and "do" in Haskell).

    Another point, I've the /~> that stands like a map also but works on the second level (instead of the Valid type -- third level)

    So my second question is corollary to the former... Am I approching a sustainable solution with this construction ?

    sorry to be that long


    The concept you are looking for here is monad transformers. In brief, monad transformers compensate for monads not composing by allowing you to "stack" them.

    You didn't mention the version of Scalaz you are using, but if you look in the scalaz-seven branch, you'll find ValidationT. This can be used to wrap any F[Validation[E, A]] into a ValidationT[F, E, A] , where in your case F = Promise . If you change f and g to return ValidationT , then you can leave your code as

    for {
      x ← f(a)
      y ← g(b)
    } yield y
    

    This will give you a ValidationT[Promise, E, B] as a result.

    链接地址: http://www.djcxy.com/p/60330.html

    上一篇: 支持JSON数组的JSON SerDe for Hive

    下一篇: 使用Scalaz在Scala中验证异步计算