/* So, you've heard about Scala. It's a cool functional language that compiles down to Java byte-code and looks and acts like a Java program. You've looked at some documentation and saw stuff like "higher order functions" and other stuff that made Scala seem complex. Scala is simple. You can code Scala almost like you code Java or Ruby (in fact, IMHO, Scala is like the best of both languages.) This example code demonstrates a bunch of Scala idioms, but should be readable by Java and Ruby developers alike. You can download Scala from http://scala-lang.org/ This example was developed with Scala 2.3.1, but should work with any version of Scala >= 2.3.1 To compile the example, cd into the directory that you've saved this file and type 'fsc Sample2.scala' To run the example, type 'scala Sample2' This is "Step 2" in David Pollak's Introducing Scala series This sample code introduces threadless concurrency (Actors) and using immutable data types (a staple of functional programming) Check out http://blog.lostlake.org for the latest rants and Scala stuff from David Pollak */ // we use an immutable TreeMap to store our items import scala.collection.immutable.TreeMap import scala.xml.{Node, NodeSeq, Elem, Text} // We use actors to manage our inventory // to allow for concurrency and remote access import scala.actors.Actor import scala.actors.Actor._ // define a holder for items // It differs from Step 1's Item in that this implementation is non-mutable. // It's like a Java string, once you create it // you cannot change it. To get a new one, you make a copy. case class Item(name: String, pn: String, price: double, qnty: int) { // add to the quantity, return a new instance // (like "123".substring(2) == "3") def add(toAdd: int) = { itemWithQnty(qnty + toAdd) // create a new, immutable instance } // take a certain number out of the item // return the number that was taken and the new item def take(howMany: int) = { val toTake = Math.max(howMany, qnty) // return the number "taken" and the rebuilt item Pair(toTake, itemWithQnty(qnty - toTake)) } // a private function that creates a new Item with the new quantity private def itemWithQnty(newQnty: int) = Item(name, pn, price, newQnty) // convert this item to XML def toXml = <item name={name} pn={pn} price={price.toString} qnty={qnty.toString} /> } // an "object" (singleton) that extends the "Application" // class will get run like a Java class with public static void main(String[]) object Sample2 extends Application { // create mock XML inventory updates 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> // Send them to the server Console.println(Server.update(inv1)) Console.println(Server.update(inv2)) // create a mock XML purchase order (note the embedded XML) val purchase = <order> <item pn='a' qnty='20'/> <item pn='b' qnty='10'/> <item pn='o' qnty='35'/> <item pn='na' qnty='23'/> </order> // send the XML to the server and print the response Console.println(Server.order(purchase)) // shut down the server so we can exit gracefully Server.shutdown } /* The singleton "inventory server" Implemented as an "Actor" Actors are "share nothing" constructs that receive messages and process them one by one. Actors provide a concurrency model that (1) is deadlock free (or at least deadlock reduced) (2) has been proven in Erlang over 10+ years and (3) does not require "synchronizes" or other keywords */ object Server { // the server actor. // vals are created the first time they are used... singletons private val server = { val ret = new SafeServer ret.start ret } // shut the server down by sending it "exit" def shutdown = server ! "exit" def update(in: Elem) = { eachItem(in) { (e, pn, qnty) => // send an update message to our server server ! Update(pn, e.attribute("name").get.text, java.lang.Double.parseDouble(e.attribute("price"). get.text), qnty) // no reason to return anything meaningful because we're not using // the XML return block Text("") } currentInventory // return the current inventory } // place an order and return an <order>...</order> XML block def order(in: Elem) = { <order> { eachItem(in) { // for each item (e, pn, qnty) => // send the server an "Order" and wait for the response server !? Order(pn, qnty) match { // if there's no matching part, generate a <not_found/> tag case None => <not_found pn={pn}/> case s: Some[Pair[int, Item]] => {// deconstruct the return value val item = s.get._2 // the item val took = s.get._1 // the number of items taken // generate the XML tag <shipped pn={pn} ordered={qnty.toString} shipped={took.toString} cost={(item.price * took).toString}/> } } } } </order> } // iterate over each "item" node and call f private def eachItem(in: Elem)(f : (Node, String, int) => Node): NodeSeq = { in.child.map { // for each node node => node match { // we've got an <item> tag and an 'pn' attribute case n @ <item/> if (!n.attribute("pn").isEmpty && // and a 'qnty' attribute !n.attribute("qnty").isEmpty) => // call the function f(n, n.attribute("pn").get.text, Integer.parseInt(n.attribute("qnty").get. text)) // if there's no match, return a "no op" Node case _ => Text("") } } } // get a list of items from the server private def items: Iterator[Item] = { /* send an "items" message (note that you can pass Any object to an actor server and the server can response with Any object. This is a lot like "duck typing" or dynamic typing. Actors either respond to the message or ignore it. */ server !? "items" match { // if the response is an Iterotor[Item] return it case i: Iterator[Item] => i // otherwise, return a zero length iterator case _ => new Array[Item](0).elements } } // generate the XML of the current inventory def currentInventory = { // the inventory value is the sum of price * qnty val invValue = items.foldRight(0.0){(i,sum) => sum + i.price * i.qnty} // return the XML including the inventory value and the XML of each node <inventory value={invValue.toString}>{ items.filter{i => i.qnty > 0}. map {i => Text("\n ") concat i.toXml}. toList } </inventory> } } /* Scala Actors are stand-alone units of computation. They get messages sent to them and they do something (or nothing) with the messages. They may send an asynchronous response, or not. Erlang has a time proven, very successful model of distrubuted computing based on Actors. You can read more about Actors in 'Event-Based Programming without Inversion of Control' @ http://lampwww.epfl.ch/~odersky/papers/jmlc06.pdf */ class SafeServer extends Actor { // start the message receive loop // with an empty map of our inventory def act = loop(new TreeMap[String, Item]) // this function continues to receive // messages and processes the messages def loop(info: TreeMap[String, Item]) { // receive a message that matches // one of the cases receive { // if it's "items" reply to the sender with // the values of our inventory and then loop case "items" => {reply(info.values) ; loop(info)} // exit from listening if we get "exit" case "exit" => exit("done") // If the message is an "Update" object // process the update without responding // to the sender case Update(pn, name, price, qnty) => { /* If the Item is in our inventory, update the quantity (remember that Item is immutable, so an inventory update creates a new instance of Item. If the Item is not in inventory, create a new one */ val tmpItem = info.get(pn) match { case None => Item(name, pn, price, qnty) case Some(item) => item.add(qnty) } // loop back on ourself with an updated inventory // tree loop(info.update(tmpItem.pn, tmpItem)) } // Process an "order" which requires a reply case Order(pn, qnty) => { // create a new info tree based on // matching the inventory level val tmpInfo = info.get(pn) match { // the part number is not found, respond with // None and the info tree does not change case None => {reply( None); info} // we've got a match case Some(item) => { // update the item val res = item.take(qnty) // reply reply( Some(res)) // update the info tree info.update(res._2.pn, res._2) } } // continue looping loop(tmpInfo) } } } } // an order has a part number and a quantity case class Order(pn: String, qnty: int) // update has more case class Update(pn: String, name: String, price: double, qnty: int) /* (c) 2007 WorldWide Conferencing, LLC. Distributed under an Apache V2.0 licnese Version 1.0 */