Scala for Java Developers - Intro
-
Upload
david-copeland -
Category
Technology
-
view
4.338 -
download
3
description
Transcript of Scala for Java Developers - Intro
ScalaWTF is it? The new hotness or yet another functional fad?
1
Scalable LanguageThat’s why it’s pronounced Ska-Lah ?!??!
2
Statically Typed
3
Runs on the JVM
4
Hybrid Language:Object-Oriented + Functional
5
Why?
6
Make Java more DRY
7
Allow easy creation of DSLs
8
Conciseness of Ruby +Safety of Static Typing
9
No meta-programming but...very liberal syntax
10
12
13
14
15
16
Type Inference
17
List<String> oyVey = new ArrayList<String>();
18
List<String> oyVey = new ArrayList<String>(); var hellsYeah = new ArrayList[String];
19
List<String> oyVey = new ArrayList<String>();var hellsYeah = new ArrayList[String]; val booyah = 45; // it’s an Int
20
List<String> oyVey = new ArrayList<String>();var hellsYeah = new ArrayList[String]; val booyah = 45; // it’s an Intval doh = booyah / 3; // not a Float!!
21
List<String> oyVey = new ArrayList<String>();var hellsYeah = new ArrayList[String]; val booyah = 45; // it’s an Intval doh = booyah / 3; // not a Float!!val better:Float = booyah / 3;
22
List<String> oyVey = new ArrayList<String>();var hellsYeah = new ArrayList[String]; val booyah = 45; // it’s an Intval doh = booyah / 3; // not a Float!!val better:Float = booyah / 3;
// return type is java.lang.Stringdef fullName = { first + “,” + last; }
23
List<String> oyVey = new ArrayList<String>();var hellsYeah = new ArrayList[String]; val booyah = 45; // it’s an Intval doh = booyah / 3; // not a Float!!val better:Float = booyah / 3;
// return type is java.lang.Stringdef fullName = { first + “,” + last; }
If the type seems obvious to you...
24
List<String> oyVey = new ArrayList<String>();var hellsYeah = new ArrayList[String]; val booyah = 45; // it’s an Intval doh = booyah / 3; // not a Float!!val better:Float = booyah / 3;
// return type is java.lang.Stringdef fullName = { first + “,” + last; }
If the type seems obvious to you...Scala can usually figure it out, too
25
Liberal Syntax
26
val semicolons = “don’t need ‘em”val but = “if it’s not clear”; val you = “need them”
27
val semicolons = “don’t need ‘em”val but = “if it’s not clear”; val you = “need them”
def braces(b:Boolean) = if (b) “needed for multiple expressions” else “you don’t need them”
28
val semicolons = “don’t need ‘em”val but = “if it’s not clear”; val you = “need them”
def braces(b:Boolean) = if (b) “needed for multiple expressions” else “you don’t need them”
• if/else is a singular expression
29
class Read(d:Date, val usage:Int) { def +(other:Int) = new Read(d,usage + other) override def toString = d + “:” + u
}
30
class Read(d:Date, val usage:Int) { def +(other:Int) = new Read(d,usage + other) override def toString = d + “:” + u
}
val f = new Read(new Date,10)val g = f + 20
31
class Read(d:Date, val usage:Int) { def +(other:Int) = new Read(d,usage + other) override def toString = d + “:” + u
}
val f = new Read(new Date,10)val g = f + 20val gee = f.+(20) // yuck, but legal
32
class Read(d:Date, val usage:Int) { def +(other:Int) = new Read(d,usage + other) override def toString = d + “:” + u def later(ms:Int) = new Read(d + ms,usage)}
val f = new Read(new Date,10)val g = f + 20val gee = f.+(20) // yuck, but legalval l = f later (3600 * 1000)
33
Mixins (Traits)
34
abstract class SaneCompare[T] { def <(other:T):Boolean def >(other:T) = !(this < other) def <=(other:T) = this == other || this < other def >=(other:T) = this == other || this > other}class Read(d:Date,u:Int) extends SaneCompare[Read] { val usage = u def <(other:Read) = usage < other.usage }if (someRead >= someOtherRead) println(”finally!”)
35
abstract class SaneCompare[T] { def <(other:T):Boolean def >(other:T) = !(this < other) def <=(other:T) = this == other || this < other def >=(other:T) = this == other || this > other}class Read(d:Date,u:Int) extends AbstractEntity { val usage = u def <(other:Read) = usage < other.usage }// hmmm....what now?
36
abstract class SaneCompare[T] { def <(other:T):Boolean def >(other:T) = !(this < other) def <=(other:T) = this == other || this < other def >=(other:T) = this == other || this > other}class Read(d:Date,u:Int) extends AbstractEntity { val usage = u def <(other:Read) = usage < other.usage }// AbstractEntity extends SaneCompare?// God class above AbstractEntity subsumes it?
37
abstract class SaneCompare[T] { def <(other:T):Boolean def >(other:T) = !(this < other) def <=(other:T) = this == other || this < other def >=(other:T) = this == other || this > other}class Read(d:Date,u:Int) extends AbstractEntity { val usage = u def <(other:Read) = usage < other.usage }// AbstractEntity extends SaneCompare?// God class above AbstractEntity subsumes it?// these are different concepts entirely
38
trait SaneCompare[T] { def <(other:T):Boolean def >(other:T) = !(this < other) def <=(other:T) = this == other || this < other def >=(other:T) = this == other || this > other}class Read(d:Date,u:Int) extends AbstractEntity with SaneCompare[Read] { val usage = u def <(other:Read) = usage < other.usage }// now we have both!
39
Traits
•Separate Concerns
40
Traits
•Separate Concerns
•Precedence is based on declaration order
41
Traits
•Separate Concerns
•Precedence is based on declaration order
•All abstract – just like Java interface
•None abstract – multiple inheritance
42
Functions
43
val reads = getSomeElectricReads // List[Read]
reads.sort( (a,b) => a.date.compareTo(b.date) < 0 )
44
val reads = getSomeElectricReads // List[Read]
reads.sort( (a,b) => a.date.compareTo(b.date) < 0 )
45
val reads = getSomeElectricReads // List[Read]
reads.sort( (a,b) => a.date.compareTo(b.date) < 0 )
def sort(compFunc:(Read,Read) => Boolean)
46
val reads = getSomeElectricReads // List[Read]
reads.sort( (a,b) => a.date.compareTo(b.date) < 0 )
def sort(compFunc:(Read,Read) => Boolean)
Function2[Read,Read,Boolean]
47
val reads = getSomeElectricReads // List[Read]
reads.sort( (a,b) => a.date.compareTo(b.date) < 0 )
def sort(compFunc:(Read,Read) => Boolean)
Function2[Read,Read,Boolean]
def sortReads(a:Read,b:Read) = a.date.compareTo(b.date) < 0
reads.sort(sortReads) // could also use a method // just keep in mind...
48
Methods are not Functions
49
Methods are not Functionsbut can be passed as Functions
50
List Processing
51
class State(val code:String,val desc:String)val states = getAllStates
// returns a List[String] with the codesstates.map( (state) => state.code )
// returns true if any state has a code of “DC”states.exists( (state) => state.code == “DC” )
// returns the state with the desc of “Hawaii”states.find( (state) => state.desc == “Hawaii” )
// returns a List[State] if states with descs matchingstates.filter( (state) => state.desc.startsWith(”V”) )
// Tons more
52
Complete Access to JDK and Java libraries
53
val s = new SingletonMetadataAwareAspectInstanceFactory()val foo = s.getOrderForAspectClass(classOf[FooBar])
54
import java.util.Observerimport java.util.Observable
class AwesomeObserver extends Observer { def update(o:Observable, arg:Any) = if (o hasChanged) println(arg.asInstanceOf[MeterRead].date)}
55
import java.util.Observerimport java.util.Observable
class AwesomeObserver extends Observer { def update(o:Observable, arg:Any) = if (o hasChanged) println(arg.asInstanceOf[MeterRead].date)}
56
import java.util.Observerimport java.util.Observable
class AwesomeObserver extends Observer { def update(o:Observable, arg:Any) = if (o hasChanged) println(arg.asInstanceOf[MeterRead].date)}
57
import java.util.Observerimport java.util.Observable
class AwesomeObserver extends Observer { def update(o:Observable, arg:Any) = if (o hasChanged) println(arg.asInstanceOf[MeterRead].date)}
58
Goodbye Java’s Baggage
59
No primitives
60
Proper F’ing Propertiesclass ServicePoint(val id:String,var name:String)
val sp = new ServicePoint(”foo”,”The Foo House”)println(sp.id) // get, but no setprintln(sp.name)sp.name = “Thy Foo Haüs”
61
Proper F’ing Propertiesclass ServicePoint(val id:String,private var _name:String) { def name = _name.toUpperCase def name_=(newName:String) = _name = newName}
val sp = new ServicePoint(”foo”,”The Foo House”)sp.name = “Thy Foo Haüs”println(sp.name) // prints THY FOO HAÜS
62
¡Adiós Checked Exceptions!def readFile(f:File) = { val is = new FileInputStream(f) var ch = f.read while (ch != -1) { print(ch) ch = f.read }} // Wow, that was clean!
63
¡Adiós Checked Exceptions!def readFile(f:File) = { try { val is = new FileInputStream(f) var ch = f.read() while (ch != -1) { print(ch) ch = f.read } } catch { case fnfe:FileNotFoundException => println(f + ” not found, dude: ” + fnfe) } // All others bubble out, even if checked in Java}
64
Can I get a closure?class Logger(level:Int) { def debug(message: => String) = log(20,message) def info(message: => String) = log(10,message) def log(logLevel:Int, message: => String) = { if (level >= logLevel) println(message) }}val log = new Logger(10)log.debug(“Got read for “ + read.date + “ with usage “ + read.usage)read.usage = 44log.info(if read.usage < 10 “low read” else “high read”)
65
Can I get a closure?class Logger(level:Int) { def debug(message: => String) = log(20,message) def info(message: => String) = log(10,message) def log(logLevel:Int, message: => String) = { if (level >= logLevel) println(message) }}val log = new Logger(10)log.debug(“Got read for “ + read.date + “ with usage “ + read.usage)read.usage = 44log.info(if read.usage < 10 “low read” else “high read”)
66
Can I get a closure?class Logger(level:Int) { def debug(message: => String) = log(20,message) def info(message: => String) = log(10,message) def log(logLevel:Int, message: => String) = { if (level >= logLevel) println(message) }}val log = new Logger(10)log.debug(“Got read for “ + read.date + “ with usage “ + read.usage)read.usage = 44log.info(if read.usage < 10 “low read” else “high read”)
67
Can I get a closure?class Logger(level:Int) { def debug(message: => String) = log(20,message) def info(message: => String) = log(10,message) def log(logLevel:Int, message: => String) = { if (level >= logLevel) println(message) }}val log = new Logger(10)log.debug(“Got read for “ + read.date + “ with usage “ + read.usage)read.usage = 44log.info(if read.usage < 10 “low read” else “high read”)
68
Can I get a closure?class Logger(level:Int) { def debug(message: => String) = log(20,message) def info(message: => String) = log(10,message) def log(logLevel:Int, message: => String) = { if (level >= logLevel) println(message) }}val log = new Logger(10)log.debug(“Got read for “ + read.date + “ with usage “ + read.usage)read.usage = 44log.info(if read.usage < 10 “low read” else “high read”)
69
“Literals”val triStateArea = List(”MD”,”DC”,”VA”)val theSouth = Map(”MD” -> true,”DC” -> false, ”VA” ->true)val perlCirca96 = (true,”Tuples rule”)val (hasTuples,message) = perlCirca96
70
“Literals”val triStateArea = List(”MD”,”DC”,”VA”)val theSouth = Map(”MD” -> true,”DC” -> false, ”VA” ->true)val perlCirca96 = (true,”Tuples rule”)val (hasTuples,message) = perlCirca96
These are actually API calls
71
“Literals”val triStateArea = List(”MD”,”DC”,”VA”)val theSouth = Map(”MD” -> true,”DC” -> false, ”VA” ->true)val perlCirca96 = (true,”Tuples rule”)val (hasTuples,message) = perlCirca96
This is done by the compilercreates a Tuple2[Boolean, String]
72
“Literals”class Read(val id:Int, val usage:Int, val age:Int)
object Read { def apply(id:Int,usage:Int,age:Int) = new Read(id,usage,age)}
val read = Read(4,10,33)
73
“Literals”class Read(val id:Int, val usage:Int, val age:Int)
object Read { def apply(id:Int,usage:Int,age:Int) = new Read(id,usage,age)}
val read = Read(4,10,33) // shortcut via compilerval read2 = Read.apply(4,10,33)
74
Crazy Awesome - Pattern Matchingdef fromEnglish(string:String) = string match { case “none” => 0 case “one” => 1 case _ => 2}
75
Crazy Awesome - Pattern Matchingdef toUML(obj:Any) = obj match { case 0 => “0” case 1 => “0..1” case n:Int => “0..” + n case true => “1” case false => “0” case “many” => “0..*” case _ => “0..*”}
76
Crazy Awesome - Pattern Matchingsealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
def proRate(read:Read) = read match { case AMIRead(d,usage,duration) => usage / duration case BillingRead(d,usage,days,c) => usage / days case CorrectedRead(BillingRead(d,oldUsage,days,c),usage) => (oldUsage + usage) / days}
77
Crazy Awesome - Pattern Matchingsealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
def proRate(read:Read) = read match { case AMIRead(d,usage,duration) => usage / duration case BillingRead(d,usage,days,c) => usage / days case CorrectedRead(BillingRead(d,oldUsage,days,c),usage) => (oldUsage + usage) / days}
• properties • equals/toString/hashCode• “extractor” • no need for “new”
78
Crazy Awesome - Pattern Matchingsealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
def proRate(read:Read) = read match { case AMIRead(d,usage,duration) => usage / duration case BillingRead(d,usage,days,c) => usage / days case CorrectedRead(BillingRead(d,oldUsage,days,c),usage) => (oldUsage + usage) / days}
79
Crazy Awesome - Pattern Matchingsealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
def proRate(read:Read) = read match { case AMIRead(d,usage,duration) => usage / duration case BillingRead(d,usage,days,c) => usage / days case CorrectedRead(BillingRead(d,oldUsage,days,c),usage) => (oldUsage + usage) / days}
80
Crazy Awesome - Pattern Matchingsealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
def proRate(read:Read) = read match { case AMIRead(d,usage,duration) => usage / duration case BillingRead(d,usage,days,c) => usage / days case CorrectedRead(BillingRead(d,oldUsage,days,c),usage) => (oldUsage + usage) / days}
81
Crazy Awesome - Pattern Matchingsealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
def proRate(read:Read) = read match { case AMIRead(d,usage,duration) => usage / duration case BillingRead(d,usage,days,c) => usage / days case CorrectedRead(BillingRead(d,oldUsage,days,c),usage) => (oldUsage + usage) / days}
82
Crazy Awesome - Implicitssealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
implicit def readToSeconds(r:Read):Int = r.date.getTime / 1000
def areConsecutive(from:Read, to:Read) = (from - to) <= from.duration
83
Crazy Awesome - Implicitssealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
implicit def readToSeconds(r:Read):Int = r.date.getTime / 1000
def areConsecutive(from:Read, to:Read) = (from - to) <= from.duration
Have a Read, but need an Int
84
Crazy Awesome - Implicitssealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
implicit def readToSeconds(r:Read):Int = r.date.getTime / 1000
def areConsecutive(from:Read, to:Read) = (from - to) > from.duration
Needs a Read and gives an Int
85
Crazy Awesome - Implicitssealed abstract class Readcase class AMIRead(date:Date,usage:Int,duration:Int) extends Readcase class BillingRead(toDate:Date,usage:Int,numDays:Int,charges:Int) extends Readcase class CorrectedRead(read:BillingRead, usage:Int) extends Read
implicit def readToSeconds(r:Read):Int = r.date.getTime / 1000
def areConsecutive(from:Read, to:Read) = (from - to) > from.duration
• Given this and matching, casting is rarely needed
86
Crazy Awesome - XML Literalsval xml = <html> <head> <title>Scala Pronunciation Guide</title> </head> <body> <h1>How to Pronounce It</h1> </body></html>
println(xml)
87
Crazy Awesome - XML Literalsval lang = getLangval title = translate(”scala.title”,lang)val xml = <html lang={lang}> <head> <title>{title}</title> </head> <body> <h1>{title.toUpperCase}</h1> </body></html>
println(xml)
88
Crazy Awesome - XML Literalsval lang = getLangval title = translate(”scala.title”,lang)val xml = <html lang={lang}> <head> <title>{title}</title> </head> <body> <h1>{title.toUpperCase}</h1> </body></html>
println(xml)
89
Crazy Awesome - XML Literalsval states = List(”DC”,”MD”,”VA”)val xml = <html> <body> <h1>States</h1> <ul> { states.map( (state) => <li>{state}</li> ) } </ul> </body></html>
println(xml)
90
Concurrency
91
Message-passing
92
Message-passingimmutable objects
93
Message PassingImmutable objects
“actors” with “mailboxes”
94
case class Accumulate(amount: Int)case class Resetcase class Total
object Accumulator extends Actor { def act = { var sum = 0 loop { react { case Accumulate(n) => sum += n case Reset => sum = 0 case Total => reply(sum); exit } } }}
object Accumulators extends Application { Accumulator.start for(i <- (1 to 100)) { Accumulator ! Accumulate(i) } Accumulator !? Total match { case result: Int => println(result) }}
95
case class Accumulate(amount: Int)case class Resetcase class Total
object Accumulator extends Actor { def act = { var sum = 0 loop { react { case Accumulate(n) => sum += n case Reset => sum = 0 case Total => reply(sum); exit } } }}
object Accumulators extends Application { Accumulator.start for(i <- (1 to 100)) { Accumulator ! Accumulate(i) } Accumulator !? Total match { case result: Int => println(result) }}
96
case class Accumulate(amount: Int)case class Resetcase class Total
object Accumulator extends Actor { def act = { var sum = 0 loop { react { case Accumulate(n) => sum += n case Reset => sum = 0 case Total => reply(sum); exit } } }}
object Accumulators extends Application { Accumulator.start for(i <- (1 to 100)) { Accumulator ! Accumulate(i) } Accumulator !? Total match { case result: Int => println(result) }}
97
case class Accumulate(amount: Int)case class Resetcase class Total
object Accumulator extends Actor { def act = { var sum = 0 loop { react { case Accumulate(n) => sum += n case Reset => sum = 0 case Total => reply(sum); exit } } }}
object Accumulators extends Application { Accumulator.start for(i <- (1 to 100)) { Accumulator ! Accumulate(i) } Accumulator !? Total match { case result: Int => println(result) }}
98
case class Accumulate(amount: Int)case class Resetcase class Total
object Accumulator extends Actor { def act = { var sum = 0 loop { react { case Accumulate(n) => sum += n case Reset => sum = 0 case Total => reply(sum); exit } } }}
object Accumulators extends Application { Accumulator.start for(i <- (1 to 100)) { Accumulator ! Accumulate(i) } Accumulator !? Total match { case result: Int => println(result) }}
99
case class Accumulate(amount: Int)case class Resetcase class Total
object Accumulator extends Actor { def act = { var sum = 0 loop { react { case Accumulate(n) => sum += n case Reset => sum = 0 case Total => reply(sum); exit } } }}
object Accumulators extends Application { Accumulator.start for(i <- (1 to 100)) { Accumulator ! Accumulate(i) } Accumulator !? Total match { case result: Int => println(result) }}
100
case class Accumulate(amount: Int)case class Resetcase class Total
object Accumulator extends Actor { def act = { var sum = 0 loop { react { case Accumulate(n) => sum += n case Reset => sum = 0 case Total => reply(sum); exit } } }}
object Accumulators extends Application { Accumulator.start for(i <- (1 to 100)) { Accumulator ! Accumulate(i) } Accumulator !? Total match { case result: Int => println(result) }}
101
There’s a lot more to it
102
Demo
103
DemoFill Gaps in Electric Meter Reads
104
6/15 kwh
6/27 kwh
6/43 kwh
6/55 kwh
6/88 kwh
105
6/15 kwh
6/27 kwh
6/43 kwh
6/55 kwh
6/88 kwh
6/3??
6/6??
6/7??
106
for all consecutive reads r1 and r2 if r2 - r1 > one day fill gaps for (r1,r2)
107
fill: for all reads (first,second,List(rest)) if gap(first,second) fill_gap(first,second) + fill(second + rest) else first + fill(second + rest)
108
fill: for all reads (first,second,List(rest)) if !first || !second reads else if gap(first,second) fill_gap(first,second) + fill(second + rest) else first + fill(second + rest)
109
def fillReads( strategy: (MeterRead,MeterRead) => Seq[MeterRead], reads:List[MeterRead]):List[MeterRead] =
reads match { case List() => List() case first :: List() => List(first) case first :: second :: rest if gap(first, second) => first :: strategy(x,y).toList ::: fillReads(strategy, second :: rest) case first :: rest => first :: fillReads(strategy,rest) }
110
def fillReads( strategy: (MeterRead,MeterRead) => Seq[MeterRead], reads:List[MeterRead]):List[MeterRead] =
reads match { case List() => List() case first :: List() => List(first) case first :: second :: rest if gap(first, second) => first :: strategy(x,y).toList ::: fillReads(strategy, second :: rest) case first :: rest => first :: fillReads(strategy,rest) }
111
def fillReads( strategy: (MeterRead,MeterRead) => Seq[MeterRead], reads:List[MeterRead]):List[MeterRead] =
reads match { case List() => List() case first :: List() => List(first) case first :: second :: rest if gap(first, second) => first :: strategy(x,y).toList ::: fillReads(strategy, second :: rest) case first :: rest => first :: fillReads(strategy,rest) }
112
(demo with code)
113
It’s all happy flowers and meadows?
114
It’s all happy flowers and meadows?not quite; a few stumbling blocks
115
Generics, type-variance, etc. can get really confusing at times
116
Library docs not as extensive as Java’s
117
Still have to compile
118
Symbol Soup sometimes
119
Symbol Soup sometimes...but it gets easier
120
Static typing sometimes paints you into a (very dark and confusing) corner
121
sometimes you just need method_missing
122
sometimes you just needmonkey patching
123
Where is Scala now?
124
scala-lang.org
125
mailing lists, irc, a few blogs
126
One good book, a few others
127
Online docs improving, but scattered
128
Spec is surprisingly readable
129
Java is on the decline
130
Java is on the declineand Ruby and Python aren’t the
only options
131
questions?
132