首页 > 解决方案 > Scala - 如何使用来自猫的 Validated monad 验证列表中的每个字符串?

问题描述

我有一个列表Strings,我需要验证它们是否与某些模式匹配:

def validate(strings: List[String]): Either[WrongStringError, List[String]] = {
    strings.foreach(s => checkString(s))
    Right(strings)
  }

private def checkString(s: String): Validated[WrongStringError, String] = {
  val pattern = .... //some regex
  Either.catchNonFatal(s.matches(pattern))
    .left
    .map(_ => WrongStringError(s"Given string: ${s} is not valid."))
  }.toValidated

但是,我想要实现的是将所有错误汇总在一起。例如,如果 5 个字符串中有 3 个不正确,我想将错误返回给用户。我想过使用Validatedmonad fromcats但我没有得到结果。如何验证字符串列表并返回不正确的错误列表?

标签: scalascala-cats

解决方案


我想过使用来自猫的 Validated monad,但我没有得到结果。

Validated 好吧,诸如(或任何 FP 数据结构)之类的东西的想法是将值组合在一起。

但是,您在这里没有编写任何东西,您只是在调用 a foreach,因此如果发生故障,您甚至不会收到错误,您将始终收到 a Right

此外,如果要收集所有错误,则需要使用某种(非空)容器,例如NonEmptyChain.

最后,我有一个值的集合和将这些值转换为有效值的函数的这种常见模式,我想将所有这些效果收集/组合成一个列表的单个效果称为traverse.

加分点,这种使用同构效果类型的常见模式看起来与我的正常效果完全一样,只是为了提供不同的(并行)行为,它只是一个Applicative而不是一个,如此普遍,以至于引入了类型类和组合器(例如以避免样板。MonadParallelparTraverse

无论如何,这就是代码的样子:

import cats.data.NonEmptyChain
import cats.syntax.all._

type Error = String

def validate(strings: List[String]): Either[NonEmptyChain[Error], List[String]] =
  strings.parTraverse { s =>
    checkString(s).toEitherNec
  }

def checkString(s: String): Either[Error, String] = {
  val pattern = ""
  Either.cond(
    test = s.matches(pattern),
    right = s,
    left = "Given string: ${s} is not valid."
  )
}

顺便说一句,我建议您更多地学习 FP。
我可能会向您推荐 Rob Norris 的演讲“Functional Programming with Effects”和(免费)书籍“Scala with Cats”。


推荐阅读