Code Examples for

Programming in Scala

Return to chapter index

18 Stateful Objects

Sample run of chapter's interpreter examples

18.1 What makes an object stateful?


// In file stateful-objects/Ex1.scala val cs = List('a', 'b', 'c')
// In file stateful-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@bf5bb7 scala> account deposit 100 scala> account withdraw 80 res1: Boolean = true scala> account withdraw 80 res2: Boolean = false
// In file stateful-objects/Ex3.scala class Keyed { def computeKey: Int = ... // this will take some time ... }
// In file stateful-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 stateful-objects/Ex4.scala class Time { var hour = 12 var minute = 0 }
// In file stateful-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 stateful-objects/Ex6.scala class Time { private[this] var h = 12 private[this] var m = 12 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 stateful-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 = fahrenheit +"F/"+ celsius +"C" }
var celsius: Float
scala> val t = new Thermometer t: Thermometer = 32.0F/0.0C scala> t.celsius = 100 scala> t res3: Thermometer = 212.0F/100.0C scala> t.fahrenheit = -40 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 stateful-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 stateful-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 stateful-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 stateful-objects/Simulator2.scala type Action = () => Unit
// In file stateful-objects/Simulator2.scala private var curtime: Int = 0
// In file stateful-objects/Simulator2.scala def currentTime: Int = curtime
// In file stateful-objects/Simulator2.scala case class WorkItem(time: Int, action: Action)
// In file stateful-objects/Simulator2.scala private var agenda: List[WorkItem] = List()
// In file stateful-objects/Simulator2.scala def afterDelay(delay: Int)(block: => Unit) { val item = WorkItem(currentTime + delay, () => block) agenda = insert(agenda, item) }
afterDelay(delay) { count += 1 }
// In file concurrency/ParallelSimulation.scala 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 stateful-objects/Simulator2.scala def run() { afterDelay(0) { println("*** simulation started, time = "+ currentTime +" ***") } while (!agenda.isEmpty) next() }
// In file stateful-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 stateful-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 stateful-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 stateful-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 stateful-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 stateful-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 stateful-objects/Simulator2.scala def probe(name: String, wire: Wire) { def probeAction() { println(name +" "+ currentTime + " new-value = "+ wire.getSignal) } wire addAction probeAction }
// In file stateful-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 = simulator.BasicCircuitSimulation$Wire@111089b input2: MySimulation.Wire = simulator.BasicCircuitSimulation$Wire@14c352e sum: MySimulation.Wire = simulator.BasicCircuitSimulation$Wire@37a04c carry: MySimulation.Wire = simulator.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 (the "Stairway Book"), please visit:

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

and:

http://booksites.artima.com/programming_in_scala

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