Code Examples for

Programming in Scala, Fourth Edition

Return to chapter index

18 Mutable Objects

Sample run of chapter's interpreter examples

18.1 What makes an object mutable?


// In file mutable-objects/Ex1.scala val cs = List('a', 'b', 'c')
// In file mutable-objects/Ex2.scala class BankAccount { private var bal: Int = 0 def balance: Int = bal def deposit(amount: Int) = { require(amount > 0) bal += amount } def withdraw(amount: Int): Boolean = if (amount > bal) false else { bal -= amount true } }
scala> val account = new BankAccount account: BankAccount = BankAccount@d504137 scala> account deposit 100 scala> account withdraw 80 res1: Boolean = true scala> account withdraw 80 res2: Boolean = false
// In file mutable-objects/Ex3.scala class Keyed { def computeKey: Int = ... // this will take some time ... }
// In file mutable-objects/Ex3.scala class MemoKeyed extends Keyed { private var keyCache: Option[Int] = None override def computeKey: Int = { if (!keyCache.isDefined) keyCache = Some(super.computeKey) keyCache.get } }

18.2 Reassignable variables and properties


var hour = 12
// In file mutable-objects/Ex4.scala class Time { var hour = 12 var minute = 0 }
// In file mutable-objects/Ex5.scala class Time { private[this] var h = 12 private[this] var m = 0 def hour: Int = h def hour_=(x: Int) = { h = x } def minute: Int = m def minute_=(x: Int) = { m = x } }
// In file mutable-objects/Ex6.scala class Time { private[this] var h = 12 private[this] var m = 0 def hour: Int = h def hour_= (x: Int) = { require(0 <= x && x < 24) h = x } def minute = m def minute_= (x: Int) = { require(0 <= x && x < 60) m = x } }
// In file mutable-objects/Ex7.scala class Thermometer { var celsius: Float = _ def fahrenheit = celsius * 9 / 5 + 32 def fahrenheit_= (f: Float) = { celsius = (f - 32) * 5 / 9 } override def toString = s"{fahrenheit}F/{celsius}C" }
var celsius: Float
scala> val t = new Thermometer t: Thermometer = 32.0F/0.0C scala> t.celsius = 100 mutated t.celsius scala> t res3: Thermometer = 212.0F/100.0C scala> t.fahrenheit = -40 mutated t.fahrenheit scala> t res4: Thermometer = -40.0F/-40.0C

18.3 Case study: Discrete event simulation

18.4 A language for digital circuits


val a = new Wire val b = new Wire val c = new Wire
val a, b, c = new Wire
def inverter(input: Wire, output: Wire) def andGate(a1: Wire, a2: Wire, output: Wire) def orGate(o1: Wire, o2: Wire, output: Wire)
// In file mutable-objects/Simulator2.scala def halfAdder(a: Wire, b: Wire, s: Wire, c: Wire) = { val d, e = new Wire orGate(a, b, d) andGate(a, b, c) inverter(c, e) andGate(d, e, s) }
// In file mutable-objects/Simulator2.scala def fullAdder(a: Wire, b: Wire, cin: Wire, sum: Wire, cout: Wire) = { val s, c1, c2 = new Wire halfAdder(a, cin, s, c1) halfAdder(b, s, sum, c2) orGate(c1, c2, cout) }

18.5 The Simulation API


