Effective
Scala
Javaday Riga 2013

@mircodotta
Golden rule?

mple!
IT Si
KEEP
not because you
It is
at you should
can, th
What’s this talk about?
mize your use of
Opti
ala to solve real
Sc
world problems
t explosions,
withou
broken thumbs or
bullet wounds
Agenda
•
Mantras
•
Collection-foo
•
Implicits
•

Style matters
Style matters
Learn the Scala way
You know it when you got it
Scala ain’t Java
nor Ruby
nor Haskell
nor <INSERT known PL>

http://docs.scala-lang.org/style/
Abstract members
trait Shape {
val edges: Int
}

Why?

class Triangle extends Shape {
override def edges: Int = 3
}

Because this
doesn’t

compile!
Abstract members
Always use def for abstract
members!
trait Shape {
def edges: Int
}
You’ve to override
trait Worker {
def run(): Unit
}
abstract class MyWorker
extends Worker {
override def run(): Unit =
//...
}
Don’t leak your types!
trait Logger {
def log(m: String): Unit
}
object Logger {
class StdLogger extends Logger {
override def log(m: String): Unit =
println(m)
}
def apply = new StdLogger()
}

What’s the return type here?
Hide your secrets
object SauceFactory {
def makeSecretSauce(): Sauce = {
val ingredients = getMagicIngredients()
val formula = getSecretFormula()
SauceMaker.prepare(ingredients, formula)
}

te! getMagicIngredients():
iva
pr def

Ingredients = //...
def getSecretFormula(): Formula = //...
}
Visibility modifiers
• Scala has very expressive visibility
modifiers

•

Access modifiers can be augmented with qualifiers

Did you know that...
public

(default)

package

protected

private

private

nested packages have access
to private classes

•

Companion object/classes
have access to each other
private members!

private[pkg]

protected

•

everything you have in java, and much more
Don’t blow up the stack!
IF YOU THINK IT’S
tailrecursive, SAY so!
case class Node(value: Int, edges: List[Node])
def bfs(node: Node, pred: Node => Boolean): Option[Node] = {
@scala.annotation.tailrec
def search(acc: List[Node]): Option[Node] = acc match {
case Nil => None
case x :: xs =>
if (pred(x)) Some(x)
else search(xs ::: xs.edges)
}
search(List(node))
}
String interpolation
case class Complex(real: Int, im: Int) {
override def toString: String =
real + " + " + im + "i"
}

case class Complex(real: Int, im: Int) {
override def toString: String =
s"$real + ${im}i"
}

interpolator!
Mantras

Mantras
Use the REPL
Or the Worksheet ;-)
Write Expressions,
not Statements
Expressions!
• shorter
• simpler
• Composable!
def home(): HttpPage = {
var page: HttpPage = null
try page = prepareHttpPage()
catch {
case _: TimeoutEx => page = TimeoutPage
case _: Exception => page = NoPage
}
return page
}

def home(): HttpPage =
try prepareHttpPage()
catch {
case _: TimeoutEx => TimeoutPage
case _: Exception => NoPage
}
Expressions compose!
def home(): HttpPage =
try prepareHttpPage()
catch {
case _: TimeoutEx => TimeoutPage
case _: Exception => NoPage
}

Try..catch is
an expression

This is a PartialFunction[Throwable,HttpPage]

def timeoutCatch = {
case _: TimeoutEx => TimeoutPage
}
def noPageCatch = {
case _: Exception => NoPage
}

def home(): HttpPage =
try prepareHttpPage()
catch timeoutCatch orElse
noPageCatch

mp
co

e!
os
return is overrated
• superfluous
•avoid multiple exit point
•Can impact performance!
How is this compiled?
@volatile var isInterrupted: Boolean = false
def process(files: Vector[File]): Unit = {
files foreach { file =>
if(isInterrupted) return
process(file)
}
logger.debug(s"processed ${files.size}")
}
def process(files: Vector[File]): Unit = {
files foreach { file =>
if(isInterrupted) return
process(file)
}
logger.debug(s"processed ${files.size}")
}

