Scala 如果出错则抛出异常,如果成功则不返回任何内容的函数方式

Scala 如果出错则抛出异常,如果成功则不返回任何内容的函数方式,scala,exception,functional-programming,Scala,Exception,Functional Programming,我有一个validateSyntax方法,如果语句的语法无效,它会引发异常,但如果语法正确,则不需要返回任何信息 def validateSyntax(statement: String): Unit = { if (! is_valid(statement)) throw new Exception("syntax is not valid") } 然后我从主界面上称之为: val myStatements = List("foo =! bar", "print(foo + bar)")

我有一个validateSyntax方法,如果语句的语法无效,它会引发异常,但如果语法正确,则不需要返回任何信息

def validateSyntax(statement: String): Unit = {
  if (! is_valid(statement)) throw new Exception("syntax is not valid")
}
然后我从主界面上称之为:

val myStatements = List("foo =! bar", "print(foo + bar)")
myStatements.foreach(stmt => validateSyntax(stmt))
如果其中一条语句无效,我希望我的程序退出并抛出异常

目前,我将validateSyntax的返回类型设置为Unit,但我不确定这是一种功能性的方法


还有更惯用的方法吗?

抛出异常不是处理Scala中错误的惯用方法,原因有很多,例如:

Scala并没有像Java一样的检查异常概念,Scala透视图中的所有异常都是未检查的,所以编译器不会强迫您处理某些异常,因此更容易出错并忘记处理异常; 被视为副作用的例外情况和它们被视为不纯。这并不意味着它们不好或设计不当,而是意味着它们应该以一种特殊的方式处理,因此可能会使代码更加复杂,而没有任何有意义的理由。长时间的讨论超出了这个问题的范围; 错误应该是一个返回值-如果您想强制客户端处理预期的错误,应该选择适当的结果类型; 因此,在您的情况下,您可以选择下一个可能的选项:

用户可以选择[String,Unit]——左边的实例表示一些错误,右边只表示验证通过的事实。 使用经验证的cats-有关更多详细信息,请参阅官方文档: 使用改进的库和普通验证,提供类型级别的证明,以证明某些实体是有效的。有关更多详细信息,请参见项目页面: 如果使用普通,则解决方案如下所示:

  def validateSyntax(statement: String): Either[String, Unit] = {
    if (!is_valid(statement)) Left("syntax is not valid") else Right(())
  }

  val myStatements = List("foo =! bar", "print(foo + bar)")
  val validatedStatements = myStatements.map(statement => statement -> validateSyntax(statement))
  val allErrors = validatedStatements.collect {
    case (statement, Left(error)) => s"Statement: `$statement` is invalid because of $error"
  }

  if(allErrors.nonEmpty) {
    println(s"Statements not valid. All errors:\n - ${allErrors.mkString("\n - ")}")
    sys.exit(-1)
  }
将生成下一个示例输出:

Statements not valid. All errors:
 - Statement: `foo =! bar` is invalid because of syntax is not valid
 - Statement: `print(foo + bar)` is invalid because of syntax is not valid
如果您希望生成更精确的错误消息,并通过一条以上的规则验证每条语句,那么前面提到的cats Validated可能是一种选择。请参见下面的实施示例:

  import cats.data._
  import cats.data.Validated._
  import cats.instances.string._
  import cats.instances.unit._
  import cats.syntax.show._
  import cats.syntax.validated._
  import cats.syntax.foldable._

  type InvalidNec[T] = Invalid[NonEmptyChain[T]]

  type ValidatedStatement = (String, ValidatedNec[String, String])
  type InvalidStatement = (String, InvalidNec[String])

  implicit def listShow[T](implicit show: Show[T]): Show[List[T]] = Show.show(_.map(show.show).mkString("\n"))
  implicit val invalidNecShow: Show[InvalidNec[String]] = Show.show(_.e.mkString_("\n - ", "\n - ", ""))
  implicit val invalidStatementShow: Show[InvalidStatement] = Show.show {
    case (statement, invalid) =>  show"Statement `$statement` is invalid, because of next errors: $invalid"
  }

  def validateRule1(statement: String): ValidatedNec[String, Unit] = {
    val validCondition: Boolean = false // put validation condition here
    if(validCondition) ().validNec else "Statement does not satisfy first rule1".invalidNec
  }

  def validateRule2(statement: String): ValidatedNec[String, Unit] = {
    val validCondition: Boolean = false // put validation condition here
    if(validCondition) ().validNec else "Statement does not satisfy first rule2".invalidNec
  }

  def validateSyntax(statement: String): ValidatedNec[String, String] = {
    (validateRule1(statement) combine validateRule2(statement)).map(_ => statement)
  }

  val myStatements = List("foo =! bar", "print(foo + bar)")
  val validatedStatements: List[ValidatedStatement] = myStatements.map(statement => statement -> validateSyntax(statement))

  val invalidStatements: List[InvalidStatement] = validatedStatements.collect {
    case (statement, invalid: InvalidNec[String]) => statement -> invalid
  }

  if (invalidStatements.nonEmpty) {
    println(show"$invalidStatements")
    sys.exit(-1)
  }
