Functional assertion in Scala

混江龙づ霸主 提交于 2019-12-05 01:32:34

问题


Is there built-in support for assertions that return a result?

It is very non-functional to do this:

  def addPositive(a: Int, b: Int) = {
    assert(a > 0 && b > 0)
    a + b
  }

I would rather do something similar to:

  def addPositive(a: Int, b: Int) = 
    assert(a > 0 && b > 0)(a + b)

In this way I can avoid the imperative aspect of asserts. (the latter does not compile) Is anything similar available?


回答1:


Functional programming treats functions as pure mathematical functions (ideally). So what's the mathematics' way of saying a function doesn't work for some parameters and must blow up ?

Partial Functions

It turns out that Scala has a pretty good support for this concept: PartialFunction. This is how you would rewrite your code using partial functions:

val addPositive: PartialFunction[(Int, Int), Int] = {
  case (a, b) if a > 0 && b > 0 => a + b
}

This has several benefits:

If you call it with the wrong parameters it will throw a MatchError exception.

addPositive(-1, 2) => Exception in thread "main" scala.MatchError: (-1,2) (of class scala.Tuple2$mcII$sp)

You can actually sample the function's domain to check if some values are well suited as arguments for the function:

addPositive.isDefinedAt(-1, 2) => false

If you would like to apply the function to some arguments and get either a result, or some value indicating failure, you can lift it to return Option

addPositive.lift(-1, 2) => None
addPositive.lift(1, 2) => Some(12)

You can compose it with other functions to provide fallback in case of invalid arguments:

val fallback: PartialFunction[(Int, Int), Int] = { case (a, b) => Int.MinValue }
val f = addPositive orElse fallback

f(-1, 2) => -2147483648

Or to treat errors in a custom way:

val raiseError: PartialFunction[(Int, Int), Int] = {
  case (a, b) => throw new IllegalArgumentException(s"Cannot apply addPositive to arguments $a and $b")
}
val g = addPositive orElse raiseError

g(-1, 2) => Exception in thread "main" java.lang.IllegalArgumentException: Cannot apply addPositive to arguments -1 and 2

It works well with the standard lib: see Seq.collect and Seq.collectFirst.

Also PartialFunction is a normal unary function, so you inherit all the function operation as well.

Here is an article explaining very elegantly partial functions in Scala:

Scala partial functions (without a PhD)




回答2:


You could roll out your own implementation:

def assert[T](cond: =>Boolean)(expr: =>T): T = {
  assert(cond)
  expr
}

You could also use the option type to avoid exceptions, but that means that you'd later have to pattern match on the result:

def addPositive(a: Int, b: Int): Option[int] =
  if (a > 0 && b > 0) Some(a + b)
  else None

This can be refactored in the similar manner as the assert variant above.




回答3:


There is the require function for preconditions, which pretty much accomplishes what you want. You can do

def addPositive(a: Int, b: Int) = 
   require (a > 0 && b > 0, a + b)

There are part of scala.Predef, so they are always included. The parameter (a+b in the example) is passed by name, so it will only be executed if the condition isn't true.

requires is always activated, if you want to be able to deactivate it you can make it with assert too, like :

assert(a > 0 && b > 0, a + b)


来源:https://stackoverflow.com/questions/15970145/functional-assertion-in-scala

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!