|
1 | 1 | package scalatutorial.sections |
2 | 2 |
|
| 3 | +import scalatutorial.aux.BankAccount |
| 4 | + |
3 | 5 | /** @param name imperative_programming */ |
4 | 6 | object ImperativeProgramming extends ScalaTutorialSection { |
5 | 7 |
|
| 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 = () |
6 | 333 | } |
0 commit comments