产生下一个输出结果的示例:

Statement `foo =! bar` is invalid, because of next errors: 
 - Statement does not satisfy first rule1
 - Statement does not satisfy first rule2
Statement `print(foo + bar)` is invalid, because of next errors: 
 - Statement does not satisfy first rule1
 - Statement does not satisfy first rule2

希望这有帮助

抛出异常不是处理Scala中错误的惯用方法,原因有很多,例如:

Scala并没有像Java一样的检查异常概念,Scala透视图中的所有异常都是未检查的,所以编译器不会强迫您处理某些异常,因此更容易出错并忘记处理异常; 被视为副作用的例外情况和它们被视为不纯。这并不意味着它们不好或设计不当,而是意味着它们应该以一种特殊的方式处理,因此可能会使代码更加复杂,而没有任何有意义的理由。长时间的讨论超出了这个问题的范围; 错误应该是一个返回值-如果您想强制客户端处理预期的错误,应该选择适当的结果类型; 因此,在您的情况下,您可以选择下一个可能的选项:

用户可以选择[String,Unit]——左边的实例表示一些错误,右边只表示验证通过的事实。 使用经验证的cats-有关更多详细信息,请参阅官方文档: 使用改进的库和普通验证,提供类型级别的证明,以证明某些实体是有效的。有关更多详细信息,请参见项目页面: 如果使用普通,则解决方案如下所示:

  def validateSyntax(statement: String): Either[String, Unit] = {
    if (!is_valid(statement)) Left("syntax is not valid") else Right(())
  }

  val myStatements = List("foo =! bar", "print(foo + bar)")
  val validatedStatements = myStatements.map(statement => statement -> validateSyntax(statement))
  val allErrors = validatedStatements.collect {
    case (statement, Left(error)) => s"Statement: `$statement` is invalid because of $error"
  }

  if(allErrors.nonEmpty) {
    println(s"Statements not valid. All errors:\n - ${allErrors.mkString("\n - ")}")
    sys.exit(-1)
  }
将生成下一个示例输出:

Statements not valid. All errors:
 - Statement: `foo =! bar` is invalid because of syntax is not valid
 - Statement: `print(foo + bar)` is invalid because of syntax is not valid
如果您希望生成更精确的错误消息,并通过一条以上的规则验证每条语句,那么前面提到的cats Validated可能是一种选择。请参见下面的实施示例:

  import cats.data._
  import cats.data.Validated._
  import cats.instances.string._
  import cats.instances.unit._
  import cats.syntax.show._
  import cats.syntax.validated._
  import cats.syntax.foldable._

  type InvalidNec[T] = Invalid[NonEmptyChain[T]]

  type ValidatedStatement = (String, ValidatedNec[String, String])
  type InvalidStatement = (String, InvalidNec[String])

  implicit def listShow[T](implicit show: Show[T]): Show[List[T]] = Show.show(_.map(show.show).mkString("\n"))
  implicit val invalidNecShow: Show[InvalidNec[String]] = Show.show(_.e.mkString_("\n - ", "\n - ", ""))
  implicit val invalidStatementShow: Show[InvalidStatement] = Show.show {
    case (statement, invalid) =>  show"Statement `$statement` is invalid, because of next errors: $invalid"
  }

  def validateRule1(statement: String): ValidatedNec[String, Unit] = {
    val validCondition: Boolean = false // put validation condition here
    if(validCondition) ().validNec else "Statement does not satisfy first rule1".invalidNec
  }

  def validateRule2(statement: String): ValidatedNec[String, Unit] = {
    val validCondition: Boolean = false // put validation condition here
    if(validCondition) ().validNec else "Statement does not satisfy first rule2".invalidNec
  }

  def validateSyntax(statement: String): ValidatedNec[String, String] = {
    (validateRule1(statement) combine validateRule2(statement)).map(_ => statement)
  }

  val myStatements = List("foo =! bar", "print(foo + bar)")
  val validatedStatements: List[ValidatedStatement] = myStatements.map(statement => statement -> validateSyntax(statement))

  val invalidStatements: List[InvalidStatement] = validatedStatements.collect {
    case (statement, invalid: InvalidNec[String]) => statement -> invalid
  }

  if (invalidStatements.nonEmpty) {
    println(show"$invalidStatements")
    sys.exit(-1)
  }
