An Introduction to Scala (2014)

Post on 13-Jan-2017

106 views 2 download

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 structures@annotation.tailrec

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.

- william@scalawilliam.com