cod*_*oor 5 scala exists findfirst scala-cats cats-effect
假设我有一组规则,其中有一个IO[Boolean]在运行时返回的验证函数。
case class Rule1() {
def validate(): IO[Boolean] = IO.pure(false)
}
case class Rule2() {
def validate(): IO[Boolean] = IO.pure(false)
}
case class Rule3() {
def validate(): IO[Boolean] = IO.pure(true)
}
val rules = List(Rule1(), Rule2(), Rule3())
Run Code Online (Sandbox Code Playgroud)
现在我必须迭代这些规则并查看“这些规则中的任何一个是否有效”,如果不有效则抛出异常!
for {
i <- rules.map(_.validate()).sequence
_ <- if (i.contains(true)) IO.unit else IO.raiseError(new RuntimeException("Failed"))
} yield ()
Run Code Online (Sandbox Code Playgroud)
上面代码片段的问题在于它试图评估所有规则!我真正想要的是在第一次验证时退出true。
不知道如何在 Scala 中使用猫效果来实现这一点。
我声称这existsM是实现你想要的最直接的方法。它的行为与 几乎相同exists,但对于一元谓词:
for {
t <- rules.existsM(_.validate())
_ <- IO.raiseUnless(t)(new RuntimeException("Failed"))
} yield ()
Run Code Online (Sandbox Code Playgroud)
一旦找到第一个,它也会停止搜索true。
这raiseUnless只是一些语法糖,相当于if-else你的问题中的。
如果您查看 IDE 中可用扩展方法的列表,您可以找到findM:
for {
opt <- rules.findM(_.validate())
_ <- opt match {
case Some(_) => IO.unit
case None => IO.raiseError(new RuntimeException("Failed")
}
} yield ()
Run Code Online (Sandbox Code Playgroud)
foldLeft可以使用和手动执行此操作flatMap:
rules.foldLeft(IO.pure(false)) { (valueSoFar, nextValue) =>
valueSoFar.flatMap {
case true => IO.pure(true) // can skip evaluating nextValue
case false => nextValue.validate() // need to find the first true IO yet
}
}.flatMap {
case true => IO.unit
case false => IO.raiseError(new RuntimeException("Failed")
}
Run Code Online (Sandbox Code Playgroud)
前者应该具有额外的优点,即在找到第一个匹配项时不必遍历整个集合,而后者仍将遍历所有项目,即使会在某个时候开始丢弃它们。findM通过tailRecM在内部使用在第一次满足条件时终止迭代来解决这个问题。