45
Advanced Scala April 28th, 2016 OLEG MÜRK, CHIEF ARCHITECT

Advance Scala - Oleg Mürk

Embed Size (px)

Citation preview

Advanced ScalaApril 28th, 2016

OLEG MÜRK, CHIEF ARCHITECT

PLANET OS - APRIL 2016

Advanced Scala

• Advanced features are mostly meant for writing new • Libraries • Frameworks • Domain Specific Languages (DSLs) • Language extensions

• Application developer mostly needs to know these features • To efficiently use the libraries • To efficiently debug issues • To understand weird typing errors

PLANET OS - APRIL 2016

• Beginner (A1) • Java-like expressions • Class, def, val, var • Closures • Collections • For-expressions

• Intermediate (A2) • Pattern matching • Trait composition • (Tail) recursion

• Expert (A3) • Folds • Streams • Actors • Parser combinators

Scala Levels: Application Developer

PLANET OS - APRIL 2016

• Beginner (Library: L1) • Type parameters • Traits • Lazy vals • Currying • Call-by-name • Intermediate (L2)

• Variance • Existential types • Self types • Monads • Extractors

• Expert (L3) • Abstract types • Implicit definitions • Higher-kinded types

Scala Levels: Library Developer

PLANET OS - APRIL 2016

Higher Order Functions

class List[A] {

def filter(f : A => Boolean) : List[A]

def map[B](f : A => B) : List[B]

def foldLeft[B](z: B)(f: (B, A) => B): B

def foldRight[B](z: B)(f: (A, B) => B): B

}

• Higher order functions take/return functions as arguments

PLANET OS - APRIL 2016

Call-by-Name Parameters

• Call-by-name parameters are not executed at the time of call

def runLater[R](f: => R) = { new Thread( new Runnable { def run { val result = f; … } } ).start()}

runLater { blockingExpensiveFun() }

PLANET OS - APRIL 2016

Implicit Conversions

• Implicit conversions are implemented with implicit methods

object A2BUtils { implicit def convertA2B(a:A):B = …}

import A2BUtils._

val a:A = …val b:B = a // convertA2B(a)

PLANET OS - APRIL 2016

Extension Methods

• Extensions methods can be implemented with implicit classes

implicit class DurationInt(private val n: Int) { def seconds = { Duration(n.toLong, TimeUnit.SECONDS) }}

5.seconds // new DurationInt(5).seconds

PLANET OS - APRIL 2016

Implicit Parameters

• Methods & classes can have implicit parameters

def work(…)(implicit timeout:Duration) = { …}

implicit val myDuration = 5.seconds

work(…) // work(…)(myDuration)

PLANET OS - APRIL 2016

Type Classes aka Ad-Hoc Polymorphism

• Type classes can be implemented using implicitstrait Ordering[T] { def compare(x: T, y: T): Int}def min[T](a:T, b:T)(implicit o:Ordering[T]) = …

// Define ordering of Intsimplicit object IntOrdering extends Ordering[Int] { def compare(x: Int, y: Int) = x - y}

min(5,6) // min(5,6)(IntOrdering)

PLANET OS - APRIL 2016

Type Classes aka Ad-Hoc Polymorphism

• Type classes can be implemented using implicitstrait Ordering[T] { def compare(x: T, y: T): Int}

// These two are equivalentdef min[T](a:T, b:T)(implicit o:Ordering[T]) = …def min[T:Ordering](a:T, b:T) = if (implicitly[Ordering[T]].compare(a,b)) a else b}

PLANET OS - APRIL 2016

Futures

• Is a way to asynchronously retrieve the result of a concurrent operation

def blockingExpensiveFun() : Result = { … }

val resultFuture : Future[Result] = Future { blockingExpensiveFun() : Result }

PLANET OS - APRIL 2016

Execution Context

• Is an abstraction for running Runnable • It is usually passed around implicitly

implicit val executionContext: ExecutionContext = …

val resultFuture: Future[Result] = Future { blockingExpensiveFun() }//(executionContext)

PLANET OS - APRIL 2016

Awaiting Future Results

• Synchronously await future result with timeout import scala.concurrent._import scala.concurrent.duration._ val resultFuture = Future { blockingExpensiveFun() }

