在 for-comp 中使用 EitherT-s 进行数据验证

Data validation with EitherT-s inside for-comp

考虑一下我要解决的问题的简化版本。这是我当前的实现。

case class Foo()

def addFoo(json: String): EitherT[Future, Exception, Long] = {
  def parse(json: String): EitherT[Future, Exception, Foo] = ???

  def validate(foo: Foo): EitherT[Future, Exception, Foo] = ???

  def save(foo: Foo): EitherT[Future, Exception, Long] = ???

  for {
    foo <- parse(json)
    _ <- validate(foo)
    id <- save(foo)
  } yield id
}

这感觉不对,因为尽管 validate returns ExceptionFooEitherT,它只是执行验证和 returns 异常或未更改的 foo 值。所以,我们从不对 Either 的正确部分感兴趣,也从不使用它。有没有更好地对应 "validation" 语义的方法?

如果您不关心正确的值,您可以使用 Unit 而不是 Foo,例如:

def validate(foo: Foo): EitherT[Future, Exception, Unit] = ???

您可以更改 validate 的类型签名以增加更多类型安全性:

def validate(foo: Foo): EitherT[Future, Exception, ValidFoo]

其中 ValidFoo 看起来像这样:

object ValidFoo {
  def believeMeItsValid(raw: Foo): ValidFoo = new ValidFoo(raw)
}

class ValidFoo private (val foo: Foo)

save 将只接受经过验证的输入:

def save(foo: ValidFoo): EitherT[Future, Exception, Long]

这将强制您在保存输入之前验证输入并有助于防止 布尔盲目性