Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala中的链接验证_Scala_Monads_Either - Fatal编程技术网

Scala中的链接验证

Scala中的链接验证,scala,monads,either,Scala,Monads,Either,我有一个Scalacase类,包含命令行配置信息: case class Config(emailAddress: Option[String], firstName: Option[String] lastName: Option[String] password: Option[String]) 我正在编写一个验证函数,用于检查每个值是否为某个Some: def validateCo

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

case class Config(emailAddress: Option[String],
                  firstName: Option[String]
                  lastName: Option[String]
                  password: Option[String])
我正在编写一个验证函数,用于检查每个值是否为某个
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)
  }
}
但是,如果我理解Haskell的Monad,那么我似乎应该能够将验证链接在一起(伪语法):

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


有什么方法可以用
Try
,或者其他类来实现吗?

这是一个case类,为什么不用模式匹配来实现呢

def validateConfig(config: Config): Try[Config] = config match {
  case Config(None, _, _, _) => Failure(new IllegalArgumentException("Email Address")
  case Config(_, None, _, _) => Failure(new IllegalArgumentException("First Name")
  case Config(_, _, None, _) => Failure(new IllegalArgumentException("Last Name")
  case Config(_, _, _, None) => Failure(new IllegalArgumentException("Password")
  case _ => Success(config)
}
在你的简单例子中,我的首要任务是忘记单子和链锁,只要去掉那种讨厌的
if…else
味道就行了

然而,虽然case类对于短列表非常有效,但是对于大量配置选项,这会变得很乏味,并且错误的风险也会增加。在这种情况下,我会考虑这样的事情:

def optionMap: OptionMap = ...
def validate: Either[List[String], OptionMap] = {
  val badOptions = optionMap collect { case (s, None) => s }
  if (badOptions.size > 0)
    Left(badOptions)
  else
    Right(optionMap)
}
  • 添加一个方法,使用选项名作为键,返回配置选项的键->值映射
  • 让验证方法检查映射中是否有任何值为
    None
  • 如果没有这样的值,则返回success
  • 如果至少有一个值匹配,则返回该值名称并返回错误
  • 所以假设某处是有定义的

    type OptionMap = scala.collection.immutable.Map[String, Option[Any]]
    
    Config
    类有如下方法:

    def optionMap: OptionMap = ...
    
    def validate: Either[List[String], OptionMap] = {
      val badOptions = optionMap collect { case (s, None) => s }
      if (badOptions.size > 0)
        Left(badOptions)
      else
        Right(optionMap)
    }
    
    然后我会这样写
    Config.validate

    def optionMap: OptionMap = ...
    
    def validate: Either[List[String], OptionMap] = {
      val badOptions = optionMap collect { case (s, None) => s }
      if (badOptions.size > 0)
        Left(badOptions)
      else
        Right(optionMap)
    }
    
    因此,现在
    Config.validate
    返回一个
    Left
    包含所有坏选项的名称,或者返回一个
    Right
    包含选项及其值的完整映射。坦率地说,您在
    右侧的
    中输入的内容可能并不重要

    现在,任何想要验证
    Config
    的东西都只需调用
    Config.validate
    并检查结果。如果它是一个
    ,它可以抛出一个
    IllegalArgumentException
    ,其中包含一个或多个坏选项的名称。如果它是
    正确的
    ,它可以做它想做的任何事情,知道
    配置
    是有效的

    因此,我们可以将您的
    validateConfig
    函数重写为

    def validateConfig(config: Config): Try[Config] = config.validate match {
      case Left(l) => Failure(new IllegalArgumentException(l.toString))
      case _ => Success(config)
    }
    
    你能看到它的功能和面向对象性有多强吗

    • 没有命令链
      if…else
    • Config
      对象验证自身
    • Config
      对象无效的后果留给较大的程序处理
    不过,我认为一个真实的例子会更加复杂。您通过说“它是否包含
    Option[String]
    None
    ?”来验证选项,但不检查字符串本身的有效性。实际上,我认为您的
    Config
    类应该包含一个选项映射,其中名称映射到值和验证字符串的匿名函数。我可以描述如何扩展上述逻辑以使用该模型,但我想我将把它留给您作为练习。我将给您一个提示:您可能不仅要返回失败选项的列表,还要返回每种情况下失败的原因


    哦,顺便说一下。。。我希望以上任何一点都不意味着我认为您应该将选项及其值作为
    optionMap
    存储在对象内部。我认为这样检索它们是有用的,但我永远不会鼓励这样暴露实际的内部表示;)

    这是一个case类,那么为什么不使用模式匹配呢

    def validateConfig(config: Config): Try[Config] = config match {
      case Config(None, _, _, _) => Failure(new IllegalArgumentException("Email Address")
      case Config(_, None, _, _) => Failure(new IllegalArgumentException("First Name")
      case Config(_, _, None, _) => Failure(new IllegalArgumentException("Last Name")
      case Config(_, _, _, None) => Failure(new IllegalArgumentException("Password")
      case _ => Success(config)
    }
    
    在你的简单例子中,我的首要任务是忘记单子和链锁,只要去掉那种讨厌的
    if…else
    味道就行了

    然而,虽然case类对于短列表非常有效,但是对于大量配置选项,这会变得很乏味,并且错误的风险也会增加。在这种情况下,我会考虑这样的事情:

    def optionMap: OptionMap = ...
    
    def validate: Either[List[String], OptionMap] = {
      val badOptions = optionMap collect { case (s, None) => s }
      if (badOptions.size > 0)
        Left(badOptions)
      else
        Right(optionMap)
    }
    
  • 添加一个方法,使用选项名作为键,返回配置选项的键->值映射
  • 让验证方法检查映射中是否有任何值为
    None
  • 如果没有这样的值,则返回success
  • 如果至少有一个值匹配,则返回该值名称并返回错误
  • 所以假设某处是有定义的

    type OptionMap = scala.collection.immutable.Map[String, Option[Any]]
    
    Config
    类有如下方法:

    def optionMap: OptionMap = ...
    
    def validate: Either[List[String], OptionMap] = {
      val badOptions = optionMap collect { case (s, None) => s }
      if (badOptions.size > 0)
        Left(badOptions)
      else
        Right(optionMap)
    }
    
    然后我会这样写
    Config.validate

    def optionMap: OptionMap = ...
    
    def validate: Either[List[String], OptionMap] = {
      val badOptions = optionMap collect { case (s, None) => s }
      if (badOptions.size > 0)
        Left(badOptions)
      else
        Right(optionMap)
    }
    
    因此,现在
    Config.validate
    返回一个
    Left
    包含所有坏选项的名称,或者返回一个
    Right
    包含选项及其值的完整映射。坦率地说,您在
    右侧的
    中输入的内容可能并不重要

    现在,任何想要验证
    Config
    的东西都只需调用
    Config.validate
    并检查结果。如果它是一个
    ,它可以抛出一个
    IllegalArgumentException
    ,其中包含一个或多个坏选项的名称。如果它是
    正确的
    ,它可以做它想做的任何事情,知道
    配置
    是有效的

    因此,我们可以将您的
    validateConfig
    函数重写为

    def validateConfig(config: Config): Try[Config] = config.validate match {
      case Left(l) => Failure(new IllegalArgumentException(l.toString))
      case _ => Success(config)
    }
    
    你能看到它的功能和面向对象性有多强吗

    • 没有命令链
      if…else
    • Config
      对象验证自身
    • Config
      对象无效的后果留给较大的程序处理
    不过,我认为一个真实的例子会更加复杂。您通过说“它是否包含
    Option[String]
    None
    ?”来验证选项,但不检查字符串本身的有效性。实际上,我认为您的
    Config
    类应该包含一个选项映射,其中名称映射到值和验证字符串的匿名函数。我可以描述如何扩展上述逻辑以使用该模式