Published 2019-07-12.
Time to read: 2 minutes.
Every Scala source file enjoys a huge but invisible set of definitions that are defined in Predef. This lecture discusses provides a gentle introduction to Predef without getting into the weeds.
Scala’s Predef
defines many important implicit conversions and utility methods.
The sample code for this lecture can be found in
courseNotes/.
assert and require
If you are a Java or Node.js programmer, then you’ll be glad to know that the asserts
found in those languages are very similar to Scala’s assert.
Predef.scala
defines the require and assert methods, which are used to enforce conditions during runtime.
require throws IllegalArgumentException if it evaluates the given condition as false.
If used properly, require will only make itself known when some code has called it without proper setup or
with invalid parameters; you might consider this action similar to a HTTP 400 BadRequest response from a web application.
Here is an example.
scala> def percentImprovement(a: Int, b: Int): Double = { require(b!=0) // Prevent divide by zero exception (b.toDouble - a.toDouble) / a.toDouble * 100.0 }
scala> percentImprovement(40, 50) res4: Double = 25.0
scala> percentImprovement(40, 0) java.lang.IllegalArgumentException: requirement failed at scala.Predef$.require(Predef.scala:327) at .percentImprovement(<console>:2) ... 28 elided
In contrast, when assert makes itself known, and if assert was used properly, when the program you are running has reached an
inconsistent state an AssertionError will be thrown; you might consider this action similar to a HTTP 500
InternalServerError response from a web application.
The following code example, which computes factorial and shows good usage of both require and assert, is from
StackOverflow.
def factorial(i: Int): Long = {
import scala.annotation.tailrec
require(i >= 0, "i must be non-negative") // this is for correct input
@tailrec def loop(k: Int, result: Long = 1): Long = {
assert(result == 1 || result >= k) // this is only for verification
if (k > 0) loop(k - 1, result * k) else result
}
loop(i)
}
Removing assert’s Overhead for Production
If your program is compiled with -Xelide-below ASSERTION or with -Xdisable-assertions,
then the compiler will be prevented
from generating byte code for assertions.
If you have a large number of asserts then the use of these switches can significantly reduce the size of your program’s runtime
memory image, and its performance will be also be improved.
Scala programmers should use assert liberally to verify the invariants everywhere in their programs because there is no runtime penalty
in production if one of these compile-time switches is used.
You can rest assured that the pre- and post-conditions for every method and function call is validated in your program.
Unlike assert, require is not elidable via a compiler switch, so it should only be used in libraries.
require is best used to inform the programmer of problems with preconditions for invocations of a library’s methods and functions.
© Copyright 1994-2024 Michael Slinn. All rights reserved.
If you would like to request to use this copyright-protected work in any manner,
please send an email.
This website was made using Jekyll and Mike Slinn’s Jekyll Plugins.