使用Scalaz在Scala中验证异步计算
正在编写一个完全异步库来访问远程服务(使用Play2.0)时,我使用Promise
和Validation
来创建非阻塞呼叫,该呼叫具有一种呈现失败和有效结果的类型。
Promise
来自Play2-scala,其中Validation
来自scalaz。
所以这里就是这种功能的例子
A => Promise[Validation[E, B]]
B => Promise[Validation[E, C]]
到目前为止,如此好,现在如果我想编写它们,我可以简单地使用Promise
呈现flatMap
这一事实,所以我可以用一个理解
for (
x <- f(a);
y <- g(b)
) yield y
好的,我在这里采取了一个快捷方式来解决我的问题,因为我没有在重新使用for-comprehension中的Validation
结果。 所以如果我想在g
重用x
,这里就是我可以做到的
for (
x <- f(a); // x is a Validation
y <- x.fold(
fail => Promise.pure(x),
ok => g(ok)
)
) yield y
很公平,但这种样板会反复污染我的代码。 这里的问题是我有一种像M[N[_]]
这样的二级Monadic结构。
在这个阶段,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
一样,但是在第二层次上工作(而不是有效类型 - 第三层次)
所以我的第二个问题是前者的必然结果......我是否正在采用这种结构来实现可持续解决方案?
很抱歉,那么久
你在这里寻找的概念是monad变形金刚。 简而言之,monad变压器通过补偿monad而不是让你“叠加”它们。
你没有提到你正在使用的Scalaz版本,但是如果你看看Scalaz-7分支,你会发现ValidationT。 这可用于将任何F[Validation[E, A]]
包装到ValidationT[F, E, A]
,其中您的情况为F = Promise
。 如果您更改f
和g
以返回ValidationT
,那么您可以将代码保留为
for {
x ← f(a)
y ← g(b)
} yield y
这会给你一个ValidationT[Promise, E, B]
。
上一篇: Async computation with Validation in Scala using Scalaz