Skip to content

Commit f25cf97

Browse files
committed
Add ImperativeProgramming section
1 parent 543fa5c commit f25cf97

File tree

4 files changed

+360
-16
lines changed

4 files changed

+360
-16
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package scalatutorial.aux
2+
3+
class BankAccount {
4+
5+
private var balance = 0
6+
7+
def deposit(amount: Int): Unit = {
8+
if (amount > 0) balance = balance + amount
9+
}
10+
11+
def withdraw(amount: Int): Int =
12+
if (0 < amount && amount <= balance) {
13+
balance = balance - amount
14+
balance
15+
} else throw new Error("insufficient funds")
16+
}
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,333 @@
11
package scalatutorial.sections
22

3+
import scalatutorial.aux.BankAccount
4+
35
/** @param name imperative_programming */
46
object ImperativeProgramming extends ScalaTutorialSection {
57

8+
/**
9+
* Until now, our programs have been side-effect free.
10+
*
11+
* Therefore, the concept of ''time'' wasn't important.
12+
*
13+
* For all programs that terminate, any sequence of actions would have given the same results.
14+
*
15+
* This was also reflected in the substitution model of computation.
16+
*
17+
* = Reminder: Substitution Model =
18+
*
19+
* Programs can be evaluated by ''rewriting'':
20+
* - a name is evaluated by replacing it with the right-hand side of its definition,
21+
* - function application is evaluated by replacing it with the function’s right-hand
22+
* side, and, at the same time, by replacing the formal parameters by the actual
23+
* arguments.
24+
*
25+
* Say you have the following two functions `iterate` and `square`:
26+
*
27+
* {{{
28+
* def iterate(n: Int, f: Int => Int, x: Int) =
29+
* if (n == 0) x else iterate(n-1, f, f(x))
30+
* def square(x: Int) = x * x
31+
* }}}
32+
*
33+
* Then the call `iterate(1, square, 3)` gets rewritten as follows:
34+
*
35+
* {{{
36+
* iterate(1, square, 3)
37+
* if (1 == 0) 3 else iterate(1-1, square, square(3))
38+
* iterate(0, square, square(3))
39+
* iterate(0, square, 3 * 3)
40+
* iterate(0, square, 9)
41+
* if (0 == 0) 9 else iterate(0-1, square, square(9))
42+
* 9
43+
* }}}
44+
*
45+
* Rewriting can be done anywhere in a term, and all rewritings which
46+
* terminate lead to the same solution.
47+
*
48+
* This is an important result of the λ-calculus, the theory
49+
* behind functional programming.
50+
*
51+
* For instance, these two rewriting will eventually lead to the same result:
52+
*
53+
* {{{
54+
* if (1 == 0) 3 else iterate(1 - 1, square, square(3))
55+
* iterate(0, square, square(3))
56+
*
57+
* // OR
58+
* if (1 == 0) 3 else iterate(1 - 1, square, square(3))
59+
* if (1 == 0) 3 else iterate(1 - 1, square, 3 * 3)
60+
* }}}
61+
*
62+
* = Stateful Objects =
63+
*
64+
* One normally describes the world as a set of objects, some of which
65+
* have state that ''changes'' over the course of time.
66+
*
67+
* An object ''has a state'' if its behavior is influenced by its
68+
* history.
69+
*
70+
* Example: a bank account has a state, because the answer to the question
71+
* “can I withdraw 100 CHF ?” may vary over the course of the lifetime of
72+
* the account.
73+
*
74+
* = Implementation of State =
75+
*
76+
* Every form of mutable state is constructed from variables.
77+
*
78+
* A variable definition is written like a value definition, but with the
79+
* keyword `var` in place of `val`:
80+
*
81+
* {{{
82+
* var x: String = "abc"
83+
* var count = 111
84+
* }}}
85+
*
86+
* Just like a value definition, a variable definition associates a value
87+
* with a name.
88+
*
89+
* However, in the case of variable definitions, this association can be
90+
* changed later through an ''assignment'':
91+
*
92+
* {{{
93+
* x = "hi"
94+
* count = count + 1
95+
* }}}
96+
*
97+
* = State in Objects =
98+
*
99+
* In practice, objects with state are usually represented by objects that
100+
* have some variable members.
101+
*
102+
* Here is a class modeling a bank account:
103+
*
104+
* {{{
105+
* class BankAccount {
106+
* private var balance = 0
107+
* def deposit(amount: Int): Int = {
108+
* if (amount > 0) balance = balance + amount
109+
* balance
110+
* }
111+
* def withdraw(amount: Int): Int =
112+
* if (0 < amount && amount <= balance) {
113+
* balance = balance - amount
114+
* balance
115+
* } else throw new Error("insufficient funds")
116+
* }
117+
* }}}
118+
*
119+
* The class `BankAccount` defines a variable `balance` that contains the
120+
* current balance of the account.
121+
*
122+
* The methods `deposit` and `withdraw` change the value of the `balance`
123+
* through assignments.
124+
*
125+
* Note that `balance` is `private` in the `BankAccount`
126+
* class, it therefore cannot be accessed from outside the class.
127+
*
128+
* To create bank accounts, we use the usual notation for object creation:
129+
*
130+
* {{{
131+
* val account = new BankAccount
132+
* }}}
133+
*
134+
* = Working with Mutable Objects =
135+
*
136+
* Here is a program that manipulates bank accounts.
137+
*
138+
* {{{
139+
* val account = new BankAccount // account: BankAccount = BankAccount
140+
* account deposit 50 //
141+
* account withdraw 20 // res1: Int = 30
142+
* account withdraw 20 // res2: Int = 10
143+
* account withdraw 15 // java.lang.Error: insufficient funds
144+
* }}}
145+
*
146+
* Applying the same operation to an account twice in a row produces different results.
147+
* Clearly, accounts are stateful objects.
148+
*
149+
* = Identity and Change =
150+
*
151+
* Assignment poses the new problem of deciding whether two expressions
152+
* are "the same"
153+
*
154+
* When one excludes assignments and one writes:
155+
*
156+
* {{{
157+
* val x = E; val y = E
158+
* }}}
159+
*
160+
* where `E` is an arbitrary expression, then it is reasonable to assume that
161+
* `x` and `y` are the same. That is to say that we could have also written:
162+
*
163+
* {{{
164+
* val x = E; val y = x
165+
* }}}
166+
*
167+
* (This property is usually called ''referential transparency'')
168+
*
169+
* But once we allow the assignment, the two formulations are different. For example:
170+
*
171+
* {{{
172+
* val x = new BankAccount
173+
* val y = new BankAccount
174+
* }}}
175+
*
176+
* Are `x` and `y` the same?
177+
*
178+
* = Operational Equivalence =
179+
*
180+
* To respond to the last question, we must specify what is meant by “the same”.
181+
*
182+
* The precise meaning of “being the same” is defined by the property of
183+
* ''operational equivalence''.
184+
*
185+
* In a somewhat informal way, this property is stated as follows:
186+
*
187+
* - Suppose we have two definitions `x` and `y`.
188+
* - `x` and `y` are operationally equivalent if ''no possible test'' can
189+
* distinguish between them.
190+
*
191+
* = Testing for Operational Equivalence =
192+
*
193+
* To test if `x` and `y` are the same, we must
194+
*
195+
* - Execute the definitions followed by an arbitrary sequence `S` of operations that
196+
* involves `x` and `y`, observing the possible outcomes.
197+
*
198+
* {{{
199+
* val x = new BankAccount
200+
* val y = new BankAccount
201+
* f(x, y)
202+
* }}}
203+
*
204+
* - Then, execute the definitions with another sequence `S'` obtained by
205+
* renaming all occurrences of `y` by `x` in `S`:
206+
*
207+
* {{{
208+
* val x = new BankAccount
209+
* val y = new BankAccount
210+
* f(x, x)
211+
* }}}
212+
*
213+
* - If the results are different, then the expressions `x` and `y` are certainly different.
214+
* - On the other hand, if all possible pairs of sequences `(S, S')` produce the same result,
215+
* then `x` and `y` are the same.
216+
*
217+
* Based on this definition, let's see if the expressions
218+
*
219+
* {{{
220+
* val x = new BankAccount
221+
* val y = new BankAccount
222+
* }}}
223+
*
224+
* Let's follow the definitions by a test sequence:
225+
*
226+
* {{{
227+
* val x = new BankAccount
228+
* val y = new BankAccount
229+
* x deposit 30
230+
* y withdraw 20 // java.lang.Error: insufficient funds
231+
* }}}
232+
*
233+
* Now rename all occurrences of `y` with `x` in this sequence. We obtain:
234+
*/
235+
def observationalEquivalence(res0: Int): Unit = {
236+
val x = new BankAccount
237+
val y = new BankAccount
238+
x deposit 30
239+
x withdraw 20 shouldBe res0
240+
}
241+
242+
/**
243+
* The final results are different. We conclude that `x` and `y`
244+
* are not the same.
245+
*
246+
* = Establishing Operational Equivalence =
247+
*
248+
* On the other hand, if we define
249+
*
250+
* {{{
251+
* val x = new BankAccount
252+
* val y = x
253+
* }}}
254+
*
255+
* then no sequence of operations can distinguish between `x` and `y`, so
256+
* `x` and `y` are the same in this case.
257+
*
258+
* = Assignment and Substitution Model =
259+
*
260+
* The preceding examples show that our model of computation by
261+
* substitution cannot be used.
262+
*
263+
* Indeed, according to this model, one can always replace the name of a
264+
* value by the expression that defines it. For example, in
265+
*
266+
* {{{
267+
* val x = new BankAccount
268+
* val y = x
269+
* }}}
270+
*
271+
* the `x` in the definition of `y` could be replaced by `new BankAccount`.
272+
*
273+
* But we have seen that this change leads to a different program!
274+
*
275+
* The substitution model ceases to be valid when we add the assignment.
276+
*
277+
* It is possible to adapt the substitution model by introducing a ''store'',
278+
* but this becomes considerably more complicated.
279+
*
280+
* = Imperative Loops =
281+
*
282+
* In the first sections, we saw how to write loops using recursion.
283+
*
284+
* == While-Loops ==
285+
*
286+
* We can also write loops with the `wile` keyword:
287+
*
288+
* {{{
289+
* def power (x: Double, exp: Int): Double = {
290+
* var r = 1.0
291+
* var i = exp
292+
* while (i > 0) { r = r * x; i = i - 1 }
293+
* r
294+
* }
295+
* }}}
296+
*
297+
* As long as the condition of a ''while'' statement is `true`,
298+
* its body is evaluated.
299+
*
300+
* == For-Loops ==
301+
*
302+
* In Scala there is a kind of `for` loop:
303+
*
304+
* {{{
305+
* for (i <- 1 until 3) { System.out.print(i + " ") }
306+
* }}}
307+
*
308+
* This displays `1 2`.
309+
*
310+
* For-loops translate similarly to for-expressions, but using the
311+
* `foreach` combinator instead of `map` and `flatMap`.
312+
*
313+
* `foreach` is defined on collections with elements of type `A` as follows:
314+
*
315+
* {{{
316+
* def foreach(f: A => Unit): Unit =
317+
* // apply `f` to each element of the collection
318+
* }}}
319+
*
320+
* Example:
321+
*
322+
* {{{
323+
* for (i <- 1 until 3; j <- "abc") println(i + " " + j)
324+
* }}}
325+
*
326+
* translates to:
327+
*
328+
* {{{
329+
* (1 until 3) foreach (i => "abc" foreach (j => println(i + " " + j)))
330+
* }}}
331+
*/
332+
def nothing(): Unit = ()
6333
}

