scala + wattzon, sitting in a tree
DESCRIPTION
A talk given to Scala enthusiasts at the Twitter HQ on using Scala in a real world environments (we get paid to write in Scala)TRANSCRIPT
Bay Area Scala Enthusiasts
Scala and WattzOn,Sitting in a Tree...
March 10, 2009
Who are we?
Jeremy Cloud Raffi Krikorian
Who is Synthesis?
• Synthesis is a software and strategy consultancy
• Cambridge, MA
• 7 people
What is WattzOn?
• WattzOn is a free online tool to quantify, track, compare and understand the total amount of energy needed to support all of the facets of your lifestyle
End User Perspective
My Lifestyle: 14,504 Watts
What is WattzOn?Developer Perspective
WattzOn
Holmz
scala-orm
scala-utils
swag
What is Holmz?
• Object oriented “spread-sheet”
• Hierarchies of objects with properties
• Some properties are computed from formulas over other objects and properties
• Sophisticated formula language (DSL)
• Intended as back-end for other systems
Holmz Understands Units
• Define a property as being “meters/second”
• Insert a value in “smoots/year”, and it will auto convert
• (5 [gal] * 50[MJ/L] / 4[h]) @ [W]
Holmz DSLProperties can be defined as simple algebraic expressions over other properties within the same object
mpg = miles / gallon
Holmz DSLProperties can be defined as a reduction of properties on other objects
stuff_watts = for s in creator.worksheets.profile_stuff select sum(s.watts) default 0[W]
Holmz DSLFormulas can contain extractor expressions for decomposing other objects
component_rollup = for [count, comp] in components
let [trans_watts, _*] = item_to_country(comp, made_in)
select sum(count * (comp.watts + trans_watts)), sum(count * comp.mass), sum(count * comp.disposal_watts)
default 0[W], 0[kg], 0[W]
Parser Combinators
def cond = (or ~ opt("?" ~> expr ~ (":" ~> expr))) ^^ {case c ~ None => ccase c ~ Some(t ~ e) => Conditional(c, t, e)
}
def or = chainl1(and, "||" ~> and, success{(a:AST, b:AST)=>Or(a,b)})
def and = chainl1(rel, "&&" ~> rel, success{(a:AST, b:AST)=>And(a,b)})
Parser Combinators
• At first, appears to be line noise
• Terse
• Reasonably quick to write, once you understand them
• Gets a bit hairy for complex grammars
• Error handling is difficult
• Performance is so-so
Handwritten Parser
• About 6x faster
• About 6x the number of lines of code
• Better error reporting
Handwritten Parser
def cond = {ts.foldOpt(or) {case (c, SyntaxToken("?", _)) =>val t = exprts.syntax(":")val e = exprConditional(c, t, e)
}}
def cond = (or ~ opt("?" ~> expr ~ (":" ~> expr))) ^^ {case c ~ None => ccase c ~ Some(t ~ e) => Conditional(c, t, e)
}
Handwritten Parser
def or = {ts.fold(and) {case (left, SyntaxToken("||", _)) => Or(left, and)
}}
def or = chainl1(and, "||" ~> and, success{(a:AST, b:AST)=>Or(a,b)})
Parser Combinators,To use or not to use
• Complexity of grammar
• Performance sensitivity
• Error reporting
• Readability a wash
What is WattzOn?Developer Perspective
WattzOn
Holmz
scala-orm
scala-utils
swag
What is scala-orm?
• native scala O/R mapping layer
• lightweight and fast
• XML schema in, generated scala code out
• unimaginative name
scala-orm schema
<table name="EnumValue"><field name="version" type="int"/><field name="enumType" type="EnumType"/><field name="key" type="string"/><field name="displayName" type="string"/><field name="description" type="string" size="0,65535"/>
<field name="creator" type="User"/><index fields="enumType, key" unique="true"/>
</table>
scala-orm output
• data object class
• data accessor object
• low overhead read, write, delete methods
• no reflection
scala idioms
• def get(id: ID)(implicit session: SdbSession): Option[T]
• def getAll(ids: ID*)(implicit session: SdbSession): Stream[T]
dynamic query builder
def getRecentlyAnswered(n: Int): JList[WorksheetAnswers] =
{DomainObject.query.where("domain" -> domain).innerJoin("creator", "confirmed" -> true).orderBy("createdAt" -> false).limit(n).query.all.map(getOrUpdateAnswers)
}
What is WattzOn?Developer Perspective
WattzOn
Holmz
scala-orm
scala-utils
swag
What is scala-utils?
• miscellaneous utility classes
• JavaBridge
JavaBridge
• Interacting with java collections is cumbersome in scala
Java Lists w/o JavaBridge
import java.util.{List => JList}
def painful(jlist: JList[String]) = {val iter = jlist.iteratorwhile (iter.hasNext) {val elt = iter.next...
}
Feels like Java 1.3 or earlier
JavaBridge
import java.lang.{Iterable=>JIterable}
object JavaBridge {implicit def jit2sit[T](jit:JIterable[T]):Iterable[T] = {new Iterable[T] { def elements:Iterator[T] = new Iterator[T] {
val baseIter = jit.iterator()def hasNext = baseIter.hasNextdef next = baseIter.next
}}
}...
}
Java Lists w/ JavaBridge
import java.util.{List => JList}import JavaBridge._
def easy(jlist: JList[String]) = {for (elt <- jlist) {...
}}
Java Maps w/o JavaBridge
import java.util.{map => JMap}
def painful(jmap: JMap[String,String]) = {val iter = jmap.entrySet.iteratorwhile (iter.hasNext) {val entry = iter.nextval key = entry.getKeyval value = entry.getValue...
}}
JavaBridgeimport java.util.{Map=>JMap}
object JavaBridge {...implicit def jmap2sit[K,V](jmap:JMap[K,V]):Iterable[(K,V)] = new Iterable[(K,V)] {def elements:Iterator[(K,V)] = new Iterator[(K,V)] { val baseIter = jmap.entrySet.iterator
def hasNext = baseIter.hasNext def next = { val entry = baseIter.next()
(entry.getKey(), entry.getValue()) } }}...
}
Java Maps w/ JavaBridge
import java.util.{map => JMap}import JavaBridge._
def easy(jmap: JMap[String,String]) = {for ((key, value) <- jmap) {...
}}
Java Maps w/o JavaBridge
import java.util.{HashMap=>JHashMap,Map=>JMap}
def painful(): JMap[String,String] = {val attrs = new JHashMap[String,String]attrs.put(“firstName”, “Jeremy”)attrs.put(“lastName”, “Cloud”attrs
}
JavaBridge
import java.collection.{HashMap=>JHashMap, Map=>JMap}
object JavaBridge {...def jmap[A, B](elems: (A, B)*): JMap[A,B] = {val map = new JHashMap[A,B](elems.size * 2)for ((k, v) <- elems) map.put(k, v)map
}...
}
Java Maps w/ JavaBridge
import java.util.{Map=>JMap}import JavaBridge._
def easy(): JMap[String,String] = {jmap(“firstName” -> “Jeremy”,“lastName” -> “Cloud”)
}