// In file mutable-objects/Simulator1.scala abstract class Simulation { type Action = () => Unit case class WorkItem(time: Int, action: Action) private var curtime = 0 def currentTime: Int = curtime private var agenda: List[WorkItem] = List() private def insert(ag: List[WorkItem], item: WorkItem): List[WorkItem] = { if (ag.isEmpty || item.time < ag.head.time) item :: ag else ag.head :: insert(ag.tail, item) } def afterDelay(delay: Int)(block: => Unit) = { val item = WorkItem(currentTime + delay, () => block) agenda = insert(agenda, item) } private def next() = { (agenda: @unchecked) match { case item :: rest => agenda = rest curtime = item.time item.action() } } def run() = { afterDelay(0) { println("*** simulation started, time = " + currentTime + " ***") } while (!agenda.isEmpty) next() } }
// In file mutable-objects/Simulator2.scala type Action = () => Unit
// In file mutable-objects/Simulator2.scala private var curtime: Int = 0
// In file mutable-objects/Simulator2.scala def currentTime: Int = curtime
// In file mutable-objects/Simulator2.scala case class WorkItem(time: Int, action: Action)
// In file mutable-objects/Simulator2.scala private var agenda: List[WorkItem] = List()
// In file mutable-objects/Simulator2.scala def afterDelay(delay: Int)(block: => Unit) = { val item = WorkItem(currentTime + delay, () => block) agenda = insert(agenda, item) }
afterDelay(delay) { count += 1 }
private def insert(ag: List[WorkItem], item: WorkItem): List[WorkItem] = { if (ag.isEmpty || item.time < ag.head.time) item :: ag else ag.head :: insert(ag.tail, item) }
// In file mutable-objects/Simulator2.scala def run() = { afterDelay(0) { println("*** simulation started, time = " + currentTime + " ***") } while (!agenda.isEmpty) next() }
// In file mutable-objects/Simulator2.scala private def next() = { (agenda: @unchecked) match { case item :: rest => agenda = rest curtime = item.time item.action() } }
Simulator.scala:19: warning: match is not exhaustive! missing combination Nil agenda match { ^ one warning found

18.6 Circuit Simulation


// In file mutable-objects/Simulator1.scala package org.stairwaybook.simulation abstract class BasicCircuitSimulation extends Simulation { def InverterDelay: Int def AndGateDelay: Int def OrGateDelay: Int class Wire { private var sigVal = false private var actions: List[Action] = List() def getSignal = sigVal def setSignal(s: Boolean) = if (s != sigVal) { sigVal = s actions foreach (_ ()) } def addAction(a: Action) = { actions = a :: actions a() } } def inverter(input: Wire, output: Wire) = { def invertAction() = { val inputSig = input.getSignal afterDelay(InverterDelay) { output setSignal !inputSig } } input addAction invertAction } // continued in Listing 18.10...
// In file mutable-objects/Simulator1.scala // ...continued from Listing 18.9 def andGate(a1: Wire, a2: Wire, output: Wire) = { def andAction() = { val a1Sig = a1.getSignal val a2Sig = a2.getSignal afterDelay(AndGateDelay) { output setSignal (a1Sig & a2Sig) } } a1 addAction andAction a2 addAction andAction } def orGate(o1: Wire, o2: Wire, output: Wire) = { def orAction() = { val o1Sig = o1.getSignal val o2Sig = o2.getSignal afterDelay(OrGateDelay) { output setSignal (o1Sig | o2Sig) } } o1 addAction orAction o2 addAction orAction } def probe(name: String, wire: Wire) = { def probeAction() = { println(name + " " + currentTime + " new-value = " + wire.getSignal) } wire addAction probeAction } }
// In file mutable-objects/Simulator2.scala class Wire { private var sigVal = false private var actions: List[Action] = List() def getSignal = sigVal def setSignal(s: Boolean) = if (s != sigVal) { sigVal = s actions foreach (_ ()) } def addAction(a: Action) = { actions = a :: actions a() } }
// In file mutable-objects/Simulator2.scala def inverter(input: Wire, output: Wire) = { def invertAction() = { val inputSig = input.getSignal afterDelay(InverterDelay) { output setSignal !inputSig } } input addAction invertAction }
// In file mutable-objects/Simulator2.scala def andGate(a1: Wire, a2: Wire, output: Wire) = { def andAction() = { val a1Sig = a1.getSignal val a2Sig = a2.getSignal afterDelay(AndGateDelay) { output setSignal (a1Sig & a2Sig) } } a1 addAction andAction a2 addAction andAction }
// In file mutable-objects/Simulator2.scala def probe(name: String, wire: Wire) = { def probeAction() = { println(name + " " + currentTime + " new-value = " + wire.getSignal) } wire addAction probeAction }
// In file mutable-objects/Simulator1.scala package org.stairwaybook.simulation abstract class CircuitSimulation extends BasicCircuitSimulation { def halfAdder(a: Wire, b: Wire, s: Wire, c: Wire) = { val d, e = new Wire orGate(a, b, d) andGate(a, b, c) inverter(c, e) andGate(d, e, s) } def fullAdder(a: Wire, b: Wire, cin: Wire, sum: Wire, cout: Wire) = { val s, c1, c2 = new Wire halfAdder(a, cin, s, c1) halfAdder(b, s, sum, c2) orGate(c1, c2, cout) } }
scala> import org.stairwaybook.simulation._ import org.stairwaybook.simulation._
scala> object MySimulation extends CircuitSimulation { | def InverterDelay = 1 | def AndGateDelay = 3 | def OrGateDelay = 5 | } defined module MySimulation
scala> import MySimulation._ import MySimulation._
scala> val input1, input2, sum, carry = new Wire input1: MySimulation.Wire = BasicCircuitSimulation$Wire@111089b input2: MySimulation.Wire = BasicCircuitSimulation$Wire@14c352e sum: MySimulation.Wire = BasicCircuitSimulation$Wire@37a04c carry: MySimulation.Wire = BasicCircuitSimulation$Wire@1fd10fa scala> probe("sum", sum) sum 0 new-value = false scala> probe("carry", carry) carry 0 new-value = false
scala> halfAdder(input1, input2, sum, carry)
scala> input1 setSignal true scala> run() *** simulation started, time = 0 *** sum 8 new-value = true scala> input2 setSignal true scala> run() *** simulation started, time = 8 *** carry 11 new-value = true sum 15 new-value = false

18.7 Conclusion

For more information about Programming in Scala, Fourth Edition (the "Stairway Book"), please visit:

http://www.artima.com/shop/programming_in_scala_4ed

and:

http://booksites.artima.com/programming_in_scala_4ed

Copyright © 2007-2019 Artima, Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.