src/main/scala/scalatutorial/sections/ObjectOrientedProgramming.scala

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -533,20 +533,6 @@ object ObjectOrientedProgramming extends ScalaTutorialSection {
533533
*
534534
* Singleton objects are values, so `Empty` evaluates to itself.
535535
*
536-
537-
Exercise
538-
========
539-
540-
Write a method `union` for forming the union of two sets. You should
541-
implement the following abstract class.
542-
543-
abstract class IntSet {
544-
def incl(x: Int): IntSet
545-
def contains(x: Int): Boolean
546-
def union(other: IntSet): IntSet
547-
}
548-
549-
*
550536
* = Dynamic Binding =
551537
*
552538
* Object-oriented languages (including Scala) implement
@@ -571,7 +557,7 @@ implement the following abstract class.
571557
*
572558
* = Traits =
573559
*
574-
* In Java, as well as in Scala, a class can only have one superclass.
560+
* In Scala, a class can only have one superclass.
575561
*
576562
* But what if a class has several natural supertypes to which it conforms
577563
* or from which it wants to inherit code?
@@ -596,7 +582,7 @@ implement the following abstract class.
596582
* class Square extends Shape with Planar with Movable …
597583
* }}}
598584
*
599-
* Traits cannot have (value) parameters, only classes can.
585+
* On the other hand, traits cannot have (value) parameters, only classes can.
600586
*
601587
* = Scala's Class Hierarchy =
602588
*

0 commit comments

Comments
 (0)