Scala is a new Functional/OO hybrid language.
The existing materials that introduce Scala tend toward demonstrating the features that differentiate
Scala from other languages. I am working on a series of code that introduces Scala to Java and Ruby
developers in a way that focuses on the similarities using Scala-style idioms. I hope that this series will help
people get comfortable with Scala such that they can make use of all the cool and powerful features
that differentiate Scala. Feedback and comments are appreciated.
Step 1 was an introduction to some of the basic Scala idioms. The most "functional" it got
was passing a function to another function. Step 2 is more oriented toward slightly more advanced
functional concepts: zero side effects and message-based Actors.
Imperative programs are littered with little bits of state. There's global state. There's mutable
object state. There's state in methods. When one tests, one has to test the cross-product of state.
There can be unintended side effects based on changing state where state was not changed in the past.
Java has immutable Strings. Once a String is created, it never changes. Strings are safe to pass around.
Strings don't need any 'synchronization' for access because once they are created, they don't change.
Functional languages extend the immutable classes significantly. Many collections are immutable.
There are all kinds of optimizations that can be done with, for example, singly linked lists. Just like
immutable Strings are on balance a performance boon, other "language level" constructs (collections)
can benefit from being immutable.
The other functionally flavored construct is "Actors." Erlang is the best example of Actors. They are
light weight "message handlers". Messages are sent to Actors, they are processed and if a response is required,
the response is sent. Actors are very "dynamic typed" in that you can send any object as a message to an Actor.
It's up to the Actor to handle or ignore the message (or respond with an error.) For more, see
Event-Based Programming without Inversion
of Control.
The source is available
import scala.collection.immutable.TreeMap
import scala.xml.{Node, NodeSeq, Elem, Text}
import scala.actors.Actor
import scala.actors.Actor._
case class Item(name: String, pn: String, price: double, qnty: int) {
def add(toAdd: int) = {
itemWithQnty(qnty + toAdd) }
def take(howMany: int) = {
val toTake = Math.max(howMany, qnty)
Pair(toTake, itemWithQnty(qnty - toTake))
}
private def itemWithQnty(newQnty: int) = Item(name, pn, price, newQnty)
def toXml = <item name={name} pn={pn} price={price.toString}
qnty={qnty.toString} />
}
object Sample2 extends Application {
val inv1 = <update>
<item pn='a' qnty='25' name='Apple' price='0.25' />
</update>
val inv2 = <update>
<item pn='o' qnty='45' name="Orange" price='0.4' />
<item pn='b' qnty='4' name="Banana" price="0.15" />
</update>
Console.println(Server.update(inv1))
Console.println(Server.update(inv2))
val purchase = <order>
<item pn='a' qnty='20'/>
<item pn='b' qnty='10'/>
<item pn='o' qnty='35'/>
<item pn='na' qnty='23'/>
</order>
Console.println(Server.order(purchase))
Server.shutdown
}
object Server {
private val server = {
val ret = new SafeServer
ret.start
ret
}
def shutdown = server ! "exit"
def update(in: Elem) = {
eachItem(in) {
(e, pn, qnty) =>
server ! Update(pn, e.attribute("name").get.text,
java.lang.Double.parseDouble(e.attribute("price").
get.text), qnty)
Text("")
}
currentInventory }
def order(in: Elem) = {
<order>
{
eachItem(in) { (e, pn, qnty) =>
server !? Order(pn, qnty) match {
case None => <not_found pn={pn}/>
case s: Some[Pair[int, Item]] => { val item = s.get._2 val took = s.get._1
<shipped pn={pn} ordered={qnty.toString} shipped={took.toString}
cost={(item.price * took).toString}/>
}
}
}
}
</order>
}
private def eachItem(in: Elem)(f : (Elem, String, int) => Node): NodeSeq = {
in.child.map { node =>
node match {
case n @ <item/> if (!n.attribute("pn").isEmpty &&
!n.attribute("qnty").isEmpty) =>
f(n, n.attribute("pn").get.text,
Integer.parseInt(n.attribute("qnty").get.
text))
case _ => Text("")
}
}
}
private def items: Iterator[Item] = {
server !? "items" match {
case i: Iterator[Item] => i
case _ => new Array[Item](0).elements
}
}
def currentInventory = {
val invValue = items.foldRight(0.0){(i,sum) => sum + i.price * i.qnty}
<inventory value={invValue.toString}>{
items.filter{i => i.qnty > 0}.
map {i => Text("\n ") concat i.toXml}.
toList
}
</inventory>
}
}
class SafeServer extends Actor {
def act = loop(new TreeMap[String, Item])
def loop(info: TreeMap[String, Item]) {
receive {
case "items" => {reply(info.values) ; loop(info)}
case "exit" => exit("done")
case Update(pn, name, price, qnty) =>
{
val tmpItem = info.get(pn) match {
case None => Item(name, pn, price, qnty)
case Some(item) => item.add(qnty)
}
loop(info.update(tmpItem.pn, tmpItem))
}
case Order(pn, qnty) =>
{
val tmpInfo = info.get(pn) match {
case None => {reply( None); info}
case Some(item) => {
val res = item.take(qnty)
reply( Some(res))
info.update(res._2.pn, res._2)
}
}
loop(tmpInfo)
}
}
}
}
case class Order(pn: String, qnty: int)
case class Update(pn: String, name: String, price: double, qnty: int)