Scala Tutorials Part #29 - Design By Contract
Originally Posted On : 27 Dec 2017
Design By Contract
This is part 29 of the Scala tutorial series. Check here for the full series.
Index
Introduction
Design by contract is a programming approach where there are preconditions and post-conditions to a code block and if these conditions are not satisfied an exception is thrown to the caller. This was originally designed by Betrand Mayer who was the creator of the Eiffel programming language.
Picture courtesy - Wikipedia
Some languages do not have first class support, some have support through external libraries. Scala has some support through the language itself and that is what this post is all about.
High Level Overview
Scala has four methods assert
, assume
, require
and ensuring
which are present in the Predef.scala
package and they are available by default and no library import is required.
Of these assert
, assume
and require
are preconditions and ensuring
is a post-condition. Preconditions are meant to be guards before entering a code block while a post condition happens happen after execution i.e like a do while
construct. Code examples below will make them clear.
Assert and Assume
The assert
keyword would be familiar if you have done unit testing before. It takes in a boolean condition as a parameter and checks if it is true else throws an exception.
This will throw an exception.
assume
is very similar.
This also throws an exception.
In fact, it throws the same exception java.lang.AssertionError
only with the name changed such as assertion failed or assumption failed depending on what we use. Even the source code is almost identical.
So why the almost identical ones? The answer lies in how they should be used.
assert
is kind of like attempting to prove something. It can be regarded as a predicate in a mathematical sense i.e which needs to be checked by the static checker. assume
on the other hand is like an axiom where the code can rely upon the static checker to throw an error if its not true. So they are in some sense just syntactic sugar.
Require and Ensuring
Methods require
and ensure
often work in tandem. Let’s look at an example.
This can fail in two ways i.e the passed number is not an even number and also when the squared result is greater than the limit that is passed in.
Calling it with the below parameters,
Causes a java.lang.IllegalArgumentException
. This blames the caller for giving a value that is termed illegal as per the contract. Notice how this is different from the java.lang.AssertionError
. One is an exception and another is an error. I have already explained this in a previous blog - exception handling in Scala
If we try to break the ensuring condition,
Turns out that ensuring
calls assert
internally. One more thing to note is that ensuring
cannot access variables inside of the method block. Its scope is limited to the method parameters.
Conclusion
We have briefly seen what these methods can do. These can be combined to form complex design patterns such as Design by Contract, Defensive Programming etc., An important thing to note is that assert
and assume
can be removed using a compiler flag -Xdisable-assertions
during compile time. This is useful when you don’t want such statements to enter into production.
There are advanced patterns possible. Notice that all of these functions in Predef.scala
have another signature where functions can be passed in i.e they are higher order functions.
In the Scala world these patterns are not extensively used. This is because they throw an exception during run time. There are better design patterns where we can encode such conditions during compile time. We have already seen the Option type which is one way of encoding state within a container which is compile time. There are other ways such as Either
, Try
etc., We will take a look at those in later tutorials. With that said, there are situations where you might want to use design by contract, such as dealing with situations where null
is quite possible(interfacing with java code) and then throwing an exception.