Top Banner
Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3
68
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Effective ScalaHENRIK ENGSTRÖM

SOFTWARE ENGINEER - TYPESAFE@h3nk3

Page 2: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

✦ Typesafe Console Tech Lead

✦ Akka Honorary Team Member

✦ Programming Scala since 2010

$ whoami

✦ Consultant since 1998 - mainly Java

✦ Been with Typesafe since 2011

✦ Arsenal Supporter + Martial Artist

Page 3: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

About Typesafe

✦ The Typesafe Platform

‣ Play, Akka, Scala, Scala IDE, Slick, SBT, etc.

✦ Subscription

✦ Training and Consulting

Page 4: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Agenda✦ Basic Stuff

✦ Object Orientation in Scala

✦ Implicits

✦ Types

✦ Collections

✦ Pattern Matching

✦ Functional Programming

Page 5: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Basic Stuff

Page 6: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// REPL = Read Eval Print Loop$ scala_home/bin/scalaWelcome to Scala version 2.10.0scala> println("Hello, world!")Hello, world!scala>

Use the REPL

Page 7: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// Put JAR files in scala_home/lib to get access$ scala_home/bin/scalaWelcome to Scala version 2.10.0scala> import com.x.y.z.MyClassscala> val instance = new MyClassscala> instance.myMethod

The REPL and JARs

Page 8: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

REPL 2013✦ IDE Worksheet

- Scala IDE : awesome

- IntelliJ : okay

Page 9: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// JAVAString result = null;if (z < 9) result = "<9" else result = ">=9";System.out.println("Result: " + result);// SCALAprintln("Result: " + if (z < 9) "<9" else ">=9"))

Expression vs Statement

Page 10: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// What type is variable quiz?var x = 1val quiz = while (x < 10) { println("X is: " + x) x += 1}

Statement Pop Quiz

✦ All expressions in Scala returns a type

Page 11: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// Don’t tell the computer how to fishimport scala.collection.mutable.{HashSet => MHS}def findPeopleInCity(c: String, ppl: Seq[People]): Set[People] = { val found = new MHS[People]() for (p <- ppl) for (a <- p.address) if (a.city == c) found.put(p) found}

No Fishing Instructions Please!

Page 12: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

def findPeopleInCity(c: String, ppl: Seq[People]): Set[People] = { for { p <- ppl.toSet[People] a <- p.address if a.city == c } yield p}// SQL LIKE SYNTAX; FROM, WHERE, AND, SELECT

Instead - Just Order Fish

Page 13: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// 1) Mutable code leads to cloning // 2) Cloning leads to performance degradation// => Mutable code leads to worse performanceclass Footballer { private var cars = Array[Car]() def setCars(c: Array[Car]): Unit = cars = c.clone def getCars: Array[Car] = cars.clone}

Stay Immutable

Page 14: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// Safer code - use immutable collectionclass Footballer { private var cars = Vector.empty[Car] def setCars(c: Vector[Car]) = cars = c def getCars: Vector[Car] = cars}

Stay Immutable

Page 15: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// Case classes make the class immutablescala> case class Car(brand: String)scala> case class Footballer(name: String, team: String, cars: Vector[Car] = Vector.empty)scala> var jw = new Footballer("Jack Wilshire", "Arsenal")Footballer(Jack Wilshire, Arsenal, Vector())scala> jw = jw.copy(cars = Vector(Car("Porsche")))Footballer(Jack Wilshire, Arsenal, Vector(Car(Porsche)))

Case Classes ftw

Page 16: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Immutable Benefits✦ Simple equality

✦ Simple hashcode

✦ No need to lock

✦ No defensive copying

✦ Scala Case Classes

‣ Automatic equality + hashcode (Murmur)

‣ Lots of other goodies (e.g. copy)

Page 17: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// Sometimes local mutability makes senseimport scala.collection.mutable.ArrayBufferclass MyClass { def theData: Seq[Int] = { val buffer = new ArrayBuffer[Int]() populateData(buffer) buffer.toSeq }}

