MUTATIVE PROGRAMMING

The functions we have designed returns the same value for same argument values, no matter what. But many real world programs need to remember values from their past uses. In other worlds the computational process has a "state", and the return value of some functions may depend on the state of the process as well as on their inputs.

The programs we have written so far did not leave any traces of their work in the state of the process. Once we start writing programs which instead leave such traces, we enter the realm of "mutative programming". Mutative functions do not only compute an output value, but also cause a permanent change in the state of the process, which effect how the same -or another- function will work when called afterwards. In other words mutative functions do not only compute a result value but also cause "side effects" in the state of the world/process.

Example: traffic lights

Traffic lights change their color when a timer triggers them. However the next color of the traffic light depends on what was its color before. As a consequence we are presented with a stateful problem.

Assignment to variables

The way to represent -and mutate- the state of the process is to store the state in variables. A variable is a storage box in the memory to store a value. One can change the value in the box when desired, thus the name "variable".

We have actually declared such variables, but not yet "varied" them. The set! command in Racket is used to update/vary such variables. It is a very different command than we have learned before because it does not return anything! Instead it changes a variable, i.e. its purpose is not to produce an output, but to change the process state:

    > (define x 0)
    > x
    0
    > (set! x 1)
    (void)
    > x
    1
    > (set! x (+ x 1))
    (void)
    > x
    2
    > 

Note that set! command returns void, that is it returns nothing.

Solution: Traffic lights

The mutative solution to traffic lights problem is below.

    (define light "red")

    (define (next)
      (cond
        ((string=? light "red") (set! light "yellow"))
        ((string=? light "yellow") (set! light "green"))
        (else (set! light "red"))))

Now, run the program then use the interpreter screen to execute next function several times:

    > (next)
    (void)
    > light
    "yellow"
    > next
    next
    > (next)
    (void)
    > light
    "green"
    > 

Note that the function we have written has not input parameters. Its input is the value of a variable, ie. its input is the current state of the process. It also has no return value, it does its job by changing/varying a variable.

Sequencing commands

We often encounter a situation in which we need to both change a variable and return a value from a function. This means our function needs to do two independent actions. This is a situation we have never seen before. All functions we have written, simple or complex, contained a single expression: arithmetic or conditional, or both, or several of both nested inside. To manage with this new situation we will use another special command (begin ...). This command can encapsulate several commands in a sequence, and returns the outcome of the last command as its return value. We will replace the next function as follows:

    (define light "red")

    (define (next)
      (begin
        (cond
          ((string=? light "red") (set! light "yellow"))
          ((string=? light "yellow") (set! light "green"))
          (else (set! light "red")))
        light))

This version of the next function first executes a conditional statement, then the light as the last entity in (begin ...) sequence is returned as its return value. Now try executing the next command, it returns the most recent value of light variable instead of void:

    > (next)
    "yellow"
    > (next)
    "green"
    > (next)
    "red"
    > (next)
    "yellow"
    > 

Example problem: Managing address book

    (define-struct Address (name phone))
    (define addressBook empty)

    (define (addPhone name phone)
      (set! addressBook (cons 
                         (make-Address name phone)
                         addressBook)))

Now update the address book using interactive pane:

    > addressBook
    empty
    > (addPhone "ali" "111")
    (void)
    > addressBook
    (list (make-Address "ali" "111"))
    >

Exercise: find phone number

Write a function to find a phone number of person whose name is given, in the address book (note you don't need to use set! or begin or any of the new techniques to do that).

Exercise: delete phone number

Write a function to remove a person whose name is given, from the address book.

Exercise: delete phone number, return success status

Write a function to remove a person whose name is given, from the address book. Make sure your function returns true if the person is found and removed, and returns false if the person can not be found.