-
Notifications
You must be signed in to change notification settings - Fork 11
Functions and State
So far, we have worked with immutable data structures i.e. functional programming and our programs have been side effect free.
Therefore, the concept of "time" wasn't important: for all programs that terminate, any sequence of actions would have given the same results. This was also reflected in the substitution model of computation.
Programs can be evaluated by re-writing:
// Given the function definition
def f(x1, ..., xn) = B;
// the function call
f(v1, ..., vn)
// can be written as
[v1/x1, ..., vn/xn] B // v/x means v is replaced by x in the functionbody B
Example
Say you have the following two functions iterate and square:
def iterate(n: Int, f: Int => Int, x: Int) = {
if (n == 0) x else iterate(n-1, f, f(x))
}
def square(x: Int) = x * x
// then the call iterate(1, square, 3) gets rewritten as follows:
→ if (1 == 0) 3 else iterate(1-1, square, square(3))
→ iterate(0, square, square(3))
→ iterate(0, square, 3 * 3)
→ iterate(0, square, 9)
→ if (0 == 0) 9 else iterate(0-1, square, square(9)) // recursion
→ 9 // returned
Observation
Rewriting can be done anywhere in a term, and all rewritings which terminate lead to the same solution. This is an important result of the λ-calculus, the theory behind functional programming.
So in the above example, we could have started rewriting in a different order, still would give the same result.
→ if (1 == 0) 3 else iterate(1-1, square, square(3))
→ if (1 == 0) 3 else iterate(1-1, square, 3 * 3)
→ ...
→ 9 // returned
All these above observation hold in the world of pure functional programming.
One normally describes the world as a set of objects, some of which have state that changes over the course of time. An object has a state if its behavior is influenced by its history.
Example: a bank account has a state, because the answer to the question "can I withdraw 100 USD?" may vary over the course of the lifetime of the account.
In scala, this is where keyword var
(variable) comes into picture which can be used instead of val
(final).
Every form of mutable state is constructed from variables.
A variable definition is written like a value definition, but with the keyword var
in place of val
:
var x: String = "abc"
var count = 111
Just like a value definition, a variable definition associates a value with a name. However, in the case of variable definitions, this association can be changed later through an assignment:
x = ”hi”
count = count + 1
In practice, objects with state are usually represented by objects that have some variable members.
Example: Here is a class modeling a bank account. class BankAccount { private var balance = 0 // variable def deposit(amount: Int): Unit = { if (amount > 0) balance = balance + amount } def withdraw(amount: Int): Int = if (0 < amount && amount <= balance) { balance = balance - amount balance } else throw new Error(”insufficient funds”) }
Note that balance is private in the BankAccount class, it therefore cannot be accessed from outside the class.
To create bank accounts, we use the usual notation for object creation:
```scala
val account = new BankAccount
Consider the following class:
class BankAccountProxy(ba: BankAccount) {
def deposit(amount: Int): Unit = ba.deposit(amount)
def withdraw(amount: Int): Int = ba.withdraw(amount)
}
This does not have any var
, but it is still stateful since it depends in the history of the BankAccount.
Here we are transitioning from pure functional programming to combination of functional and imperative programming