an introduction to scala (2014)
TRANSCRIPT
William Narmontas
An introduction to Scala
http://scala.contractors/
https://scalawilliam.com/
City of LondonApril 2014
What is Scala?
It’s a ..ScalableLanguage
by Typesafe for the Java Virtual Machine.
Famous QuotesIf I were to pick a language today other than Java, it would be ScalaJames Gosling, “father” of Java
I can honestly say if someone had shown me the Programming Scala book by Martin Odersky, Lex Spoon & Bill Venners back in 2003 I’d probably have never created GroovyJames Strachan, creator of Groovy
No other language on the JVM seems as capable of being a ‘replacement for Java’ as Scala, and the momentum behind Scala is now unquestionableCharles Nutter, co-creator of JRuby
Who uses Scala?AppleBank of AmericaBarclaysBBCBSkyBCiscoCitigroupCredit SuisseeBayeHarmonyEDFFourSquareGawkerHSBCITVKlout
LinkedInMorgan StanleyNetflixNovellRackspaceSkySonySpringerThe GuardianTom TomTrafiguraTumblrTwitterUBSVMwareXerox
Topics
1. Benefits for clients, developers
2. Scala language
3. Scala ecosystem
4. Questions
Scala for clients1. Migration and the JVM
2. Speed of delivery: domain driven design and testing
3. Scalability and the cloud
4. Support and manpower
Migration and the JVM- Easy to add
- Libraries
- Reusability
- Developer migration
Domain Driven Design and Testing
- Type safety- Concise and expressive code- Domain Driven Testing- Tests read naturally
Scalability and Cloud
- Specific tools
- Genericity
- JVM
Support and manpower- Permanent available- Contractors available (Harvey Nash!)
- Typesafe https://typesafe.com - Underscore http://underscoreconsulting.com - Us! http://scala.contractors
Mobile payments platformBefore
- Spring-MVC- Hibernate- HttpClient- JAXB- JUnit
After- Spray- ActiveMQ/JMSscala- Spray Client- Native Scala XML - ScalaTest/Gatling
Scala for developers1. Migration and the JVM
2. Terse code and type-safety
3. Libraries
4. New paradigms
5. Concurrency
Migration and JVM- Maven plugin
- Scala library dependency
- JDK6+
- Java-like Scala initially
Terse code and type-safety- Earlier errors
- Faster iterations
- Easier communication
- Easier testing
Scala languageThe grassroots
Essentials- Everything is an expression, returns a value
- val = value = cannot be changed
- var = variable = can be changed
- def = method
Hello Worldobject HelloExample extends App {
def say(what: String, to: String = "World") {
println(s"$what, $to!")
}
say("Hello")
say("Good Evening", "Scala")
}
Hello, World!
Good Evening, Scala!
Beginner’s fizz buzzdef fizzbuzz(n: Int) =
if ((n % 3 == 0) && (n % 5 == 0)) "fizzbuzz"
else if (n % 3 == 0) "fizz"
else if (n % 5 == 0) "buzz" else s"$n"
: String = 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11,
fizz, 13, 14, fizzbuzz, 16, 17, fizz, 19, buzz
(1 to 20) map fizzbuzz mkString ", "
Collections: word count"this is a test of a word count".split(" ").groupBy(identity)
.mapValues(_.size).toList.sortBy(-_._2).mkString("\n")
(a,2)
(test,1)
(this,1)
(count,1)
(is,1)
(word,1)
(of,1)
Collections: many kindsval list = List(1, 2, 3, 99) list: List[Int] = List(1, 2, 3, 99)
val newerList = list :+ 333 :+ 2 newerList: List[Int] = List(1, 2, 3, 99, 333, 2)
val javaList = new java.util.ArrayList[Int]() { add(2); add(22) } javaList: java.util.ArrayList[Int] = [2, 22]
import scala.collection.JavaConverters._
javaList.asScala.reverse.map{_*2} : scala.collection.mutable.Buffer[Int] =ArrayBuffer(44, 4)
val m = Map("tall"->"height", "short"->"height", "slow"->"speed", "fast"->"speed")
m: scala.collection.immutable.Map[String,String] =Map(tall -> height, short -> height,slow -> speed, fast -> speed)
m.values.toSet : scala.collection.immutable.Set[String] =Set(height, speed)
Case Classes: very fittingcase class Dog(name: String, age: Int = 0)
val max = Dog("Max", 12) max: Dog = Dog(Max,12)
max.age : Int = 12
Dog("Spot", 5) == Dog("Spot", 5) : Boolean = true
max.copy(age = 13) : Dog = Dog(Max,13)
Tuples: case classes lighttype NamePrice = (String, Int)
val getStockNamePrice: String => NamePrice = {
case "APPL" => ("Apple", 54179)
}
val (name, price) = getStockNamePrice("APPL")
getStockNamePrice("APPL")._2
val (ticker, name, price) = ("GOOG", "Google", 57829)
val (key, value) = "urn" -> "urn:scalawilliam.com"
val namePrice = getStockNamePrice("APPL")
namePrice.productIterator.foreach(println)
name: String = Apple
price: Int = 54179
: Int = 54179
ticker: String = GOOG
name: String = Google
price: Int = 57829
key: String = urn
value: String =urn:scalawilliam.com
Apple
54179
Object-Orientedobject Rational extends App {
case class Rational(numerator: Int, denominator: Int=1) extends Ordered[Rational] {
private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
private val g = gcd(numerator.abs, denominator.abs)
val numer = numerator / g
val denom = denominator / g
def + (that: Rational) = Rational(numer * that.denom + that.numer * denom, denom * that.denom)
def - (that: Rational) = Rational(numer * that.denom - that.numer * denom, denom * that.denom)
def unary_- = Rational(-numer, denom)
def * (that: Rational) = Rational(numer * that.numer, denom * that.denom)
def / (that: Rational) = Rational(numer * that.denom, denom * that.numer)
def compare(that: Rational) = this.numer * that.denom - that.numer * this.denom
def ==(that: Rational) = that.numer == numer && that.denom == denom
override def toString = if (denom == 1) s"$numer" else s"$numer/$denom"
}
implicit def toRational(num: Int) = Rational(num)
}
Object-Oriented1 + Rational(2, 3)
-Rational(2, 3) + 1
Rational(2, 3) * Rational(2, 3)
Rational(10) == Rational(20, 2)
val rationals = List[Rational](
1, 2, Rational(2, 3), Rational(10, 7))
1 > Rational(3, 2)
rationals.sorted
rationals.max
: Rational = 5/3
: Rational = 1/3
: Rational = 4/9
: Boolean = true
: Boolean = false
: List[Rational] = List(1, 2, 2/3, 10/7)
: List[Rational] = List(2/3, 1, 10/7, 2)
: Rational = 2
Functions are first-classval twoToSix = (1 to 5).map{_+1} Vector(2, 3, 4, 5, 6)
val isEven = (_: Int) % 2 == 0 isEven: Int => Boolean = <function1>
def not[T](p: T => Boolean) =
p andThen {!_}
not[T](val p: T => Boolean)
=> T => Boolean
twoToSix.filter(not(isEven)) Vector(3, 5)
twoToSix.filterNot(isEven)
val (even, odd) = nums partition
isEven
even = Vector(2, 4, 6)
odd = Vector(3, 5)
Vector(3, 5)
case class Dog(name: String, age: Int = 0)
Matching wisely
def describe(what: Any): String = what match {
case Dog("Doge", _) => "Doge very pattern"
case Dog(name, age) if age == 0 => s"Newborn dog $name"
case first :: _ => s"A list starting with $first"
case other => s"Indescribable $other"
}
Matching wiselydescribe(Dog("Doge"))
describe(Dog("Spot"))
describe(Dog("Spot", 5))
: String = Newborn dog Spot
: String = Indescribable Dog(Spot,5)
: String = Doge very pattern
: String = A list starting with Dog(Max,0)
: String = Indescribable List()
describe(List(Dog("Max")))
describe(List())
Lazy computationlazy val externalIpAddress = {
println("Getting external IP address...")
scala.io.Source.fromURL("http://whatismyip.akamai.com/").getLines().mkString
}
externalIpAddress: String = <lazy>
val httpsUrl = s"""https://$externalIpAddress"""
Getting external IP address…httpsUrl: String = https://66.249.68.131
val httpUrl = s"""http://$externalIpAddress"""
httpUrl: String = http://66.249.68.131
Passing control [email protected]
def retry[T](times: Int)(f: => T): Try[T] =
Try(f) match {
case success @ Success(_) => success
case _ if times > 1 => retry(times - 1)(f)
case failure => failure
}
retry: [T](times: Int)(f: => T)scala.util.Try[T]
Let’s try this again...var counter = 0
retry(times = 5) {
counter = counter + 1
if ( counter == 4 ) {
s"We are reaching this point at the ${counter}th iteration"
} else {
throw new RuntimeException("Badly failed")
}
}
: scala.util.Try[String] = Success(We are reaching this point at 4th iteration)
Implicits
implicit def toPerson(name: String) = Person(name)
val person: Person = "Dino"
def greet(implicit person: Person) {
println(s"Hello, ${person.name}!")
}
greet("William")
case class Person(name: String)
greet
Hello, William!
Hello, Dino!
Person(Dino)
implicit val implicitPerson = person Person(Dino)
implicit class addXmlToUrl(url: java.net.URL) {
def loadAsXml = scala.xml.XML.load(url)
}
Implicits: pimp my library
new java.net.URL("http://weather.yahooapis.com/forecastrss?w=2391279").loadAsXml
\\
"forecast" \\ "@text" mkString ", "
Sunny, Partly Cloudy, Partly Cloudy,
Rain/Snow Showers, Partly Cloudy
Smart for-comprehensionsval staff = Map(
"IT" -> Map("Jonathan" -> 23, "Margaret" -> 26, "Terrence" -> 41),
"HR" -> Map("Amanda" -> 29, "Juliet" -> 21, "Isabelle" -> 33)
)
val people = for {
(department, people) <- staff.toList
(name, age) <- people
} yield new {
override val toString = s"$name ($age) is working in the $department department."
val personAge = age
}
people.sortBy(_.personAge).mkString("\n")
Juliet (21) is working in the HR department.
Jonathan (23) is working in the IT department.
Margaret (26) is working in the IT department.
Amanda (29) is working in the HR department.
Isabelle (33) is working in the HR department.
Terrence (41) is working in the IT department.
Reactive Futuresval tradeFeeQuote = future { stock.getTradeFee() }
val currentValueQuote = future { stock.getCurrentValue() }
/** Check out once we've made 5% on our investment **/
val trade = for {
tradeFee <- tradeFeeQuote
currentValue <- currentValueQuote
strikeValue = (stock.initialValue + stock.initialTradeFee + tradeFee) + 5.percent
if currentValue > strikeValue
} yield Sell(stock)
trade map Platform.Execute onComplete {
case Success(stockSale) => println(s"Sold the whole stock: $stockSale")
case Failure(e) => println(s"Didn't sell the stock: $e")
}
Streams
: List[BigInt] = List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)
fibs: Stream[BigInt] = Stream(0, ?)
val fibs: Stream[BigInt] =
0 #:: 1 #:: fibs.zip(fibs.tail).map { n => n._1 + n._2 }
fibs.take(10).toList
Language: what wasn’t covered- Traits- Tail recursion- Folds- Partial functions- Type classes- Extractors- Macros- Options
Scala’s ecosystemThe exciting stuff
Scala’s ecosystem- Development tools
- Concurrency tools
- ScalaTest
- Web libraries
- Data libraries
Development- SBT, scala-maven-plugin- IntelliJ, Eclipse, Typesafe Activator- REPL (Scala; SBT; Maven)- Worksheet- Script
Akka Actors (DSL) throttlecase object Dump
case object Process
def second = new Date().getSeconds
val throttler = actor(new Act {
val numbers = mutable.Queue[Int]()
become {
case Dump =>
println(s"Dump $numbers")
case Process if numbers.nonEmpty =>
println(s"[$second] processing ${numbers.dequeue()}")
case Process =>
println(s"[$second] Awaiting input...")
case toProcess: Int =>
numbers.enqueue(toProcess)
}
context.system.scheduler.schedule(0.seconds, 1.second, self, Process)
})
for { i <- 5 to 8 } throttler ! i
[55] Processing 5[56] Processing 6[57] Processing 7[58] Processing 8[59] Awaiting input…[0] Awaiting input…[1] Awaiting input…[2] Awaiting input...
Async def seconds=new java.util.Date().getSeconds
val futureGetAge = future {
Thread.sleep(4000)
println(s"Got age at $seconds")
12
}
val futureGetName = future {
Thread.sleep(2000)
println(s"Got name at $seconds")
"Ralf"
}
async{
println(s"at $seconds")
val age=await{futureGetAge}
val name=await{futureGetName}
println(s"at $seconds")
println(s"$name is $age years old")
}
at 31Got name at 33Got age at 35at 35Ralf is 12 years old
Scalatraclass ScalatraExample extends ScalatraServlet {
get("/hello/:name") {
params("name") match {
case "" => halt(status = 400, body = "No name given")
case name => <p>Hello, <name>{name}</name>!</p>
}
}
}
Spray Clientcase class DepositFunds(value: Int)
case class AccountBalance(value: Int)
object DepositorJsonProtocol extends DefaultJsonProtocol {
implicit val depositFundsFormat = jsonFormat1(DepositFunds)
implicit val accountBalanceFormat = jsonFormat1(AccountBalance)
}
import DepositorJsonProtocol._
val pipeline: HttpRequest => Future[AccountBalance] = (
addHeader("X-Forwarded-For", "4.1.2.3")
~> addHeader("X-Account-Id", "159192924")
~> addCredentials(OAuth2BearerToken("bd9cad8121bcadfa2313123d"))
~> sendReceive
~> unmarshal[AccountBalance]
)
val response: Future[AccountBalance] =
pipeline(Post("https://api.netpayments.co.uk/deposit", DepositFunds(42)))
Play!- Typesafe’s web framework
- Async HTTP
- WebSockets
- Good documentation
ScalaTestimport org.scalatest._
class SampleSpec extends FlatSpec with Matchers {
"An empty set" should "produce NoSuchElementException when head is invoked" in {
intercept[NoSuchElementException] {
Set.empty.head
}
}
"A non-empty set" must "filter as expected" in {
Set('Red, 'Green, 'Blue) filter {_ == 'Red} should contain only 'Red
}
}
Scala.JS (scala-js-fiddle)http://www.scala-js-fiddle.com/gist/9405209/Oscilloscope.scala
http://www.scala-js-fiddle.com/gist/9131923
json4sval hoursWinners: Seq[StockWinner] = for {
JArray(stocks) <- parse(json) \\ "stocks"
JObject(stock) <- stocks
JField("ticker", JString(ticker)) <- stock
JField("price an hour ago", JInt(previousPrice)) <- stock
JField("price now", JInt(currentPrice)) <- stock
upPercent = 100 * (currentPrice - previousPrice) / previousPrice
if upPercent > 0
} yield StockWinner(ticker, upPercent.toInt)
hoursWinners.sortWith(_.upPercent > _.upPercent).take(10)
scala.xml._import scala.xml._
object FindCoders extends App {
val pretty = new PrettyPrinter(60, 2).format(_: Node)
val feed = XML.load(new java.net.URL("http://www.planetscala.com/atom.xml"))
val result = <coders>{(for {
entry <- feed \\ "entry"
if (entry \\ "code").nonEmpty
name <- entry \ "author" \ "name"
} yield <coder>{name.text}</coder>).distinct}</coders>
pretty andThen println apply result
}
// <coders>
// <coder>Eric</coder>
// <coder>Paul Chiusano</coder>
// <coder>Archontophoenix</coder>
// </coders>
Conclusion- Java is easy to write, hard to read
- Scala is hard to write, easy to read
- Expressive, concise, powerful, scalable
- Code with a smile on your face
What do I learn next?
- ScalaTest
- Akka
- Scalatra
- Spray
What do I do next?
- Start using ScalaTest
- Explore the Typesafe Activator
- Read the The Neophyte's Guide to Scala
- Read Matt Stephens’ Design Driven Testing
Finally
- @ScalaWilliam & LinkedIn “William Narmontas”
- https://scalawilliam.com/
- http://scala.contractors/ - Open to Consulting.