Software DevelopmentLearn How Strictness And Laziness Are Used To Create Better Programs In...

Learn How Strictness And Laziness Are Used To Create Better Programs In Scala

In computer programming an evaluation strategy sets the rules that have to be applied when an expression is to be evaluated. Consider an expression of the form (1-x) (1+x). If a case arises where the value of x is small enough to be considered close to zero a better way of evaluating the expression becomes 1-2x-x*x. derivation of the expression is not very important. The key concept being communicated is that when in your programs expression evaluation is important knowledge of how the compiler handles evaluation is important.

In a functional style of programming evaluation strategies inform the order of argument evaluation, point at which evaluation happens and the form of the evaluation. Purely functional programming style dictates evaluation should not result in side effects so the process of evaluation consists of expressions being rewritten. When function arguments evaluation happens in a left to right manner this is referred to as an applicative order. This kind of evaluation is the same that is applied when you curry a function.

When evaluating a function you can use strict or non strict evaluation. When you use strict evaluation a function is only evaluated after its arguments have been evaluated. The applicative order of argument evaluation discussed previously is a form of strict evaluation but you are not limited to using applicative order when doing strict evaluation. In non strict evaluation argument evaluation only happens when arguments are needed. In lazy evaluation argument evaluation only happens when they are required which makes it a form of non strict evaluation. All functions defined in Scala are strictly evaluated unless you explicitly state they are not. When an evaluation enters into a state where it runs continuously without returning a value or throwing an error such an evaluation is referred to as not terminating. An evaluation that fails to terminate under strict evaluation can still terminate under lazy evaluation.

To demonstrate how strict evaluation happens in Scala consider a function defined below that converts miles to kilometres.
def milesTokm(miles: Double) : Double = miles * 1.60934
When you invoke this function to convert 34.5 miles to kilometers it will give the correct value. However when you call the function in this way milesTokm(sys.error(“failure”)) an exception will be thrown.

In Scala you are able to write non strict functions by specifying arguments that are not evaluated. This is only done to arguments that you intend to pass without evaluation. To specify this you use => before the type definition of argument. To define our miles to kilometers function as non strict the syntax shown below is used
def milesTokm(miles: => Double) : Double = miles * 1.60934
Non strict evaluation can be categorized into evaluation by name or evaluation by name and evaluation by need. Evaluation by name happens when function argument evaluation happens every time arguments are referenced. Evaluation by need happens when argument evaluation is done only once and its result is cached. Evaluation by need is commonly referred to as lazy evaluation.

Lazy evaluation offers several advantages that every programmer needs to consider. First it is possible to separate program description from evaluation. The advantage here is that you are able to define a program and only partially evaluate some parts leaving others. Another advantage is that we are able to modularize our code. By breaking our programs into smaller parts debugging becomes easier.

The lazy modifier that is placed before val ensures the value is evaluated once and referenced subsequently.
To explain how laziness helps us create better programs look at use of List in code below
List(1,2,3,4). map (_ + 10).filter (_ % 2 == 0).map (_ * 3)
In this code we have chained three operations. The first map will add 10 to each of the elements in the list. The filter operation will help us remove any elements from the list that are not divisible by 2. In the second map operation we multiply the filtered elements by 3. The final result is a list containing the elements 36 and 42. Every operation creates a temporary result that is used by the next operation. A better way to approach this kind of problem is to avoid creating temporary results and lazy execution comes to our help. Consider the code shown below where we are rewriting the chained program using a steam. A stream is an important pillar in functional programming style.

trait Stream[+A] {
def uncons: Option[(A, Stream[A])]
def isEmpty: Boolean = uncons.isEmpty
object Stream {
def empty[A]: Stream[A] =
new Stream[A] { def uncons = None }
def cons[A](start: => A, stop: => Stream[A]): Stream[A] =
new Stream[A] {
lazy val uncons = Some((start, stop))
def apply[A](as: A*): Stream[A] =
if (as.isEmpty) empty
else cons(as.head, apply(as.tail: _*))

In the above code we have used singly linked lists which are immutable. For a review of these please refer to functional data structures tutorial. The function cons has been declared using non strict style. Start and stop will only be evaluated when they are needed.
This tutorial introduced you to ways in which expressions are evaluated. We looked at the two approaches of evaluating an expression i.e strict and non strict evaluation. Examples of how each is implemented in Scala were given. We looked at an example that does not use lazy evaluation and the inefficiencies that result from creating copies of data. The example was implemented using a Stream that does not copy data.


Please enter your comment!
Please enter your name here

Exclusive content

- Advertisement -

Latest article


More article

- Advertisement -