扁平化嵌套的Scalaz验证

10

我对scalaz还不太熟悉,我从验证开始学起。

我有一些形式为以下的验证函数:

def validateXyz(...): ValidationNEL[String, String] = ...

我随后使用应用程序样式来组合多个验证,然后调用另一个也返回验证的函数:

(validateXyz(...) |@| validateAbc(...)) { (first, second) =>
   otherFunction(first, second)
}

其中,

def otherFunction(first: String, second: String): ValidationNEL[String, String] = ...

然而,调用上述代码的结果类型为:

val result: ValidationNEL[String, ValidationNEL[String, String]] = ...

我可以通过在结果上使用两个函数调用fold来解压缩它,第一个函数只将NEL作为失败传播,而第二个函数只传播其参数:

def propagateF(result: NonEmptyList[String]): ValidationNEL[String, String] = result.fail
def propagateV(result: ValidationNEL[String, String]) = result

result.fold(propagateF, propagateV)
// result type: ValidationNEL[String, String]

这段代码可以正常运行并返回正确的类型和结果。但是,它似乎不是正确的解决方案,所以我肯定遗漏了什么。我需要做什么来避免最后的可怕折叠?

1个回答

10
你在这里寻找的是单子中的“join”操作。 问题在于,由于错误端携带了不能被Monad保留的Semigroup结构,因此Validation本身并不真正是一个单子。但如果需要的话,你总是可以转到Either单子。这个功能由flatMap提供。
(validateXyz(...) |@| validateAbc(...))(otherFunction).flatMap(x => x)
如果外部有错误,结果将是该错误。如果在成功内部出现错误,则结果将是内部错误。否则结果将是成功。请注意,内部和外部同时存在错误是不可能的。这就是为什么如果您想组合错误,必须使用 Applicative 而不是 Monad 的原因。

绝对惊人。这让我摆脱了真正的困境。 - qu1j0t3
任何涉及scalaz的答案都需要包含导入,以允许其编译。如果没有包含,那么非常令人沮丧。 - noahlz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接