Local Mutability

Page 18: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// A classic game of nulldef auth(usr: String, pwd: String): Privileges = if (usr == null || usr.isEmpty || pwd == null || pwd.isEmpty || !validCredentials(usr, pwd)) withPrivileges(Anonymous) else privilegesFor(usr)

Use Option

Page 19: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

def auth(usr: Option[String], pwd: Option[String]): Privileges = { val privileges: Option[Privileges] = { u <- usr p <- pwd if (!u.isEmpty && !p.isEmpty) if canAuthenticate(u, p) } yield privilegesFor(u) privileges getOrElse withPrivileges(Anonymous)}

Use Option

Page 20: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Object Orientation

Page 21: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

trait SquareShape { val width: Int val height: Int val area: Int = width * height}class Rect(w: Int, h: Int) extends SquaredShape { override val width = w override val height = h}scala> val r1 = new Rectangle(1, 314)scala> r1.heightres0: Int = 314scala> r1.areares1: Int = 0

val for abstract members

Page 22: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Scala Initialization Order✦ From the Scala specification

(section 5.1)

- http://www.scala-lang.org/docu/files/ScalaReference.pdf

‣ First, the superclass constructor is evaluated

‣ Then, all base classes in the template’s linearization ...

‣ Finally the statement sequence stats is evaluated

Page 23: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

trait SquareShape { def width: Int def height: Int def area: Int = width * height}class Rect(w: Int, h: Int) extends SquaredShape { override val width = w override val height = h}// or even bettercase class Rect(width: Int, height: Int) extends SquaredShape

def is much better

Page 24: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// Non-trivial return types should always be// annotated!def convert(x: Int) = x match { case 1 => 1.toChar case 2 => true case z => z.toByte}def convert(x: Int): AnyVal = x match {

Annotate your APIs

Page 25: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Composition and Inheritance

✦ Prefer composition over inheritance

- easier to modify (e.g. DI)

✦ Composition can use inheritance in Scala

- leads to the famous cake pattern

Page 26: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

trait UserRepoComponent { def userLocator: UserLocator def userUpdater: UserUpdater trait UserLocator { def findAll: Vector[User] } trait UserUpdater { def save(user: User) }}

Let’s bake a cake

Page 27: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

trait JPAUserRepoComponent extends UserRepoComponent { def em: EntityManager def userLocator = new JPAUserLocator(em) def userUpdater = new JPAUserUpdater(em) class JPAUserLocator(em: EntityManager) extends UserLocator { def findAll: Vector[User] = em.createQuery("from User", classOf[User]).getResultList.toVector } class JPAUserUpdater(em: EntityManager) extends UserUpdater { def save(user: User) = em.persist(user) }}

Baking in process

Page 28: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

trait UserServiceComponent { def userService: UserService trait UserService { def findAll: Vector[User] def save(user: User): Unit def checkStatusOf(user: User): String }}

Service Layer

Page 29: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

trait DefaultUserServiceComponent extends UserServiceComponent { this: UserRepositoryComponent => def userService = new DefaultUserService class DefaultUserService extends UserService { def findAll = userLocator.findAll def save(user: User) = userUpdater.save(user) def checkStatus(user: User) = s"User $user seems okay to me" }}

Service Layer Implementation

Page 30: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

