Scala Paradigms

Download Scala Paradigms

Post on 27-Jan-2015

106 views

Category:

Technology

3 download

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

TRANSCRIPT

<ul><li> 1. Axiom ArchitecturesTOM FLAHERTY Scala Paradigms GETTING TO KNOW SCALA WHAT MAKES SCALA UNIQUE FOR DSLS MULTIPLE PARADIGMS IN ONE LANGUAGE APPLYING SCALA IN THE REAL WORLD1 </li></ul><p> 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 Listpurple - Imported Scala and Java classesbold - Declared classes and methodsgreen- Constants and string literalsgray - Commentsorange Console output Scala keywords behave for the most part like their Java counterpartsabstract 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 _ : = =&gt; age = agex.toString.toInt case _ =&gt; Error( Bad XML Syntax, node.toString ) } }6 7. OBJECTClasses in Scala cannot have static members. Instead Scala has objectA class &amp; 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 Elems subclasses { // A factory method is called when its signature matchesdef 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=&gt; one case i:Int=&gt; i is an Int // All ints except 1 case two=&gt; 2 case d:Double if d &gt; 12.0 =&gt; double &gt; 12.0 case d:Double =&gt; double (u+v).toString // Extract u,v case (i:Int,j:Int) =&gt; (i+j).toString // A tuple of 2 ints case (x,y) =&gt; x.toString + y.toString // A tuple of Anys case { t } =&gt; t.toString// scala.xml.Node case head :: tail =&gt; head.toString // any is a List case _=&gt; no match } val Split = """(d*):(S*)""".r // raw string regex "12:test" match { case Split(id,name) =&gt; println( name + " = " + id ) case _=&gt; println( "No match" ) } &gt; test = 12 9 10. CLOSURES // Closures declarations are strongly typed func1:(Int,Int) =&gt; Int // Two Int args. returns Int func2:(String) =&gt; Unit // String arg and returns nothing // Closure implementations can be assigned to variables. // Return types can be inferred val inc = (i:Int) =&gt; i + 1 // inc(1) returns 2 val add = (a:Int,b:Int) =&gt; a + b // add(2,3) returns 5 var mul = (a:Int) =&gt; a * n // n is defined outside val sum = ( nums:Int* ) =&gt; { var s=0; for( num i + 1 ) // Returns List(2,3,4) List(1,2,3).map( _ =&gt; + 1 ) // Wildcard closure10 11. PARTIAL FUNCTIONS trait PartialFunction[-A,+B] extends (A) =&gt; BA 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)=&gt;Double = { case a=&gt;6 case b =&gt;3 }// Parsed Expression of a string value to a tuple in JSON { case s ~ v =&gt; (s,v) }// Strict type checking of arguments in a tuple in MathParser (u:Exp,v:Exp) =&gt; 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 ) =&gt; ... case MsgTwo( head, body ) =&gt; ... case _=&gt; ... // Log error } } } // send (!) message asynchronously to the actor's mailbox messagehandler ! message12 13. A JSON PARSERP~Q sequential compositionstart = obj | array BNF~&gt; repsep( pair, "," ) repsep( value, "," ) value) ^^ { case s ~ v =&gt; (s,v) } def str = accept("string", { case lex.StringLit(s) =&gt; s } ) def num = accept("number", { case lex.NumericLit(n)=&gt; 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 &amp; 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 EXAMPLESTransformationParsing InputRepresentationOutput 4. External DSL 1.Base 5. Pattern BNF ParserExpression MatchingA full LanguageDefines an InternalCalculate Similar to Internal DSL DSL with OperatorsDifferentiateMathParser Exampleand ConversionsExample 2. ADTsCase Classes that define the DSLby extendingBase Expression 3. AST Abstract Syntax Tree15 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) &amp; 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 precedencedef ~^ ( v:Exp ) : Exp = Pow(this,v) // ~^ high precedencedef / ( 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 negationdef 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 // -uprefix case class Par( u:Exp )extends Exp // parentheses case class Dif( u:Exp )extends Exp // Differential case class Err( e:String ) extends Exp // Error17 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 19. A MATH EXPRESSION PARSER object MathParser extends StdTokenParsers {val lex = new StdLexical; type E = Explex.delimiters ++= List( "(",")","+","-","^","/","*" ) def NUM:Parser[E] = numericLit ^^ { (s:String) =&gt; Num(s.toDouble)}def VAR:Parser[E] = ident^^ { (s:String) =&gt; Var(s) } def par:Parser[E] = "(" ~&gt; exp Par(u) }def neg:Parser[E] = "-" ~ exp ^^ { case "-" ~ u =&gt; Neg(u)} def beg:Parser[E] = NUM | VAR | par | negdef pow:Parser[E] = beg * ( "^" ^^^ { (u:E,v:E) =&gt; Pow(u,v) } )def mul:Parser[E] = pow * ( "*" ^^^ { (u:E,v:E) =&gt; Mul(u,v) } )def div:Parser[E] = mul * ( "/" ^^^ { (u:E,v:E) =&gt; Div(u,v) } )def add:Parser[E] = div * ( "+" ^^^ { (u:E,v:E) =&gt; Add(u,v) } )def sub:Parser[E] = add * ( "-" ^^^ { (u:E,v:E) =&gt; Sub(u,v) } )def exp:Parser[E] = sub | failure("exp")def parse( str:String ) : Exp = { } } 19 20. CALCULATION WITH RECURSIVE PATTERN MATCHING trait Calculate { this:Exp =&gt; // this is Exp val NaN : Double = Math.NaN_DOUBLE type Assign = (String) =&gt; Double // { a=&gt;3.0, b=&gt;6.0 } def calc( e:Exp, a:Assign ) : Double = e match{case Num(d) =&gt; d// Unwrap the doublecase Var(s) =&gt; a(s) // Return double assigned to variablecase Add(u,v) =&gt; calc(u,a) + calc(v,a) // Recursecase Sub(u,v) =&gt; calc(u,a) - calc(v,a) // Recursecase Mul(u,v) =&gt; calc(u,a) * calc(v,a) // Recursecase Div(u,v)=&gt;val d=calc(v,a) if d==0.0 NaN else calc(u,a)/dcase Pow(u,v) =&gt; Math.pow( calc(u,a), calc(v,a) )case Neg(u) =&gt; -calc(u,a) // Recursecase Par(u) =&gt; calc(u,a) // Strip off parenthesescase Dif(u) =&gt; NaNcase Err(u) =&gt; NaNcase _=&gt; NaN} }20 21. DIFFERENTIATION WITH PATTERN MATCHING trait Differentiate { this:Exp =&gt; // this is Exp with all its operators &amp; implicitsdef d( e:Exp ) : Exp = e match { case Num(n) =&gt; 0 // diff of constant zero case Var(s) =&gt; Dif(Var(s)) // x becomes dx case Add(u,v) =&gt; d(u) + d(v) // Add(d(u),d(v)) case Sub(u,v) =&gt; d(u) - d(v) // Sub(d(u),d(v)) case Mul(u,v) =&gt;v*d(u)+u*d(v) case Div(u,v) =&gt; (v*d(u)-u*d(v))/(v~^2) case Pow(u,v) =&gt; v * u~^(v-1) * d(u) case Neg(u) =&gt; -d(u) // Neg(d(u)) case Par(u) =&gt; Par(d(u)) case Dif(u) =&gt; Dif(d(u)) // 2rd dif case Err(u) =&gt; Dif(Err(e)) case _=&gt; Err(Dif(e)) } }21 22. RUNSval a=a; val b=b; val x=x; val y=y; val z=z val as:Assign = { case a=&gt;6 case b =&gt;3 } // Partial functionval 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) // 216val xy = x*y; val dxy = d(xy) // y*dx+x*dx val x3 = x~^3; val dx3 = d(x3)// 3*x^(3-1)*dxval sx3 = sim(dx3) // 3*x^2dxval 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*dzval sxyz = sim(dxyx) // z*y*dx+z*x*dy+x*y*dz 22 23. THE BEAUTY OF IMMUTABILITY Immutable vals are used in ADT argumentsThe 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 Scalas Uniform Class HierarchyFor Primitives as first class objects TRAITS TAKE ONE THE THICK &amp; THINFOR HIGH REUSE TRAITS TAKE TWO THE SELF TYPEFOR FREE DEPENDENCY INJECTION TRAITS - TAKE THREE STACKABLE MODIFICATIONSFOR AOP INTERCEPTORS MIXINS WITH TRAITSFOR MULTIPLE VIEWS 24 25. Scalas 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 matchingscala.Null// is a subtype of all AnyRef classes scala.Nothing // is a subtype of all Anyclasses 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 &amp; 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 classdef compare( a:A ) : Int// The Thick part. Concrete methods declared with operators. // All based on the abstract method compare(a)def &lt; ( a:A ) : Boolean = compare(a) &lt; 0def &gt; ( a:A ) : Boolean = compare(a) &gt; 0def = 0 } 26 27. TRAITS TAKE TWO THE SELF TYPE FOR FREE DEPENDENCY INJECTIONJust add the self type to a trait to specify how it interprets thisclass Base {...}trait AddOn{ this:Base =&gt; ... } trait More{...}trait AddOnOn { this:Base with More =&gt; ... }AddOn is a coupled facet of Base with access to Base fields and methods AddOns concrete methods builds on Base rather than abstract defs AddOn can be injected during constructionval BaseAddOn = new Base with AddOnAddOn 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 abstractclass Real { def doStuff=println("Real Stuff")} extends Stuff// abstract override tells the compiler that things are OK // The call to super will reach...</p>