Create a Scala project called circuits. Then create a Scala class in the project's src directory called Counter.
A counter is an example of a simple memory circuit. It holds a single integer, count, which can be incremented modulo some limit.
Here's our initial class declaration:
class Counter(val lim: Int) {
val limit = lim
var count = 0
def this() { this(10)}
def inc { count = (count + 1) %
limit}
}
Notes:
· Counter has two fields, limit, a constant, and count, a variable. Both are private, but Scala generates public getter and setter methods for the variable and a public getter method for the constant. These methods have the same names as the corresponding fields creating the impression that the fields are public.
· Scala identifies classes with their primary constructors. So we can view this as the declaration of a counter constructor that takes a single parameter, lim. Executing the constructor executes every declaration in the following block.
· This class also provides an auxiliary constructor (this) which calls the primary constructor with lim = 10.
· Empty parameter lists need not be shown, except for the auxillary constructor, apparently.
We don't need to create and instantiate classes to create objects. If we only need a singleton—i.e. a single object—we can simply declare it using an object declaration.
For example, we only need a single TestCounter object to test our counter class. There are two approaches. We can create a tester with a main method:
object TestCounter {
def main(args: Array[String]): Unit = {
// Counter test code goes here
}
}
An object with a main method is executable. Alternatively, we can create a tester that extends the App object:
object TestCounter extends App {
// Counter test code goes here
}
App objects are executable. The declaration block of an object extending App is the code that will be executed.
Here is our tester:
object TestCounter extends App {
val c1 = new Counter(7); val c2 = new
Counter; val c3 = new Counter(3)
for(i <- 1 to 50) {
c1.inc
c2.inc
c3.inc
}
println("c1.count = " +
c1.count)
println("c2.count = " +
c2.count)
println("c3.count = " +
c3.count)
c3.count = 500
println("c3.count = " +
c3.count)
println("c3.limit = " +
c3.limit)
}
Here's the output it produces:
c1.count = 1
c2.count = 0
c3.count = 2
c3.count = 500
c3.limit = 3
Notice that the count and limit fields of our counters appear to be accessible to the tester. Actually, these are calls to auto-generated getters and setters.
Let's try to improve our Counter declaration:
class Counter(val limit: Int = 10) {
// val limit = lim
private var count = 0
// def this() { this(10)}
def inc { count = (count + 1) % limit}
def getCount = count
}
Notes:
· We can use the parameters for the primary constructor as the fields themselves.
· We can also specify a default value for limit. This eliminates the need for the auxiliary constructor
· We declared count private, so no more auto-generated setters in getters. Instead, we must provide our own getter.
Here's the new test harness:
object TestCounter extends App {
val c1 = new Counter(7); val c2 = new
Counter; val c3 = new Counter(3)
for(i <- 1 to 50) {
c1.inc
c2.inc
c3.inc
}
println("c1.count = " +
c1.getCount)
println("c2.count = " +
c2.getCount)
println("c3.count = " +
c3.getCount)
// c3.count = 500 illegal now
// println("c3.count = " +
c3.getCount)
println("c3.limit = " +
c3.limit)
}
Here is the output it produces:
c1.count = 1
c2.count = 0
c3.count = 2
c3.limit = 3
A companion object is a special instance of a class that may contain additional fields and methods.
These additional members can be treated as the static members of the class.
Here's a companion object for the Counter class. It must have the same name as the class and must be declared in the same .scala file:
object Counter {
var numCounters = 0 // keep track of
the number of counters created
def apply(lim: Int = 10) = { new
Counter(lim) }
def test(c: Counter) {
for(i <- 1 to 10) c.inc
println("count = " +
c.getCount)
}
}
We'll increment numCounters in the class/constructor declaration:
class Counter(val limit: Int = 10) {
private var count = 0
Counter.numCounters += 1
def inc { count = (count + 1) % limit}
def getCount = count
}
Here's our revised test harness:
object TestCounter extends App {
val c1 = Counter(7); val c2 = Counter();
val c3 = Counter(3);
Counter.test(c1)
Counter.test(c2)
Counter.test(c3)
println("# counters = " +
Counter.getCount)
}
Here's the output produced:
count = 3
count = 0
count = 1
# counters = 3
Notes:
· Because we defined an apply method, we can call the companion object like a function. (This is Scala's secret to merging functional and object-oriented programming. All functions are objects and the syntax foo(x, y, z) compiles to foo.apply(x, y, z).)
· Counter.apply invokes the Counter constructor and therefore eliminates the need to call new.
· We must restore the empty parentheses to the definition of c2, otherwise c2 becomes another name for the companion object.
· We increment numCounters in the primary constructor in case someone wants to still use new to create counters.
· Scala has no static variables, constants, or functions. It doesn't need them. Any such thing can be declared in a singleton or companion object.
Generic classes contain type parameters:
class Stack[T] {
val elems = new ArrayBuffer[T](100)
var sp = 0
def push(elem: T) {
elems += elem
sp += 1
}
def top = if (sp > 0)elems(sp - 1)
def pop { sp -= 1 }
}
Now the same code can be reused to create different types of stacks:
object Stack extends App {
val s = new Stack[String]
s.push("One")
s.push("Two")
s.push("Three")
println(s.top)
s.pop
println(s.top)
val ss = new Stack[Int]
ss.push(1)
ss.push(2)
ss.push(3)
println(ss.top)
ss.pop
println(ss.top)
}
Notes:
· The companion object can extend App.