在Scala中链接验证

Ral*_*lph 1 monads scala either

我有一个case class包含命令行配置信息的Scala :

case class Config(emailAddress: Option[String],
                  firstName: Option[String]
                  lastName: Option[String]
                  password: Option[String])
Run Code Online (Sandbox Code Playgroud)

我正在编写一个验证函数来检查每个值是否为Some:

def validateConfig(config: Config): Try[Config] = {
  if (config.emailAddress.isEmpty) {
    Failure(new IllegalArgumentException("Email Address")
  } else if (config.firstName.isEmpty) {
    Failure(new IllegalArgumentException("First Name")
  } else if (config.lastName.isEmpty) {
    Failure(new IllegalArgumentException("Last Name")
  } else if (config.password.isEmpty) {
    Failure(new IllegalArgumentException("Password")
  } else {
    Success(config)
  }
}
Run Code Online (Sandbox Code Playgroud)

但如果我理解来自Haskell的monad,似乎我应该能够将验证链接在一起(伪语法):

def validateConfig(config: Config): Try[Config] = {
  config.emailAddress.map(Success(config)).
    getOrElse(Failure(new IllegalArgumentException("Email Address")) >>
  config.firstName.map(Success(config)).
    getOrElse(Failure(new IllegalArgumentException("First Name")) >>
  config.lastName.map(Success(config)).
    getOrElse(Failure(new IllegalArgumentException("Last Name")) >>
  config.password.map(Success(config)).
    getOrElse(Failure(new IllegalArgumentException("Password"))
}
Run Code Online (Sandbox Code Playgroud)

如果任何config.XXX表达式返回Failure,则整个thing(validateConfig)应该失败,否则Success(config)应该返回.

有没有办法用这个Try,或者其他一些类?

Tra*_*own 8

将每个转换Option为正确投影的实例非常简单Either:

def validateConfig(config: Config): Either[String, Config] = for {
  _ <- config.emailAddress.toRight("Email Address").right
  _ <- config.firstName.toRight("First Name").right
  _ <- config.lastName.toRight("Last Name").right
  _ <- config.password.toRight("Password").right
} yield config
Run Code Online (Sandbox Code Playgroud)

Either 在标准库的术语中,它不是monad,但它的正确投影是,并且将在失败的情况下提供您想要的行为.

如果你最喜欢a Try,你可以转换结果Either:

import scala.util._

val validate: Config => Try[Config] = (validateConfig _) andThen (
  _.fold(msg => Failure(new IllegalArgumentException(msg)), Success(_))
)
Run Code Online (Sandbox Code Playgroud)

我希望标准库提供了一种更好的方法来进行这种转换,但事实并非如此.