// In most cases You shouldn’t do that :)val result = Await.result(resutFuture, 15.seconds)

PLANET OS - APRIL 2016

Future Results & Callbacks

• Futures can succeed or fail

val resultFuture: Future[Result] = Future { blockingExpensiveFun()} resultFuture onComplete { case Success(result) => … case Failure(throwable) => …}

PLANET OS - APRIL 2016

Promises

• Promises are used to communicate values between threads

trait Promise[R] {

def future : Future[R]

def success(result:R)

}

PLANET OS - APRIL 2016

Promises

• Promises are used to communicate values between threadsdef runLater[R](f: => R) : Future[R] = { val promise = Promise[R]() new Thread(new Runnable { def run { val result = f; promise.success(result) }}).start() promise.future}

val resultFuture:Future[R] = runLater{ blockingExpensiveFun() }

PLANET OS - APRIL 2016

Futures Can be Composed: … using Callback Hell

val rateQuote = Future { connection.getCurrentValue(USD)}

rateQuote onSuccess { case quote => val purchase = Future { if (isProfitable(quote)) connection.buy(amount, quote) else throw new Exception("not profitable") } purchase onSuccess { case _ => println("Purchased " + amount + " USD") }}

PLANET OS - APRIL 2016

Futures Can be Composed: … or using Monad nirvana

val usdQuote = Future { connection.getCurrentValue(USD) }val chfQuote = Future { connection.getCurrentValue(CHF) }

val purchase = for { usd <- usdQuote chf <- chfQuote if isProfitable(usd, chf)} yield connection.buy(amount, chf)

purchase onSuccess { case _ => println("Purchased " + amount + " CHF")}

PLANET OS - APRIL 2016

For-Comprehensions are Syntactic Sugar for Monads

val purchase = usdQuote.flatMap { usd => chfQuote .withFilter(chf => isProfitable(usd, chf)) .map(chf => connection.buy(amount, chf))}

val purchase = for { usd <- usdQuote chf <- chfQuote if isProfitable(usd, chf)} yield connection.buy(amount, chf)

PLANET OS - APRIL 2016

For Comprehensions

• For comprehensions work with any Monad • Examples of monads:

• Seq • List • Option • Either • Try • …

PLANET OS - APRIL 2016

For Comprehensions: Seq Example

val list : Seq[(Int,Int)] = for( i <- 0 to 100 j <- i to 2*i if (i*j % 7 == 0) ) yield (i,j)

PLANET OS - APRIL 2016

For Comprehensions: Option Example

sealed trait Option[+T]case class Some[+T](v:T) extends Option[T]case object None extends Option[Nothing]

val option : Option[Result] = for( i <- mayBeIntermediate() : Option[Intermediate] r <- mayBeResult(r) : Option[Result]

if check(i, r) ) yield r

PLANET OS - APRIL 2016

For-Comprehensions: Desugaring

genA().map(a => f(a))

for{ a <- genA()} yield f(a)

PLANET OS - APRIL 2016

For-Comprehensions: Desugaring

genA().flatMap{ a => genB(a).map(b => f(a,b)) }

for{ a <- genA() b <- gebB(a)} yield fun(a,b)

PLANET OS - APRIL 2016

For-Comprehensions: Desugaring

genA().flatMap{ a => genB(a) .withFilter{ b => check(a,b) } .map(b => f(a,b)) ) } }

for{ a <- genA() b <- gebB(a) if check(a,b) } yield f(a,b)

PLANET OS - APRIL 2016

For Comprehensions & Monads

• For comprehensions work with any Monad • Examples of monads:

• Seq, List, Option, Either, Try • Futures • IO, (Side-)Effects • Query DSLs (Slick) • Testing DSLs (ScalaCheck)

PLANET OS - APRIL 2016

Slick - Functional Relational Mapping (FRM)

select c.name, c.price, s.name from coffees c join suppliers s on (c.supID == s.id)where c.name = "Expresso"

for { (c, s) <- coffees join suppliers on (_.supID === _.id) if c.name === "Espresso"} yield (c.name, c.price, s.name)

PLANET OS - APRIL 2016

ScalaCheck - Random Testing of Program Properties

val ints = Gen.choose(-100, 100)

def leafs: Gen[Leaf] = for { x <- ints} yield Leaf(x)