scalac A.scala -Xprint:uncurry
def process(files: List[File]): Unit = {
try {
files.foreach { file =>
if (isInterrupted)
throw new NonLocalReturnControl(nonLocalReturnKey1, value = ())
process(file)
}
logger.debug("processed “ + files.size)
}
catch {
case ex: NonLocalReturnControl =>
if (ex.key.eq(nonLocalReturnKey1)) ex.value
else throw ex
}
}
Don’t use null
null is a disease
• nullcheks will spread
• code is brittle
• NPE will still happen
• assertions won’t help
Forget null, use Option
• no more “it may be null”
• type-safe
• documentation
def authenticate(session: HttpSession,
username: Option[String],
password: Option[String]) =
for {
user <- username
pass <- password
if canAuthenticate(user,pass)
privileges <- privilegesFor(user)
} yield inject(session, privileges)
But don’t overuse Option
a null-object may be a much
better fit
def home(): HttpPage =
try prepareHttpPage()
catch {
case _: TimeoutEx => TimeoutPage
case _: Exception => NoPage
}

Null Object!
Stay immutable
Immutability
3 reasons why it matters?

• Simplifies reasoning
• simplifies reasoning
• simplifies reasoning
Immutability
Allows co/contra variance
ity
il
tab e
mu
anc -ari ---+v
------les
ub
Tro

Jav
a

String[] a = {""};
Object[] b = a;
b[0] = 1;
String value = a[0];
ayStoreException
java.lang.Arr
Immutability
• correct equals & hashcode!
• it also simplifies reasoning
about concurrency

• thread-safe by design
Immutability
• Mutability is still ok, but
keep it in local scopes

• api methods should return
immutable objects
Do you want faster Scala compilation?

program to an interface, not
an implementation.
Collection-foo

Collection-foo
http://www.scala-lang.org/docu/files/collections-api/collections.html
Learn the API
!=, ##, ++, ++:, +:, /:, /:, :+, ::, :::, :, <init>, ==, addString, aggregate,
andThen, apply, applyOrElse, asInstanceOf, canEqual, collect, collectFirst,
combinations, companion, compose, contains, containsSlice, copyToArray,
copyToBuffer, corresponds, count, diff, distinct, drop, dropRight, dropWhile,
endsWith, eq, equals, exists, filter, filterNot, find, flatMap, flatten, fold,
foldLeft, foldRight, forall, foreach, genericBuilder, getClass, groupBy, grouped,
hasDefiniteSize, hashCode, head, headOption, indexOf, indexOfSlice, indexWhere,
indices, init, inits, intersect, isDefinedAt, isEmpty, isInstanceOf,
isTraversableAgain, iterator, last, lastIndexOf, lastIndexOfSlice, lastIndexWhere,
lastOption, length, lengthCompare, lift, map, mapConserve, max, maxBy, min, minBy,
mkString, ne, nonEmpty, notify, notifyAll, orElse, padTo, par, partition, patch,
permutations, prefixLength, product, productArity, productElement, productIterator,
productPrefix, reduce, reduceLeft, reduceLeftOption, reduceOption, reduceRight,
reduceRightOption, removeDuplicates, repr, reverse, reverseIterator, reverseMap,
reverse_:::, runWith, sameElements, scan, scanLeft, scanRight, segmentLength, seq,
size, slice, sliding, sortBy, sortWith, sorted, span, splitAt, startsWith,
stringPrefix, sum, synchronized, tail, tails, take, takeRight, takeWhile, to,
toArray, toBuffer, toIndexedSeq, toIterable, toIterator, toList, toMap, toSeq,
toSet, toStream, toString, toTraversable, toVector, transpose, union, unzip,
unzip3, updated, view, wait, withFilter, zip, zipAll, zipWithIndex
Know when to breakOut
def adultFriends(p: Person): Array[Person] = {
val adults = // <- of type List
for { friend <- p.friends // <- of type List
if friend.age >= 18 } yield friend
adults.toArray
}

can we avoid the 2nd iteration?
def adultFriends(p: Person): Array[Person] =
(for { friend <- p.friends
if friend.age >= 18
} yield friend)(collection.breakOut)
Common pitfall
def pick(users: Seq[User])

mutable or immutable?

Good question!
remedy

import scala.collection.immutable
def pick(users: immutable.Seq[User])
Implicits
Limit the scope of implicits
Implicit Resolution
• implicits in the local scope
•
•
•

Implicits defined in current scope (1)
explicit imports (2)
wildcard imports (3)

• parts of A type
•
•
•

companion object of the type (and inherited types)
companion objects of type arguments of the type
outer objects for nested types
http://www.scala-lang.org/docu/files/ScalaReference.pdf
Implicit Scope (Parts)
trait Logger {
def log(m: String): Unit
}
object Logger {
implicit object StdLogger extends Logger {
override def log(m: String): Unit = println(m)
}
def log(m: String)(implicit l: Logger) = l.log(m)
}
Logger.log("Hello")
// But I can override the default!
Logger.log("Hello")(RemoteLogger)
avoid implicit views
Typeclass pattern
trait Serializer[T] {
def ser(in: T): String
}
object Serializer {
def ser[T](in: T)(implicit ev: Serializer[T]) =
ev.ser(in)
implicit object IntSer extends Serializer[Int] {
def ser(in: Int): String = s"int:$in"
}

// provide implicits for common lib types...
}
Serializer.ser(2) //> res0: String = int:2
Typeclass - typesafe
case class Person(firstName: String, lastName: String)
val p = Person("Mirco", "Dotta")
Serializer.ser(p)

could not find implicit value for parameter
ev: Serializer[Person]
Typeclass - typesafe
case class Person(firstName: String, lastName: String)
object Person {
implicit object PersSer extends Serializer[Person] {
def ser(in: Person) =
s"person:${in.firstName},${in.lastName}"
}
}

val p = Person("Mirco", "Dotta")
Serializer.ser(p) > res0: String = person:Mirco,Dotta
Typeclass -extensible
Serializer.ser(2) //> res0: String = int:2

implicit object BinarySer extends Serializer[Int] {
def ser(in: Int): String = in.toBinaryString
}
Serializer.ser(2)

> res1: String = 10
Typeclass Pros & Cons
+ alternative to inheritance
•
•
•

extend classes you don’t control
single responsibility principle
modularization/decoupling

+ overridable at call site
+ binary compatibility
- decoupling
- advanced concept
Looking ahead
Level up!
Scala In Depth

Joshua Suereth

http://www.manning.com/suereth/
Effective
Scala
@mircodotta

Thanks to @jsuereth for a good portion of the slides’
content, and @heathermiller for the presentation style

Effective Scala (JavaDay Riga 2013)

  • 1.
  • 2.
  • 4.
    not because you Itis at you should can, th
  • 6.
    What’s this talkabout? mize your use of Opti ala to solve real Sc world problems t explosions, withou broken thumbs or bullet wounds
  • 7.
  • 8.
  • 9.
    Learn the Scalaway You know it when you got it Scala ain’t Java nor Ruby nor Haskell nor <INSERT known PL> http://docs.scala-lang.org/style/
  • 10.
    Abstract members trait Shape{ val edges: Int } Why? class Triangle extends Shape { override def edges: Int = 3 } Because this doesn’t compile!
  • 11.
    Abstract members Always usedef for abstract members! trait Shape { def edges: Int }
  • 12.
    You’ve to override traitWorker { def run(): Unit } abstract class MyWorker extends Worker { override def run(): Unit = //... }
  • 13.
    Don’t leak yourtypes! trait Logger { def log(m: String): Unit } object Logger { class StdLogger extends Logger { override def log(m: String): Unit = println(m) } def apply = new StdLogger() } What’s the return type here?
  • 14.
    Hide your secrets objectSauceFactory { def makeSecretSauce(): Sauce = { val ingredients = getMagicIngredients() val formula = getSecretFormula() SauceMaker.prepare(ingredients, formula) } te! getMagicIngredients(): iva pr def Ingredients = //... def getSecretFormula(): Formula = //... }
  • 15.
    Visibility modifiers • Scalahas very expressive visibility modifiers • Access modifiers can be augmented with qualifiers Did you know that... public (default) package protected private private nested packages have access to private classes • Companion object/classes have access to each other private members! private[pkg] protected • everything you have in java, and much more
  • 16.
    Don’t blow upthe stack! IF YOU THINK IT’S tailrecursive, SAY so! case class Node(value: Int, edges: List[Node]) def bfs(node: Node, pred: Node => Boolean): Option[Node] = { @scala.annotation.tailrec def search(acc: List[Node]): Option[Node] = acc match { case Nil => None case x :: xs => if (pred(x)) Some(x) else search(xs ::: xs.edges) } search(List(node)) }
  • 17.
    String interpolation case classComplex(real: Int, im: Int) { override def toString: String = real + " + " + im + "i" } case class Complex(real: Int, im: Int) { override def toString: String = s"$real + ${im}i" } interpolator!
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
    def home(): HttpPage= { var page: HttpPage = null try page = prepareHttpPage() catch { case _: TimeoutEx => page = TimeoutPage case _: Exception => page = NoPage } return page } def home(): HttpPage = try prepareHttpPage() catch { case _: TimeoutEx => TimeoutPage case _: Exception => NoPage }
  • 24.
    Expressions compose! def home():HttpPage = try prepareHttpPage() catch { case _: TimeoutEx => TimeoutPage case _: Exception => NoPage } Try..catch is an expression This is a PartialFunction[Throwable,HttpPage] def timeoutCatch = { case _: TimeoutEx => TimeoutPage } def noPageCatch = { case _: Exception => NoPage } def home(): HttpPage = try prepareHttpPage() catch timeoutCatch orElse noPageCatch mp co e! os
  • 25.
    return is overrated •superfluous •avoid multiple exit point •Can impact performance!
  • 26.
    How is thiscompiled? @volatile var isInterrupted: Boolean = false def process(files: Vector[File]): Unit = { files foreach { file => if(isInterrupted) return process(file) } logger.debug(s"processed ${files.size}") }
  • 27.
    def process(files: Vector[File]):Unit = { files foreach { file => if(isInterrupted) return process(file) } logger.debug(s"processed ${files.size}") } scalac A.scala -Xprint:uncurry def process(files: List[File]): Unit = { try { files.foreach { file => if (isInterrupted) throw new NonLocalReturnControl(nonLocalReturnKey1, value = ()) process(file) } logger.debug("processed “ + files.size) } catch { case ex: NonLocalReturnControl => if (ex.key.eq(nonLocalReturnKey1)) ex.value else throw ex } }
  • 28.
  • 29.
    null is adisease • nullcheks will spread • code is brittle • NPE will still happen • assertions won’t help
  • 30.
    Forget null, useOption • no more “it may be null” • type-safe • documentation
  • 31.
    def authenticate(session: HttpSession, username:Option[String], password: Option[String]) = for { user <- username pass <- password if canAuthenticate(user,pass) privileges <- privilegesFor(user) } yield inject(session, privileges)
  • 32.
    But don’t overuseOption a null-object may be a much better fit def home(): HttpPage = try prepareHttpPage() catch { case _: TimeoutEx => TimeoutPage case _: Exception => NoPage } Null Object!
  • 33.
  • 34.
    Immutability 3 reasons whyit matters? • Simplifies reasoning • simplifies reasoning • simplifies reasoning
  • 35.
    Immutability Allows co/contra variance ity il tabe mu anc -ari ---+v ------les ub Tro Jav a String[] a = {""}; Object[] b = a; b[0] = 1; String value = a[0]; ayStoreException java.lang.Arr
  • 36.
    Immutability • correct equals& hashcode! • it also simplifies reasoning about concurrency • thread-safe by design
  • 37.
    Immutability • Mutability isstill ok, but keep it in local scopes • api methods should return immutable objects
  • 38.
    Do you wantfaster Scala compilation? program to an interface, not an implementation.
  • 39.
  • 40.
  • 41.
    Learn the API !=,##, ++, ++:, +:, /:, /:, :+, ::, :::, :, <init>, ==, addString, aggregate, andThen, apply, applyOrElse, asInstanceOf, canEqual, collect, collectFirst, combinations, companion, compose, contains, containsSlice, copyToArray, copyToBuffer, corresponds, count, diff, distinct, drop, dropRight, dropWhile, endsWith, eq, equals, exists, filter, filterNot, find, flatMap, flatten, fold, foldLeft, foldRight, forall, foreach, genericBuilder, getClass, groupBy, grouped, hasDefiniteSize, hashCode, head, headOption, indexOf, indexOfSlice, indexWhere, indices, init, inits, intersect, isDefinedAt, isEmpty, isInstanceOf, isTraversableAgain, iterator, last, lastIndexOf, lastIndexOfSlice, lastIndexWhere, lastOption, length, lengthCompare, lift, map, mapConserve, max, maxBy, min, minBy, mkString, ne, nonEmpty, notify, notifyAll, orElse, padTo, par, partition, patch, permutations, prefixLength, product, productArity, productElement, productIterator, productPrefix, reduce, reduceLeft, reduceLeftOption, reduceOption, reduceRight, reduceRightOption, removeDuplicates, repr, reverse, reverseIterator, reverseMap, reverse_:::, runWith, sameElements, scan, scanLeft, scanRight, segmentLength, seq, size, slice, sliding, sortBy, sortWith, sorted, span, splitAt, startsWith, stringPrefix, sum, synchronized, tail, tails, take, takeRight, takeWhile, to, toArray, toBuffer, toIndexedSeq, toIterable, toIterator, toList, toMap, toSeq, toSet, toStream, toString, toTraversable, toVector, transpose, union, unzip, unzip3, updated, view, wait, withFilter, zip, zipAll, zipWithIndex
  • 42.
    Know when tobreakOut def adultFriends(p: Person): Array[Person] = { val adults = // <- of type List for { friend <- p.friends // <- of type List if friend.age >= 18 } yield friend adults.toArray } can we avoid the 2nd iteration? def adultFriends(p: Person): Array[Person] = (for { friend <- p.friends if friend.age >= 18 } yield friend)(collection.breakOut)
  • 43.
    Common pitfall def pick(users:Seq[User]) mutable or immutable? Good question! remedy import scala.collection.immutable def pick(users: immutable.Seq[User])
  • 44.
  • 45.
    Limit the scopeof implicits
  • 46.
    Implicit Resolution • implicitsin the local scope • • • Implicits defined in current scope (1) explicit imports (2) wildcard imports (3) • parts of A type • • • companion object of the type (and inherited types) companion objects of type arguments of the type outer objects for nested types http://www.scala-lang.org/docu/files/ScalaReference.pdf
  • 47.
    Implicit Scope (Parts) traitLogger { def log(m: String): Unit } object Logger { implicit object StdLogger extends Logger { override def log(m: String): Unit = println(m) } def log(m: String)(implicit l: Logger) = l.log(m) } Logger.log("Hello") // But I can override the default! Logger.log("Hello")(RemoteLogger)
  • 48.
  • 49.
    Typeclass pattern trait Serializer[T]{ def ser(in: T): String } object Serializer { def ser[T](in: T)(implicit ev: Serializer[T]) = ev.ser(in) implicit object IntSer extends Serializer[Int] { def ser(in: Int): String = s"int:$in" } // provide implicits for common lib types... } Serializer.ser(2) //> res0: String = int:2
  • 50.
    Typeclass - typesafe caseclass Person(firstName: String, lastName: String) val p = Person("Mirco", "Dotta") Serializer.ser(p) could not find implicit value for parameter ev: Serializer[Person]
  • 51.
    Typeclass - typesafe caseclass Person(firstName: String, lastName: String) object Person { implicit object PersSer extends Serializer[Person] { def ser(in: Person) = s"person:${in.firstName},${in.lastName}" } } val p = Person("Mirco", "Dotta") Serializer.ser(p) > res0: String = person:Mirco,Dotta
  • 52.
    Typeclass -extensible Serializer.ser(2) //>res0: String = int:2 implicit object BinarySer extends Serializer[Int] { def ser(in: Int): String = in.toBinaryString } Serializer.ser(2) > res1: String = 10
  • 53.
    Typeclass Pros &Cons + alternative to inheritance • • • extend classes you don’t control single responsibility principle modularization/decoupling + overridable at call site + binary compatibility - decoupling - advanced concept
  • 54.
  • 55.
    Level up! Scala InDepth Joshua Suereth http://www.manning.com/suereth/
  • 56.
    Effective Scala @mircodotta Thanks to @jsuerethfor a good portion of the slides’ content, and @heathermiller for the presentation style