正在编写一个完全异步库来访问远程服务(使用Play2.0),我正在使用Promise
和Validation
创建非阻塞调用,其类型呈现失败,一次有效的结果。
Promise
来自Play2-scala,其中Validation
来自scalaz。
所以这里是这种函数的例子类型
A => Promise[Validation[E, B]]
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>
所以我的第二个问题是前者的必然结果......我是否正在采用这种结构来实现可持续解决方案?
很抱歉这么久
答案 0 :(得分:4)
您在这里寻找的概念是monad transformers。简而言之,monad变换器允许你“堆叠”它们来补偿monads not composing。
您没有提到您正在使用的Scalaz版本,但如果您查看scalaz-seven branch,则会找到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]
。