object MyApplication extends Application { val compService = new DefaultUserServiceComponent with JPAUserRepositoryComponent { def em = Persistence.createEntityManagerFactory( "cakepattern").createEntityManager() } val service = compService.userService // use the service}

Use it

Page 31: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

class MyTest extends WordSpec with MustMatchers with Mockito { trait MockedEntityManager { def em = mock[EntityManager] } "service" must { "return all users" in { val compService = new DefaultUserServiceComponent with JPAUserRepositoryComponent with MockedEntityManager // perform tests }}

Test it

Page 32: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Implicits

Page 33: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

What is it good for?

✦ Removes boilerplate code in a specific context

‣ compile time safety

‣ must be unambiguous though

Page 34: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

trait AutoRepository { def find(regId: String)(implicit dbId: DBId): Option[Car] def findAll(country: String)(implicit dbId: DBId): Seq[Car]}class DefaultAutoRepository extends AutoRepository { def find(regId: String)(implicit dbId: DBId): Option[Car] = { // ... } def findAll(country: String)(implicit dbId: DBId): Seq[Car] = { // ... }}

Example

Page 35: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// Anti patternclass CarFinder { val dbId = DbId("Dealer1") val repo = new DefaultAutoRepository def getCar(regId: String): Option[Car] = repo.find(regId)(dbId) def listCars(country: String): Seq[Car] = repo.findAll(country)(dbId)}

Example continued

Page 36: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// Use implicits => much cleaner codeclass CarFinder { implicit val dbId = DbId("Dealer1") val repo = new DefaultAutoRepository def getCar(regId: String): Option[Car] = repo.find(regId) def listCars(country: String): Seq[Car] = repo.findAll(country)}

Example continued

Page 37: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Compiler workout✦ Implicit scope

‣ Lexical : current scope, explicit imports, wildcard imports

‣ Companions of parts of the type : companion of types, companion of types of arguments, outer objects of nested types

✦ Can be expensive in compile time - use with care

Page 38: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

trait Logger { def log(msg: String) }object Logger { implicit object DefaultLogger extends Logger { def log(msg: String) = println("DL> " + msg) } def log(msg: String)(implicit logger: Logger) = { logger.log(msg) }}

Implicit Values

Page 39: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

scala> Logger.log("a small test")DL> a small testscala> class MyLogger extends Logger { def log(msg: String) = println("ML:>> " + msg) }scala> implicit def myLogger = new MyLoggerscala> Logger.log("another test")ML:>> another test

Implicit Values

Page 40: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Implicits wisdom?

deech @deechDebugging #scala implicits islike trying to find the farter in a crowed room

Page 41: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Type Traits

Page 42: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// "describes generic interfaces using type // parameters such that the implementations can// be created for any type"trait Encodable[T] { def from(t: T): String def to(s: String): T}object Encodable { implicit object IntEncoder extends Encodable[Int] { def from(i: Int): String = "int" + i def to(s: String): Int = s.substring(s.indexOf("int")+3, s.length).toInt }}

a.k.a Type Classes

Page 43: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

class MyHandler { def convert[T](t: T)(implicit enc: Encodable[T]): String = enc.from(t) def convert[T](s: String)(implicit enc: Encodable[T]): T = enc.to(s)}scala> val myHandler = new MyHandlerscala> myHandler.convert(12345)res0: String = int12345scala> myHandler.convert(res0)res1: Int = 12345

Example Usage

Page 44: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

scala> myHandler.convert(12345L)<console>:15: error: could not find implicit value for parameter encoder: Encodable[Long]scala> implicit object LongEnc extends Encodable[Long] { def from(l: Long): String = "long" + l def to(s: String): Long = s.substring(s.indexOf("long")+4, s.length).toLong }scala> myHandler.convert(12345L)

Example continued

Page 45: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Collections

Page 46: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Collections Overview

Page 47: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Immutable Collections

Page 48: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Mutable Collections

Page 49: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

// It is absolutely *awesome*scala> val seq = Seq()scala> seq.++ ++: +: /: /:\ :+ :\ addString aggregate andThen apply applyOrElse

asInstanceOf canEqual collect collectFirst combinations companion compose contains containsSlice copyToArray copyToBuffer

corresponds count diff distinct drop dropRight dropWhile endsWith exists filter filterNot find

flatMap flatten fold foldLeft foldRight forall foreach genericBuilder groupBy grouped hasDefiniteSize head

headOption indexOf indexOfSlice indexWhere indices init inits intersect isDefinedAt isEmpty isInstanceOf

isTraversableAgain iterator last lastIndexOf lastIndexOfSlice lastIndexWhere lastOption length lengthCompare lift map max

maxBy min minBy mkString nonEmpty orElse padTo par partition patch permutations

prefixLength product reduce reduceLeft reduceLeftOption reduceOption reduceRight reduceRightOption repr reverse reverseIterator

reverseMap runWith sameElements scan scanLeft scanRight segmentLength seq size slice sliding sortBy

sortWith sorted span splitAt startsWith stringPrefix sum 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 withFilter zip zipAll zipWithIndex

Explore and learn the API

Page 50: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Message to Java Developers

✦ Use Vector not List

‣ it is faster

‣ it is more memory efficient

Page 51: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Pattern Matching

Page 52: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

@scala.annotation.tailrecdef len[A](v: Vector[A], l: Int): Int = v match { case h :: t => len(t, l + 1) case Nil => l}scala> len(Vector("a","b","c","d")res0: Int = 4

FP Pattern Matching

Page 53: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

def convertedAge(a: Animal): Int = a match { case Dog(name, age) => age * 7 case Human(_, age, _) => age case Walrus("Donny", age) => age / 10 case Walrus(name, age) if name == "Walter" => age case _ => 0}

Extracting and "instanceof"

Page 54: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Associated Cost

✦ Be aware that pattern matching is if - else if under the hood.

✦ Try to put the most common occurrences in the beginning to reduce the number of hops

Page 55: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Functional Programming

Page 56: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Word of Advice

✦ Learn patterns of Functional Programming

✦ Awesome post on Functors, Applicatives and Monads

‣ http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html

✦ See scalaz for more on FP in Scala

Page 57: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Value and Context

23

value

(+8)

function

31

2323

value & context

2323

Some[Int] None

Option

context

value

Page 58: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Functor

2323 (+8) not applicable!

2323

23 (+8) 31

3131

Some[Int] Some[Int]X (+8) X

NoneNone

Page 59: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

trait Functor[F[_]] { def fmap[A,B](fa: F[A], f: A => B): F[B]}

Functor Definition

Page 60: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

def dblFunc(values: Vector[Int]): Vector[Int] = values map { _ * 2 } scala> dblFunc(Vector(1,2,3,4))res0: Vector(2,4,6,8)

Functors in Scala

Page 61: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Applicatives

2323

Some[Int]

(+8(+8))

Some[f(x)]

23(+8)

3131

Some[Int]

Page 62: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

scala> Vector(1,2,3)res0: Vector[Int] = Vector(1,2,3)scala> List("A","B","C")res1: List[String] = List(A,B,C)scala> res0 zip res1res2: List[(Int,String)] = List((1,A),(2,B),(3,C))

Scala Applicative Example

Page 63: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Monad

2323

Some[Int]

23

isEven

2424

24

4848

24

2424

Some[Int]

isEven

*2None

Page 64: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

import scala.language.higherKindstrait Monad[M[_]] { def pure[A](a: A): M[A] def bind[A,B](ma: M[A])(f: A => M[B]): M[B]}

Monad Definition

Page 65: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

def even(number: Option[Int]): Option[Int] = for { n <- number if n % 2 == 0 } yield nscala> even(Some(22))res1: Option[Int] = Some(22)scala> even(Some(21))res2: Option[Int] = None

Scala Monad Example

Page 66: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

To Recap✦ FUNCTOR

- apply a function to a wrapped value

✦ APPLICATIVE

- apply a wrapped function to a wrapped value

✦ MONAD

- apply a function that returns a wrapped value to a wrapped value

Page 67: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

Recommended Scala Books

Page 68: Effective Scala HENRIK ENGSTRÖM SOFTWARE ENGINEER - TYPESAFE @h3nk3.

EOF@h3nk3