让我们看一下Scalaz任务的完成实现
def onFinish(f: Option[Throwable] => Task[Unit]): Task[A] =
new Task(get flatMap {
case -\/(e) => f(Some(e)).get *> Future.now(-\/(e))
case r => f(None).get *> Future.now(r)
})
什么是*>在这里完成了吗?
答案 0 :(得分:6)
这是Apply
语法。我最近添加了一些使用apply语法到scalaz的examples子项目的示例,你可以在这里特别看到*>
和<*
的一些讨论:
这个想法是你正在评估组合器两侧的两个“有效”计算,使用Apply实例来组合效果,但丢弃其中一个结果值。 <*
会抛弃右侧的值,*>
会抛弃左侧的值。
在您的示例中,我们使用Apply [Future]来组合效果,效果是延迟计算未来。在第一个案例匹配中我们有这个:
f(Some(e)).get *> Future.now(-\/(e))
因此f(Some(e)).get
在我们应用Future[Unit]
函数时返回Task
正在包装的f
,此任务正在运行其副作用。应用Future.now(-\/(e))
的右半部分是我们想要返回的值Future
,但我们希望这个未来能够对副作用Future[Unit]
的结果产生影响。结果是我们得到Future[-\/]
,但是在副作用完成之前它是不完整的。
我发现这些组合器易于理解的一个很好的例子是解析器组合器。我们假设我们有一些解析器:
trait Parser[A]
这会消耗一些输入并在过程中产生A。假设我们有一个解析角色的方法:
def chr(c: Char): Parser[Char]
和一些解析一些任意字符串的方法:
def foo: Parser[String]
然后我们可以为括号内的任意字符串创建一个解析器:
val parentheticalFoo: Parser[String] = chr('(') *> foo <* chr(')')
这会创建一个解析器,虽然它会使用一个开括号,然后是一个foo,然后是一个闭括号,它只会返回解析foo的结果。我们并不关心实际接收chr('(')
和chr(')')
parers的输出,但我们希望它们消耗输入的效果仍然可以合并到生成的解析器中。