使用Scalaz在Scala中使用Validation进行异步计算

时间:2012-06-10 10:47:27

标签: scala playframework-2.0 scalaz

正在编写一个完全异步库来访问远程服务(使用Play2.0),我正在使用PromiseValidation创建非阻塞调用,其类型呈现失败,一次有效的结果。

Promise来自Play2-scala,其中Validation来自scalaz。

所以这里是这种函数的例子类型

  • f :: A => Promise[Validation[E, B]]
  • g :: B => Promise[Validation[E, C]]

到目前为止,非常好,现在如果我想要撰写它们,我可以简单地使用Promise呈现flatMap的事实,所以我可以用for-comprehension

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

好的,我在这里找到了问题的快捷方式,因为我没有在for-comprehension中重用Validation结果。因此,如果我想在x中重用g,我就可以这样做

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

足够公平,但这种样板会一遍又一遍地污染我的代码。这里的问题是我有一种两级Monadic结构,如M[N[_]]

在这个阶段,f°编程中是否有任何结构可以通过轻松跳过secong级别来使用这种结构:

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

现在,下面是我取得类似成就的方法。

我创建了一种Monadic结构,将两个级别包装在一个中,假设ValidationPromised用两种方法对Promise类型进行了操作:

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
        )
    }

这允许我做这样的事情

      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 
          ) 
        )
      )

我们可以看到/~~>flatMap非常相似,但跳过了一个级别。问题在于冗长(这就是Scala中存在“for-comprehension”和Haskell中“do”的原因)。

还有一点,我的/~>就像一个map,但是在第二级(而不是有效类型 - 第三级别)上工作< / p>

所以我的第二个问题是前者的必然结果......我是否正在采用这种结构来实现可持续解决方案?

很抱歉这么久

1 个答案:

答案 0 :(得分:4)

您在这里寻找的概念是monad transformers。简而言之,monad变换器允许你“堆叠”它们来补偿monads not composing

您没有提到您正在使用的Scalaz版本,但如果您查看scalaz-seven branch,则会找到ValidationT。这可用于将F[Validation[E, A]]包装到ValidationT[F, E, A]中,在您的情况下F = Promise。如果您更改fg以返回ValidationT,那么您可以将代码保留为

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

这将为您提供ValidationT[Promise, E, B]