Upload
tom-flaherty
View
125
Download
3
Embed Size (px)
DESCRIPTION
Getting to Know Scala What Makes Scala Unique for DSLs - Math DSL Multiple Paradigms in One Language - Traits Mixins Applying Scala In The Real World
Citation preview
GETTING TO KNOW SCALA
WHAT MAKES SCALA UNIQUE FOR DSLS
MULTIPLE PARADIGMS IN ONE LANGUAGE
APPLYING SCALA IN THE REAL WORLD
TOM FLAHERTYAxiom Architectures
Scala Paradigms
1
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
SCALA SYNTAX
Scala keywords behave for the most part like their Java counterparts
Syntax Highlighting
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_ : = => <<: <% >: # @
blue - Scala keywords primitives, String, Array and Listpurple - Imported Scala and Java classesbold - Declared classes and methodsgreen - Constants and string literalsgray - Commentsorange – Console output
3
CLASS DECLARATIONSClass Declarations with a parameterized type [ T ]
Class arguments specify the primary constructor
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] {...}
• 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
def method( arg1:Int, ... ) : Int = { } // Method declaration
var num:Int = 0 // Mutable field. Must be initializedval one:Int = iarg // Immutable initialized by constructor
var sum:Int = _ // Let Int default to zerovar ref:AnyRef = _ // Let class reference default to null
DECLARATIONS LIST AND TUPLE
// List[T] An immutable sequence of elements of the same typeval a123 = List(1,2,3) // Type inferred by object List.apply()val b123 = 1 :: 2 :: 3 :: Nil // Nil empty listval 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 matchesval tup = (1,”two”,3.0) // Just enclose element in parenthesesprintln( tup._1, tup._2, tup._3 ) // Element access
5
XMLScala 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
OBJECTClasses in Scala cannot have static members. Instead Scala has object
class Elem[T] {...} // A parameterized Companion classclass 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 matchesval 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” )
A class & its companion object become a Java class in byte code.
7
// 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 unapplyobject Add{ def apply( u:Exp, v:Exp ) : Add = new Add(u,v) def unapply( u:Exp, v:Exp ) : Option[(Exp,Exp)] = Some(u,v) }
• 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
CASE CLASSES
8
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
CLOSURES
// Closure implementations can be assigned to variables. // Return types can be inferredval inc = (i:Int) => i + 1 // inc(1) returns 2val add = (a:Int,b:Int) => a + b // add(2,3) returns 5var mul = (a:Int) => a * n // n is defined outsideval 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 collectionList(1,2,3).map( (i:Int) => i + 1 ) // Returns List(2,3,4)List(1,2,3).map( _ => + 1 ) // Wildcard closure
// Closures declarations are strongly typedfunc1:(Int,Int) => Int // Two Int args. returns Intfunc2:(String) => Unit // String arg and returns nothing
10
PARTIAL FUNCTIONS
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 conversionval 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)
trait PartialFunction[-A,+B] extends (A) => B
11
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 interrogationcase 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 mailboxmessagehandler ! message
ACTORS
12
A JSON PARSER
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 }
start = obj | arrayobj = "{" pair {"," pair}* "}"array = "[" value {"," value}* "]"pair = str ":" value // Name valuestr = StringLitnum = NumericLitvalue = 0bj|array|str|num| "true"|"false"|"null"
BNF
P ~ Q sequential composition
~> <~ sequential ignore
P | Q alternative
opt(P) optional ?
rep(P) repetition * (zero or more)
rep1(P) repetition + (one or more)
repsep(P,Q) interleaved repetition *
repsep1(P,Q) interleaved repetition +
P^^F(R) apply closure F to result R
P^^^ conversion
Scala Parsing
Operators
13
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
A BASIC APPROACH TO DSLSWITH MATH ARITHMETIC EXAMPLES
4. External DSL
BNF ParserA full Language
Similar to Internal DSL
MathParser Example
2. ADTsCase Classes that
define the DSL
by extendingBase Expression
1.Base
ExpressionDefines an Internal DSL with Operators
and Conversions
3. ASTAbstract Syntax Tree
5. Pattern
MatchingCalculate
Differentiate
Example
Parsing Input RepresentationTransformation
Output
15
BASE MATH EXPRESSIONWITH 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
CASE CLASSES FOR ADTS
case class Num( n:double ) extends Exp // wrap doublecase class Var( s:String ) extends Exp // wrap Stringcase 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 // parenthesescase class Dif( u:Exp ) extends Exp // Differentialcase class Err( e:String ) extends Exp // Error
• 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)
17
ABSTRACT SYNTAX TREES*
+ -
“a” 3“b”2
(“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
• 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
18
A MATH EXPRESSION PARSER
object MathParser extends StdTokenParsers{ val lex = new StdLexical; type E = Exp lex.delimiters ++= List( "(",")","+","-","^","/","*" ) def NUM:Parser[E] = numericLit ^^ { (s:String) => Num(s.toDouble)} def VAR:Parser[E] = ident ^^ { (s:String) => Var(s) }
def par:Parser[E] = "(" ~> exp <~ ")" ^^ { (u:E) => Par(u) } def neg:Parser[E] = "-" ~ exp ^^ { case "-" ~ u => Neg(u)} def beg:Parser[E] = NUM | VAR | par | neg def pow:Parser[E] = beg * ( "^" ^^^ { (u:E,v:E) => Pow(u,v) } ) def mul:Parser[E] = pow * ( "*" ^^^ { (u:E,v:E) => Mul(u,v) } ) def div:Parser[E] = mul * ( "/" ^^^ { (u:E,v:E) => Div(u,v) } ) def add:Parser[E] = div * ( "+" ^^^ { (u:E,v:E) => Add(u,v) } ) def sub:Parser[E] = add * ( "-" ^^^ { (u:E,v:E) => Sub(u,v) } ) def exp:Parser[E] = sub | failure("exp") def parse( str:String ) : Exp = { … }}
19
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
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
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) // 9val ed = a/0; val cd = calc(ed,as) // NaNval ep = a~^b; val cp = calc(ep,as) // 216
val xy = x*y; val dxy = d(xy) // y*dx+x*dxval 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
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
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
Scala’s Uniform Class HierarchyAny 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 classesscala.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
TRAITS – TAKE ONE – THE THICK & THIN
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 }
• 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.
26
TRAITS – TAKE TWO – THE SELF TYPE FOR FREE DEPENDENCY INJECTION
Just add the self type to a trait to specify how it interprets this
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
AddOn and BaseAddOn can defined outside of Base to access other
packaged Jar files that you do not want incorporated with Base
class Base {...}trait AddOn { this:Base => ... }
trait More {...}trait AddOnOn { this:Base with More => ... }
val BaseAddOn = new Base with AddOn
27
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 classtrait 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 Exitval r3 = new Real with Log with Txn; r3.doStuff > 1.Txn start 2.Log enter 3.Real stuff 4.Log exit 5.Txn commitval 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.html28
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
MIXINS WITH TRAITS – PROVIDES MULTIPLE VIEWS
ClassTrait
• 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.
Trait
Self
Type
Trait
Thin &
Thick
Trait
Trait
Stackable Modifications / AOP
30
SCALA IN THE REAL WORLD
• REFERENCES
• THE FUTURE OF SCALA
• MY EXPERIENCE
• CONCLUSION
31
REFERENCES
URLs
• Scala Community: www.scala-lang.org
• Artima: www.artima.com
• Planet Scala: www.planetscala.com
IDEs – Netbeans IntelliJ Eclipse32
THE FUTURE OF SCALA
A stable foundation for dynamically type languages
A new language foundation for the JVM• Scala has 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
Site MapLift
Rules
S
State ContextSHTML
Lift
Response
Lift Core
CometMapper / Record
ORM
HTTP
Authentication
JavaScript
API jQuery
Views
Utilities / Scala Platform / J2EE Web Container / JVM
Lift A unique web framework, with:
Wicket like templates and actor concurrency
33
My work in Scala so far has centered on symbolic mathematics
MY SCALA EXPERIENCES SO FAR
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
CONCLUSION
Google code distribution for code, papers and presentations:
http://code.google.com/p/axiom-architectures/
DOSUG Slideshare:
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
EMail: [email protected]
35