Code Examples for

Programming in Scala, Fifth Edition

Return to chapter index

16 Mutable Objects

Sample run of chapter's interpreter examples

16.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): Unit = require(amount > 0) bal += amount def withdraw(amount: Int): Boolean = if amount > bal then false else bal -= amount true
val account = new BankAccount account.deposit(100) account.withdraw(80) // true account.withdraw(80) // 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 then keyCache = Some(super.computeKey) keyCache.get

16.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 var h = 12 private 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 var h = 12 private 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 import scala.compiletime.uninitialized class Thermometer: var celsius: Float = uninitialized 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
val t = new Thermometer t // 32.0F/0.0C t.celsius = 100 t // 212.0F/100.0C t.fahrenheit = -40 t // -40.0F/-40.0C

16.3 Case study: Discrete event simulation

16.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): Unit def andGate(a1: Wire, a2: Wire, output: Wire): Unit def orGate(o1: Wire, o2: Wire, output: Wire): Unit
// 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)

16.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 then 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 do 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 then 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 do next()
// In file mutable-objects/Simulator2.scala private def next() = (agenda: @unchecked) match case item :: rest => agenda = rest curtime = item.time item.action()
27 | agenda match | ^^^^^^ | match may not be exhaustive. | | It would fail on pattern case: Nil

16.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.empty def getSignal = sigVal def setSignal(s: Boolean) = if s != sigVal then 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.empty def getSignal = sigVal def setSignal(s: Boolean) = if s != sigVal then 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.*
scala> object MySimulation extends CircuitSimulation: | def InverterDelay = 1 | def AndGateDelay = 3 | def OrGateDelay = 5 // defined object MySimulation
scala> import MySimulation.*
scala> val input1, input2, sum, carry = new Wire val input1: MySimulation.Wire = ... val input2: MySimulation.Wire = ... val sum: MySimulation.Wire = ... val carry: MySimulation.Wire = ... 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

16.7 Conclusion

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

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

and:

http://booksites.artima.com/programming_in_scala_5ed

Copyright © 2007-2020 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.