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?
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With