You can observe the principle difference between a purely functional object and a stateful one even without looking object's implementation.
like the list declaration, which will always return 'a' as the List.head()...while for a stateful object, on the other hand, the resulkt of a method call or field access may depend on what operation were previously performed on the object.. A good example of a stateful object is a bank account, list show a simplified implementation of bank accounts.
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
}
}
and with the implementaion, you may run the following code to test the state operation.
val account = new BankAccount
account deposit 100
account withdraw 80
account withdraw 80
the first withdraw return a 'true' value while the second returns a 'false' value.
Reassignable variables and properties
you can perform two fundamental operation on a reassignable variable: get the value or set it to a new value.
In scala, every var that is a non-private member of some object implicitly defineds a getter and a setter method with it.
unlike the java, the getter and setter of a variable are different, the getter of a x is just named 'x', while its setter is named 'x_='.
so the following definition of Time classes
class Time {
var hour = 12
var minute = 0
}
is the same as the folowing.
// new version of Time with getter and Setter
class Time {
private[this] var h = 12
private[this] var m = 0
def hour : Int = h
def minute : Int = m
// the interesting part- the setter is written as such, becareful of the _= that is appended to the end of methods
def minute_=(x :Int) { h = x}
def hour_=(x : Int) { m = x}
}
and a even more robust one is like the one below.
// new variant of Time with getter and Setter
class Time {
private[this] var h = 12
private[this] var m = 0
def hour : Int = h
def minute : Int = m
// the interesting part- the setter is written as such, becareful of the _= that is appended to the end of methods
def minute_=(x :Int) {
require (x < 60 && 0 <= x)
h = x
}
def hour_=(x : Int) {
require(0 <= x && x < 12)
m = x
}
}
and you can also define a getter or setter without an associated field. An example is the following thermometer which encapsulate a temperatur variable that an be read and update.
the temperature can be expressed in Celsius or Fahrenheit degrees. The classes below allows you to get and set the temperatur in either measure.
class Thermometer {
var celsius : Float = _ // by specifying '_' as the "initializing value", of the variable
def fahrenheit = celsius * 9 / 5 + 32
def fahrenheit_= (f : Float) {
celsius = (f - 32) * 5 / 9
}
override def toString = fahrenheit + "F/" + celsius + "C"
}
one special note on the = _ as the initializing value of celsius, which will contain the temperatur in degree celsius.. the celsius variable is initially set to a default value by specifying '_' as the "initializing value" of the variable. more precisely, it assigns a zero value to that field. The zero value depends on the field's type, it is 0 for numeric types, false for booleans, and null for references types, this is the same variable was defined in Java without an initializer.
the test code is written as follow.
val t= new Thermometer
t.celsius = 100
t
t.fahrenheit = -40
t
A case stydtm Discrete event simulation
this example is taken from the classic textblook "strucutre and Interpretation of Computer Programs" by Abelson and Sussman.. and instead of schema this example is using Scala.
first, we will define the base classes for the simulation.
// file:
// simulation.scala
// description:
// simulation API
package org.stairwaybook.simulation
abstract class Simulation {
// type will define an alias of some types,
// the example below will define () => Unit as the type of "Action"
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) { // by-name argument
val item = WorkItem(currentTime + delay, () => block) // pass in lambda
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()
}
}
the basic class has a list which will arrange the work items in the order of the action time, the insert method does the trick of inserting work items according to its order.
the action to be performed is passed with a by-name argument/paramter. and we havbe define a type alias, which is like a delegate in C#, the type alias is defined as follow.
// type will define an alias of some types,
// the example below will define () => Unit as the type of "Action"
type Action = () => Unit
and the afterDelay method will invoke by-name parameter, which is passed to the block parameter. an examle is
afterDelay(delay) { count += 1 }
recall the declaration of the afterDelay method.
def afterDelay(delay :Int) (block :=> Uint)
with the base class and the basic operation, the next thing to do is to extend it and implement the domain specific language for circuit.
// file:
// BasicCircuitSimulation.scala
// description:
// simulation
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 (_ ()) // iterate through the collection and call each action one by one
}
}
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
}
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(AndGateDelay) {
output setSignal (o1Sig | o2Sig)
}
}
o1 addAction orAction
o2 addAction orAction
}
// you can simulation the action of putting a probe on a wire..
def prob(name: String, wire : Wire) = {
def probeAction() {
println(name + " " + currentTime + " new-value = " + wire.getSignal)
}
wire addAction probeAction
}
}
in the BasicSimulationCircuitSimulation we defines three abstract method, they are InverterDelay, AndGateDelay and OrGateDelay, the actual delay is not known at the level of this class.
note on the class above, there is such code.
def setSignal(s : Boolean) = {
if (s != sigVal) {
sigVal = s
actions foreach (_ ()) // iterate through the collection and call each action one by one
}
}
what does it mean here is that if a singal is set in the input, and the signal is different from its original value, then all action that is connected to this wire will be fired (actions foreach (_ () ) means for each of the actions in the list, assign a temporary value to _, and then invoke the action denoted by _;.
and let's take a close look at the inverter method.
def inverter(input : Wire, output : Wire) = {
def invertAction() {
val inputSig = input.getSignal
afterDelay(InverterDelay) {
output setSignal !inputSig
}
}
input addAction invertAction
}
what it means is 1. get the singla input from the input wire, and setSignal on the output wire the reverse result of inputSignal. You can apply the same analysis on the andGate and the orGate.
One last thing, in order to inspect changes of signals on wires, To accomplish this, you can simulate the action of putting a probe on a wire.
// you can simulation the action of putting a probe on a wire..
def prob(name: String, wire : Wire) = {
def probeAction() {
println(name + " " + currentTime + " new-value = " + wire.getSignal)
}
wire addAction probeAction
}
To running the simulation, it's time to see the simulation in action. to define a concrete simulation, we need to inherit from the simulation framework class. to see something interesting, we will extend the BasicCircuitSimulation and contains method definitions for half-adder and full-adder.
// file
// circuitSimuation.scala
// description:
// the circuit simulation
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)
}
}
the real simuation will define the concrete delays before it is useful.
// file
// MySimulation.scala
// description:
// my simulation scala
//
// the simulation object o8i
// object extends class ?
// runs the following in the repl
package org.stairwaybook.simulation
// package org.stairwaybook.simulation
object MySimulation extends CircuitSimulation {
def InverterDelay = 1
def AndGateDelay = 3
def OrGateDelay = 5
}
import MySimulation._
val input1, input2, sum , carry = new Wire
prob("sum", sum)
prob("carry", carry)
halfAdder(input1, input2, sum, carry)
input1 setSignal true
run()
input2 setSignal true
run()