产生下一个输出结果的示例:

Statement `foo =! bar` is invalid, because of next errors: 
 - Statement does not satisfy first rule1
 - Statement does not satisfy first rule2
Statement `print(foo + bar)` is invalid, because of next errors: 
 - Statement does not satisfy first rule1
 - Statement does not satisfy first rule2
希望这有帮助

您可以在左侧使用编码错误,在右侧使用有效结果

您可以在左侧使用编码错误,在右侧使用有效结果


我会这样做:

type Error = String
type Statement = String
type Statements = List[Statement]
type ErrorOr[A] = Either[Error, A]

def validateSyntax(statement: Statement): Option[Error] =
  if (is_valid(statement)) None
  else Some("syntax is not valid")

def ensureAllAreValid(statements: Statements): ErrorOr[Statements] = {
  @annotation.tailrec
  def loop(remaining: Statements, acc: Statements): ErrorOr[Statements] =
    remaining match {
      case statement :: tail =>
        validateSyntax(statement) match {
          case Some(error) =>
            Left(error)

          case None =>
            loop(
              remaining = tail,
              statement :: acc
            )
        }

      case Nil =>
        // If the order is not important,
        // Remove the reverse which is somewhat costly.
        Right(acc.reverse)
    }

    loop(remaining = statements, acc = List.empty)
  }
}

def main(): Unit = {
  val myStatements = List("foo =! bar", "print(foo + bar)")
  ensureAllAreValid(myStatements) match {
    case Left(error) =>
      println(error)
      system.exit(-1)

    case Right(statements) =>
      ???
  }
}

我会这样做:

type Error = String
type Statement = String
type Statements = List[Statement]
type ErrorOr[A] = Either[Error, A]

def validateSyntax(statement: Statement): Option[Error] =
  if (is_valid(statement)) None
  else Some("syntax is not valid")

def ensureAllAreValid(statements: Statements): ErrorOr[Statements] = {
  @annotation.tailrec
  def loop(remaining: Statements, acc: Statements): ErrorOr[Statements] =
    remaining match {
      case statement :: tail =>
        validateSyntax(statement) match {
          case Some(error) =>
            Left(error)

          case None =>
            loop(
              remaining = tail,
              statement :: acc
            )
        }

      case Nil =>
        // If the order is not important,
        // Remove the reverse which is somewhat costly.
        Right(acc.reverse)
    }

    loop(remaining = statements, acc = List.empty)
  }
}

def main(): Unit = {
  val myStatements = List("foo =! bar", "print(foo + bar)")
  ensureAllAreValid(myStatements) match {
    case Left(error) =>
      println(error)
      system.exit(-1)

    case Right(statements) =>
      ???
  }
}

你能解释一下否决票吗,这样我就不会重蹈覆辙了?我没有否决票。但你可能想检查一下。为了得到好的反馈,你需要展示你的代码。我想我的问题只涉及函数的签名,而不需要知道函数的主体。我用最少的代码编辑了我的问题,说明了我的案例引发异常是一种副作用,因此您的代码不起作用。您可以使用“任意”或“尝试”或“甚至”选项重构此文件,但我们需要了解更多有关您的用例的信息。请参阅以了解您必须在功能上处理此文件的选项之间的所有差异。请您解释否决票,以便我不再重复错误。我没有否决票。但你可能想检查一下。为了得到好的反馈,你需要展示你的代码。我想我的问题只涉及函数的签名,而不需要知道函数的主体。我用最少的代码编辑了我的问题,说明了m
Casey抛出异常是一种副作用,因此您的代码不起作用。您可以使用“或”或“尝试”或“甚至”选项重构此文件,但我们需要更多地了解您的用例。请参阅以了解您必须在功能上处理此文件的选项之间的所有差异。