def nodes: Gen[Node] = for { left <- trees right <- trees} yield Node(left, right)

def trees: Gen[Tree] = Gen.oneOf(leafs, nodes)

PLANET OS - APRIL 2016

Monads, Monads, Monads, Monads!

PLANET OS - APRIL 2016

Monads

All told, a monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.

Saunders Mac Lane Categories for the Working Mathematician

PLANET OS - APRIL 2016

Monads in Scala

• Monad is a trait parametrized by higher kinded type, eg • List[ _ ] • Option[ _ ] • …

trait Monad[M[_]] { def unit[A](a: A): M[A] def flatMap[A, B](m: M[A])(f: A => M[B]): M[B]}

PLANET OS - APRIL 2016

Monads Laws

• Left identity: unit(a) >>= f ≡ f(a)

• Right identity: m >>= (x => unit(x)) ≡ m

• Associativity: (m >>= f) >>= g ≡ m >>= (x => (f(x) >>= g))

• Operator >>= denotes flatMap

PLANET OS - APRIL 2016

List Monad Exampleimplicit object MonadicList extends Monad[List] {

def unit[A](a:A) : List[A] = List(a)

def flatMap[A,B](l: List[A]) (f: A => List[B]) : List[B] = {

val i:List[List[B]] = l.map(f) i.flatten

}

}

PLANET OS - APRIL 2016

Option Monad Exampleimplicit object MonadicOption extends Monad[Option] {

def unit[A](a:A) : Option[A] = Some(a)

def flatMap[A,B](opt : Option[A]) (f: A => Option[B]) : Option[B] = { opt match { case Some(a) => f(a) : Option[B] case None => None } }}

PLANET OS - APRIL 2016

Advanced Scala Concepts

• Implicits • Implicit parameters • Implicit conversions • Extension methods

• Type-classes aka Adhoc Polymorphism

• For-comprehensions and Monads

PLANET OS - APRIL 2016

Very Advanced Scala Concepts

• Algebra based programming • Monoids, groups, rings

• Category theory based programming • Functors, applicative functors, monads, arrows

• Generic programming • Type-level programming • Meta-programming

• Scala Reflect • Scala Meta

PLANET OS - APRIL 2016

Scala Libraries / DSLs

• Async - simpler way to write async code • Parser Combinators - grammar parsing DSL • ScalaCheck - random testing of program properties • Algebird - abstract algebra for scala • Scalaz/Cats - category theory based programming • Shapeless - generic programming • Spire - generic numeric computations

PLANET OS - APRIL 2016

Projects in Scala

• Slick - functional relational mapping for Scala • Akka - actor-based reactive programming tookit • Finagle - fault tolerant, protocol-agnostic RPC system • Spray - high-performance REST/HTTP for Akka • Play - high velocity web framework for Java and Scala • Scalding - Scala library to specify MapReduce jobs • Spark - in-memory cluster computing • Kafka - publish-subscribe messaging • Samza - distributed stream processing framework

PLANET OS - APRIL 2016

Scala Advantages

• FP gateway drug for Java developers • You can treat Scala as Java with prettier syntax • Runs on JVM, integrates with libraries • Higher productivity, less lines of code (with type-inference) • Very suitable for:

• Data processing • Concurrent/distributed programming • Libraries/DSLs

• Many errors are caught at compile time (by the type system)

PLANET OS - APRIL 2016

Development in Scala

PLANET OS - APRIL 2016

Scala Downsides: “Modern C++”

• Shoehorning Scala type system into JVM • Slowish compile-time forces to modularize • Brittle across Scala and library versions • Sometimes scary compiler messages • Compulsion to use all possible Scala features • Doesn't prevent from creating write-only code • Libraries/DSLs can have cryptic operators and types • Optimized inner loops cannot use any advanced features • Tooling got much better lately

PLANET OS - APRIL 2016

Scala Downsides: Cultural

• Too many different programming styles possible • Object-oriented • Basic functional programming • Advanced functional programming • Category theory with unicode symbols • Generic programming • Type-level programming

• Need to enforce cultural/coding standards in organization

PLANET OS - APRIL 2016

Obligatory Moralistic Slide

49 SEPTEMBER 2015

[email protected]