1. Axiom Architectures TOM
FLAHERTY
Scala Paradigms
GETTING TO KNOW SCALA
WHAT MAKES SCALA UNIQUE FOR DSLS
MULTIPLE PARADIGMS IN ONE LANGUAGE
APPLYING SCALA IN THE REAL WORLD
1
2. GETTING TO KNOW SCALA
• Martin Odersky
• Java 1.3 compiler was based on his work
• Since 2003
• Sponsored by EPFL
• Friendly and supportive community
• Production ready
• Seamless Java interoperability
• Performance within +- 5% of Java
- JIT is the primary reason
2
3. SCALA SYNTAX
Syntax Highlighting
blue - Scala keywords primitives, String, Array and List
purple - Imported Scala and Java classes
bold - Declared classes and methods
green - Constants and string literals
gray - Comments
orange – Console output
Scala keywords behave for the most part like their Java counterparts
abstract case catch class def do else extends false final
finally for forSome if implicit import lazy match new null
object override package private protected requires return
sealed super this throw trait try true type val var while with
yield
_ : = => <<: <% >: # @
3
4. CLASS DECLARATIONS
Class Declarations with a parameterized type [ T ]
trait Trait[T] {...}
abstract class Abs[T]( i:Int ) extends Trait[T] {...}
class Concrete[T]( i:Int ) extends Abs[T]( i:Int ) {...}
case class Case[T]( i:Int ) extends Abs[T]( i:Int ) {...}
class Mixin[T]( i:Int ) extends Abs[T]( i:Int )
with Trait1[T] with Trait2[T] {...}
Class arguments specify the primary constructor
• The class body is executed at instance creation.
• Primary constructor arguments can be passed up to its super class
• Primary constructor can declare fields with var and val
• Other constructors are declared by def this( i:Int, ... ) = { ... }
• Traits do not have constructors and therefore do not have arg lists
4
5. DECLARATIONS LIST AND TUPLE
def method( arg1:Int, ... ) : Int = { } // Method declaration
var num:Int = 0 // Mutable field. Must be initialized
val one:Int = iarg // Immutable initialized by constructor
var sum:Int = _ // Let Int default to zero
var ref:AnyRef = _ // Let class reference default to null
// List[T] An immutable sequence of elements of the same type
val a123 = List(1,2,3) // Type inferred by object List.apply()
val b123 = 1 :: 2 :: 3 :: Nil // Nil empty list
val aStr = List[String](“1”,”2",”3") // Parameterized
// Tuple Immutable sequence of elements of the different types
// Excellent for multi value returns
// Often used to group elements for pattern matches
val tup = (1,”two”,3.0) // Just enclose element in parentheses
println( tup._1, tup._2, tup._3 ) // Element access
5
6. XML
Scala has intrinsic XML literals for markup
Braces can include arbitrary Scala content
class Person( var name:String, var age:Int )
{
def toXML =
<person>
<name>{name}</name>
<age>{age}</age>
</person>
def fromXML( node:scala.xml.Node ) : Unit node match
{
case <name>{namex}</name> => name = namex.toString
case <age>{agex}</age> => age = agex.toString.toInt
case _ => Error( “Bad XML Syntax”, node.toString )
}
}
6
7. OBJECT
Classes in Scala cannot have static members. Instead Scala has object
A class & its companion object become a Java class in byte code.
class Elem[T] {...} // A parameterized Companion class
class DblElem[T]( d:Double ) extends Elem[T] {}
class IntElem[T]( i:Int ) extends Elem[T] {}
class StrElem[T]( s:String ) extends Elem[T] {}
object Elem // Factory object which hides Elem’s subclasses
{
// A factory method is called when its signature matches
def apply[T]( d:Double ) : Elem[T] = new DblElem[T]( d )
def apply[T]( i:Int ) : Elem[T] = new IntElem[T]( I )
def apply[T]( s:String ) : Elem[T] = new StrElem[T]( s )
}
// Elem.apply[T] method is called when its signature matches
val elem1 = Elem[T]( 5.0 ) // new DblElem[T]( 5.0 )
val elem2 = Elem[T]( 5 ) // new IntElem[T]( 5 )
val elem3 = Elem[T]( “5” ) // new StrElem[T]( “5” )
7
8. CASE CLASSES
• Case classes are regular classes that extract their constructor parameters
• Extraction provides an interrogation mechanism that works particular well with recursion
• Instances are constructed with the companion object apply()
• Instances are deconstructed by extracting the fields with unapply() in pattern matching
• Fields are then externally accessible for interrogation via the Option[T] wrapper
- unapply : Option[(u,v)] is called in pattern matching
- unapply evaluates to either Some[(u,v)] or None both subclasses of Option
- unapply can be modified for custom matching
- For multiple fields (u,v) T is a Tuple
// Scala expands the case class Add( u:Exp, v:Exp ) to:
class Add( val u:Exp, val v:Exp ) //Fields reset to immutable
{
def toString : String = {..} // Class and field name
def equals : Boolean = {..} // Fields compared structurally
def hashCode : Int = {..} // hashCode from fields
}
// Compiler creates a companion object with apply and unapply
object Add
{
def apply( u:Exp, v:Exp ) : Add = new Add(u,v)
def unapply( u:Exp, v:Exp ) : Option[(Exp,Exp)] = Some(u,v)
}
8
9. PATTERN MATCHING
def matchAny( any:Any ) : String = any match
{
case 1 => “one”
case i:Int => “i is an Int” // All ints except 1
case “two” => “2”
case d:Double if d > 12.0 => “double > 12.0”
case d:Double => “double <= 12.0”
case Add(u,v) => (u+v).toString // Extract u,v
case (i:Int,j:Int) => (i+j).toString // A tuple of 2 ints
case (x,y) => x.toString + y.toString // A tuple of Anys
case <tag>{ t }</tag> => t.toString // scala.xml.Node
case head :: tail => head.toString // any is a List
case _ => “no match”
}
val Split = """(d*):(S*)""".r // “”” raw string regex
"12:test" match
{
case Split(id,name) => println( name + " = " + id )
case _ => println( "No match" )
}
> test = 12
9
10. CLOSURES
// Closures declarations are strongly typed
func1:(Int,Int) => Int // Two Int args. returns Int
func2:(String) => Unit // String arg and returns nothing
// Closure implementations can be assigned to variables.
// Return types can be inferred
val inc = (i:Int) => i + 1 // inc(1) returns 2
val add = (a:Int,b:Int) => a + b // add(2,3) returns 5
var mul = (a:Int) => a * n // n is defined outside
val sum = ( nums:Int* ) =>
{ var s=0; for( num <- nums ) { s+=num; } return s }
// The map method applies the closure to every element
// in a collection and returns a new collection
List(1,2,3).map( (i:Int) => i + 1 ) // Returns List(2,3,4)
List(1,2,3).map( _ => + 1 ) // Wildcard closure
10
11. PARTIAL FUNCTIONS
trait PartialFunction[-A,+B] extends (A) => B
A partial function extends closure, by examining its arguments.
Arguments are examined for completeness and multiple returns.
Cases in curly braces are the easiest approach.
Actors use partial functions for message dispatch.
// Simple conversion
val as:(String)=>Double = { case “a”=>6 case “b” =>3 }
// Parsed Expression of a string value to a tuple in JSON
{ case s ~ v => (s,v) }
// Strict type checking of arguments in a tuple in MathParser
(u:Exp,v:Exp) => Add(u,v)
11
12. ACTORS
trait Actor extends Thread with MailBox // Simple Actor
{
def act() : unit // Abstract method
def ! ( msg:Any ) : unit = send(msg) // “!” to send
override def run() : unit = act() // overrides run()
def react( f:PartialFunction[Any,Unit]) : Nothing = {…}
}
// Message ADTs for easy interrogation
case class MsgOne( head:String, body:String )
case class MsgTwo( head:String, body:String )
class MessageHandler extends Actor // Extend Actor like Thread.
{
def act() // Implements act() from Actor instead of run()
{
react { // react makes actors event based
case MsgOne( head, body ) => ...
case MsgTwo( head, body ) => ...
case _ => ... // Log error
} }
} // send (!) message asynchronously to the actor's mailbox
messagehandler ! message
12
13. A JSON PARSER P~Q sequential composition
start = obj | array
BNF ~> <~ sequential ignore
obj = "{" pair {"," pair}* "}" P|Q alternative Scala Parsing
array = "[" value {"," value}* "]" opt(P) optional ? Operators
pair = str ":" value // Name value rep(P) repetition * (zero or more)
str = StringLit rep1(P) repetition + (one or more)
num = NumericLit repsep(P,Q) interleaved repetition *
value = 0bj|array|str|num| repsep1(P,Q) interleaved repetition +
"true"|"false"|"null" P^^F(R) apply closure F to result R
P^^^ conversion
class JSONParser extends StdTokenParsers with ImplicitConversions
{
val lex = new Lexer
lex.reserved ++= List( "true", "false", "null" ) // Keywords
lex.delimiters ++= List( "{", "}", "[", "]", ":", "," )
def start = obj | array
def obj = "{" ~> repsep( pair, "," ) <~ "}"
def array = "[" ~> repsep( value, "," ) <~ "]"
def pair = str ~ (":" ~> value) ^^ { case s ~ v => (s,v) }
def str = accept("string", { case lex.StringLit(s) => s } )
def num = accept("number", { case lex.NumericLit(n)=> n.toDouble })
def value : Parser[Any] = obj | arr | str | num |
"true"^^^true | "false"^^^false | "null"^^^null }
13
14. WHAT MAKES SCALA UNIQUE FOR DSLS
• ADTS (ABSTRACT DATA TYPES)
• OPERATORS & IMPLICIT TYPE CONVERSION FOR INTERNAL DSLS
• BNF GRAMMAR BASED PARSING FOR EXTERNAL DSLS
• FULL TRANSFORMATION WITH PATTERN MATCHING.
14
15. A BASIC APPROACH TO DSLS
WITH MATH ARITHMETIC EXAMPLES
Transformation
Parsing Input Representation
Output
4. External DSL 1.Base 5. Pattern
BNF Parser Expression Matching
A full Language Defines an Internal Calculate
Similar to Internal DSL DSL with Operators Differentiate
MathParser Example and Conversions
Example
2. ADTs
Case Classes that
define the DSL
by extending
Base Expression
3. AST
Abstract Syntax Tree
15
16. BASE MATH EXPRESSION
WITH OPERATOR METHODS FOR SYMBOLIC MATH
abstract class Exp extends with Calculate with Differentiate
{
// Convert Int and double to Num(n) & String to Var(s)
implicit def int2Exp( i:Int ) : Exp = Num(i.toDouble)
implicit def dbl2Exp( d:Double ) : Exp = Num(d)
implicit def str2Exp( s:String ) : Exp = Var(s)
// Infix operators from high to low using Scala precedence
def ~^ ( v:Exp ) : Exp = Pow(this,v) // ~^ high precedence
def / ( v:Exp ) : Exp = Div(this,v)
def * ( v:Exp ) : Exp = Mul(this,v)
def - ( v:Exp ) : Exp = Sub(this,v)
def + ( v:Exp ) : Exp = Add(this,v)
// Prefix operator for negation
def unary_- : Exp = Neg(this)
}
16
17. CASE CLASSES FOR ADTS
• On the surface these ADT case class declarations appear trivial
• No further case class implementations. Scala does it for us.
• The parser and pattern matchers assign meaning base on type
• The arguments define the internal DSL expressions
• Each ADT is a Lambda expression node that combines into an AST
• Construction is done with the companion object apply(u,v) method
• Pattern matching deconstructs ADTs with the object unapply(u,v)
case class Num( n:double ) extends Exp // wrap double
case class Var( s:String ) extends Exp // wrap String
case class Add( u:Exp, v:Exp ) extends Exp // u + v infix
case class Sub( u:Exp, v:Exp ) extends Exp // u – v infix
case class Mul( u:Exp, v:Exp ) extends Exp // u * v infix
case class Div( u:Exp, v:Exp ) extends Exp // u / v infix
case class Pow( u:Exp, v:Exp ) extends Exp // u ^ v infix
case class Neg( u:Exp ) extends Exp // -u prefix
case class Par( u:Exp ) extends Exp // parentheses
case class Dif( u:Exp ) extends Exp // Differential
case class Err( e:String ) extends Exp // Error
17
18. ABSTRACT SYNTAX TREES
*
+ -
“a” 2 “b” 3
• All leaf nodes are either Var(v:String) or Num(d:Double)
• Branch nodes are ADTs that can be and infix “+” or the prefix Add
• Extracted contents (u,v) of an ADT i.e. Add(u,v) are the child nodes
• Branch child nodes are processed with a recursive method call
• Operators cannot be used on pattern side, only processing side
• Prefix form does not require Par(), but infix does.
• ADT prefix and infix can be mixed and checked by the compiler
(“a”+2)*(“b”-3) = Mul(Add(Var(“a”),Num(2)),Sub(Var(“b”),Num(3)))
(“a”+2)*(“b”-3) = Mul(Add(“a”,2),Sub(“b”,3)) // Implicit
(“a”+2)*(“b”-3) = Mul(“a”+2,“b”+3)) // Infix
18
20. CALCULATION WITH RECURSIVE PATTERN MATCHING
trait Calculate
{
this:Exp => // this is Exp
val NaN : Double = Math.NaN_DOUBLE
type Assign = (String) => Double // { “a”=>3.0, “b”=>6.0 }
def calc( e:Exp, a:Assign ) : Double = e match
{
case Num(d) => d // Unwrap the double
case Var(s) => a(s) // Return double assigned to variable
case Add(u,v) => calc(u,a) + calc(v,a) // Recurse
case Sub(u,v) => calc(u,a) - calc(v,a) // Recurse
case Mul(u,v) => calc(u,a) * calc(v,a) // Recurse
case Div(u,v)=>val d=calc(v,a) if d==0.0 NaN else calc(u,a)/d
case Pow(u,v) => Math.pow( calc(u,a), calc(v,a) )
case Neg(u) => -calc(u,a) // Recurse
case Par(u) => calc(u,a) // Strip off parentheses
case Dif(u) => NaN
case Err(u) => NaN
case _ => NaN
}
}
20
21. DIFFERENTIATION WITH PATTERN MATCHING
trait Differentiate
{
this:Exp => // this is Exp with all its operators & implicits
def d( e:Exp ) : Exp = e match
{
case Num(n) => 0 // diff of constant zero
case Var(s) => Dif(Var(s)) // x becomes dx
case Add(u,v) => d(u) + d(v) // Add(d(u),d(v))
case Sub(u,v) => d(u) - d(v) // Sub(d(u),d(v))
case Mul(u,v) => v*d(u)+u*d(v)
case Div(u,v) => (v*d(u)-u*d(v))/(v~^2)
case Pow(u,v) => v * u~^(v-1) * d(u)
case Neg(u) => -d(u) // Neg(d(u))
case Par(u) => Par(d(u))
case Dif(u) => Dif(d(u)) // 2rd dif
case Err(u) => Dif(Err(e))
case _ => Err(Dif(e))
}
}
21
22. RUNS
val a=”a”; val b=”b”; val x=”x”; val y=”y”; val z=”z”
val as:Assign = { case a=>6 case b =>3 } // Partial function
val ea = a+b; val ca = calc(ea,as) // 9
val ed = a/0; val cd = calc(ed,as) // NaN
val ep = a~^b; val cp = calc(ep,as) // 216
val xy = x*y; val dxy = d(xy) // y*dx+x*dx
val x3 = x~^3; val dx3 = d(x3) // 3*x^(3-1)*dx
val sx3 = sim(dx3) // 3*x^2dx
val xy3 = (x+y)^3; val dxy3 = d(xy3) // 3*(x+y)^(3-1)*(dx+dy)
val sxy3 = sim(dxy3) // 3*(x+y)^2*(dx+dy)
val xyz = x*y*z; val dxyz = d(xyz) // z*(y*dx+x*dy)+x*y*dz
val sxyz = sim(dxyx) // z*y*dx+z*x*dy+x*y*dz
22
23. THE BEAUTY OF IMMUTABILITY
Immutable vals are used in ADT arguments
The use of val insures:
• ADT contents are never copied, just re-referenced.
• Safety because the references cannot change.
• The identity of the contents is preserved.
This is liberating because:
The contents of ADTs be extracted at will to construct new ADTs that
produce new represenentions and meaning by rewrapping the contents.
This is the intent of transformation with pattern matching.
23
24. MULTIPLE PARADIGMS
Scala’s Uniform Class Hierarchy
For Primitives as first class objects
TRAITS – TAKE ONE – THE THICK & THIN
FOR HIGH REUSE
TRAITS – TAKE TWO – THE SELF TYPE
FOR FREE DEPENDENCY INJECTION
TRAITS - TAKE THREE – STACKABLE MODIFICATIONS
FOR AOP INTERCEPTORS
MIXINS WITH TRAITS
FOR MULTIPLE VIEWS
24
25. Scala’s Uniform Class Hierarchy
Any
AnyVal // Scala's base class for Java primitives and unit
Double Float Long Int Short Char Byte Boolean Unit
AnyRef // java.lang.Object
String // java.lang.String
(all other Java Classes ...)
(all other Scala Classes ...)
Iterable // base Class for all Scala collections
Seq // base Class for all ordered collections
Array // compiles to Java arrays []
List // Immutable list for pattern matching
scala.Null // is a subtype of all AnyRef classes
scala.Nothing // is a subtype of all Any classes
• Primitives are treated as "first class objects" not wrapped into classes.
• array:Array[Int](len) is mapped to Int[len] array in Java
• Java's primitive class wrappers are not needed.
• Scala intelligently applies base methods to primitives, even constants.
• 5.toString "5".toInt
25
26. TRAITS – TAKE ONE – THE THICK & THIN
• Traits are similar to interfaces in Java
• As with interfaces a Scala class can mix in any number of traits.
• The mixin class can be assigned to or passed as any if its trait types.
• Traits can not be constructed so traits do not have primary constructor arguments.
• Traits with purely abstract methods map directly to Java interfaces.
• Unlike Java interfaces, Scala traits have concrete implementations of methods.
• The Thick and Thin Approach to Trait Design - Highest Reuse
• First define a small number of abstract methods - the Thin part.
• Then implement a potentially large number of concrete methods - the Thick part.
• All concrete members implemented in terms of the abstract members.
trait Ordered[A] // Parameterized with type [A]
{
// The Thin abstract part. Must implemented in base class
def compare( a:A ) : Int
// The Thick part. Concrete methods declared with operators.
// All based on the abstract method compare(a)
def < ( a:A ) : Boolean = compare(a) < 0
def > ( a:A ) : Boolean = compare(a) > 0
def <= ( a:A ) : Boolean = compare(a) <= 0
def >= ( a:A ) : Boolean = compare(a) >= 0 }
26
27. TRAITS – TAKE TWO – THE SELF TYPE
FOR FREE DEPENDENCY INJECTION
Just add the self type to a trait to specify how it interprets this
class Base {...}
trait AddOn { this:Base => ... }
trait More {...}
trait AddOnOn { this:Base with More => ... }
AddOn is a coupled facet of Base with access to Base fields and methods
AddOn’s concrete methods builds on Base rather than abstract defs
AddOn can be “injected” during construction
val BaseAddOn = new Base with AddOn
AddOn and BaseAddOn can defined outside of Base to access other
packaged Jar files that you do not want incorporated with Base
27
28. AOP WITH TRAITS - TAKE THREE – STACKABLE MODIFICATIONS
trait Stuff { def doStuff : Unit } // doStuff abstract
class Real { def doStuff=println("Real Stuff")} extends Stuff
// abstract override tells the compiler that things are OK
// The call to super will reach a concrete class
trait Log extends Stuff
{ abstract override def doStuff : Unit =
{ println("Log enter"); super.doStuff; println("Log exit")}}
trait Txn extends Stuff
{ abstract override def doStuff : Unit =
{ println("Txn start");
try { super.doStuff; println("Txn commit"); }
catch { case e: Exception => println("Txn rollback") } } }
val r2 = new Real with Log; r2.doStuff
> 1.Log enter 2.Real stuff 3.Log Exit
val r3 = new Real with Log with Txn; r3.doStuff
> 1.Txn start 2.Log enter 3.Real stuff 4.Log exit 5.Txn commit
val r4 = new Real with Txn with Log; r4.doStuff
> 1.Log enter 2.Txn start 3.Real stuff 5.Txn commit 6.Log exit
* From: http://jonasboner.com/2008/02/06/aop-style-mixin-composition-stacks-in-scala.html
28
29. AOP – RETRY EXAMPLE
class Except extends Stuff
{ def doStuff : Unit =
{ println(“Except”); throw new RuntimeException(“msg”)}}
trait Retry extends Stuff
{ abstract override def doStuff : Unit =
{ var n = 0; var retry = true
while( retry )
{
try { super.doStuff; retry = false; }
catch { case e: Exception =>
if( n < 3 ) { n += 1; println("Fail retry:" + n) }
else { retry = false; throw e } }
} } }
val r5 = new Except with Retry with Txn with Log; r5.doStuff
> 1.Log enter 2.Txn start
3.Except 4.Fail retry:1
5.Except 6.Fail retry:2
7.Except 8.Fail retry:3
9.Txn rollback 10.Log exit
* From: http://jonasboner.com/2008/02/06/aop-style-mixin-composition-stacks-in-scala.html
29
30. MIXINS WITH TRAITS – PROVIDES MULTIPLE VIEWS
• A great design strategy for breaking up a big Class.
• Mixins are type safe and checked by the compiler.
• Methods are called in an orderly predicable fashion through linearization.
• So the order of mixins is significant.
• The secret is how Scala manages this and super
• Clients only need to interface to a particular Trait.
• Each mixin Trait then provides unique facet to the client.
• Traits can be mixed in at runtime during construction.
• Traits can be stacked and invoked like AOP interceptors.
Stackable Modifications / AOP
Trait
Thin & Trait
Class
Thick
Trait
Trait Trait
Self
Type
30
31. SCALA IN THE REAL WORLD
• REFERENCES
• THE FUTURE OF SCALA
• MY EXPERIENCE
• CONCLUSION
31
33. THE FUTURE OF SCALA
Lift A unique web framework, with:
Wicket like templates and actor concurrency
Lift S
Site Map SHTML Views
Rules State Context
Lift Core
Lift
Comet
Response Mapper / Record HTTP JavaScript
ORM Authentication API jQuery
Utilities / Scala Platform / J2EE Web Container / JVM
A stable foundation for dynamically type languages
A new language foundation for the JVM
• Scalahas implemented practically all of the new features
being discussed for upgrading Java.
• Closures, parameterized types done right, flexible packaging
“If I were to pick a language to use today other than Java, it would be Scala” - James Gosling
33
34. MY SCALA EXPERIENCES SO FAR
My work in Scala so far has centered on symbolic mathematics
I am currently converting my interactive drawing application to Scala
• Visio and SVG shapes and connectors
• Text editing
• CSS and SVG styles
I am experimenting with DSLs and ADTs for Enterprise Architecture
34
35. CONCLUSION
I want to express my appreciation to:
Derek Chen-Becker Mathew McCullough Frederick Jean
Daniel Glauser John Lowe Paul Gillen
for stimulating emails, reviews, conversation and support
Google code distribution for code, papers and presentations:
http://code.google.com/p/axiom-architectures/
EMail: Thomas.EdmundFlaherty@gmail.com
DOSUG Slideshare:
35