Mike Slinn

Predef

— Draft —

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/src/main/scala/PredefFun.scala.

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 REPL
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.

Scala code
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.


* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.