Scala – for impatients –



Scala – for impatients –

0 0


scala-for-cs


On Github mrcmatuszak / scala-for-cs

Scala

for impatients

Marcin Matuszak

Motivation

- futher presentation about testing in Scala - java 8 - every language supports or fill support eventually functional programming, it is worth learning it now

Meet the Parents

  • Martin Odersky
  • Typesate Inc.
  • Scala Community
- scala appeared in 2003 - Typesafe Inc. in 2011 - functional programmig in scala - MOOC > 50_000 registers, 18_000 attempted last assignment - github +10_000 commits - 136 contributors

public class User {

    private final String firstName;
    private final String lastName;

    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public void getLastName() {
        return this.lastName;
    }
    @Override toString() { ... }
    @Override hashCode() { ... }
    @Override equals(Object obj) { ... }
}

case class User(firstName: String, lastName: String)
def compose[G[_, _]](implicit G0: Bifunctor[G]):
    Bifunctor[({type λ[α, β]=F[G[α, β], G[α, β]]})#λ] =
        new CompositionBifunctor[F, G] {
            implicit def F = self

            implicit def G = G0
}
trait Category[=>:[_, _]] extends Compose[=>:] { self =>

    def id[A]: A =>: A
    def empty: PlusEmpty[({type λ[α]=(α =>: α)})#λ] =
        new PlusEmpty[({type λ[α]=(α =>: α)})#λ] with ComposePlus {
            def empty[A] = id
}
def foldRightM[G[_], A, B](fa: F[A], z: => B)
    (f: (A, => B) => G[B])
        (implicit M: Monad[G]): G[B] =
            foldLeft[A, B => G[B]](fa, M.point(_))((b, a) => w
        => M.bind(f(a, w))(b))(z)

What is Scala

  • JVM
  • Scalable Language
  • FP + OO
  • General purpose
  • Statically typed
  • but with type inference
  • Consistency
  • Immutability
  • Parallel/Distributed programming
  • Open Sourced
http://stackoverflow.com/a/946952/2620291 - Scala is "scalable" in the sense that the language can be improved upon by libraries in a way that makes the extensions look like they are part of the language. That's why actors looks like part of the language, or why BigInt looks like part of the language. This also applies to most other JVM languages. It does not apply to Java, because it has special treatment for it's basic types (Int, Boolean, etc), for operators, cumbersome syntax that makes clear what is built in the language and what is library, etc. Now, Scala is more performatic than dynamic languages on the JVM because the JVM has no support for them. Dynamic languages on JVM have to resort t reflection, which is very slow. - No, not really. It's not that the JVM is somehow magic and makes things scale by its magic powers; it's that Scala, as a language, is architected to \ help people write scalable systems. That it's on top of the JVM is almost incidental. -- Scala as FP - No distinction between statements and expressions - Type inference - Anonymous functions with capturing semantics (i.e. closures) - Immutable variables and objects - Lazy evaluation - Delimited continuations (since 2.8) - Higher-order functions - Nested functions - Currying - Pattern matching - Algebraic data types (through "case classes") - Tuples

Notation

Notation

\[\begin{aligned} x & \in \mathbb{Z} \\ \end{aligned} \]

x : Int

Notation

\[\begin{aligned} x & \in \mathbb{Z} \\ \end{aligned} \]

x : Int

\[\begin{aligned} x & \in \mathbb{R} \\ \end{aligned} \]

x : Double

Notation

val x : Int = 3
val y : Double = 3.0
val f : Artist = new Artist("Frank Zappa")

Basic syntax

Defining things

  • def
  • var
  • val

Method

def add(x: Int, y: Int): Int = x + y

Variable

var x = 3

var y: Int = 4

var sum = add(x,y)

sum = add(sum, x)

Value - The fixed variable

val x = 3

val y: Int = 4

val sum = add(x, y)

def vs val

val x: Int = {
    println("eval")
    3
}

def y: Int = {
    println("eval")
    3
}

def vs val

scala> val x: Int = {println("eval"); 3}
eval
x: Int = 3

scala> x
res0: Int = 3

scala> x
res1: Int = 3

def vs val

scala> val x: Int = {println("eval"); 3}
eval
x: Int = 3

scala> x
res0: Int = 3

scala> x
res1: Int = 3

scala> def y: Int = {println("eval"); 3}
y: Int

scala> y
eval
res2: Int = 3

scala> y
eval
res1: Int = 3

Anonymous functions

scala> val sqrt = (x : Int) => x * 2
sqrt: Int => Int = <function1>

scala> sqrt(2)
res9: Int = 4

scala> sqrt
res10: Int => Int = <function1>

Return type

scala> def sqrt(x: Int) : Int = x * 2
sqrt: (x: Int)Int

scala> sqrt(2)
res10: Int = 4

scala> def sqrt(x: Int) : Int => Int = { x: Int => x * 2 }
sqrt: (x: Int)Int => Int

scala> sqrt(2)
res11: Int => Int = <function1>

Function currying

scala> def add(x: Int, y: Int) = x + y

scala> add(2, 4)
res0: Int = 6

scala> def add(x: Int)(y: Int) = x + y

scala> add(2)(4)
scala> add(2) { 4 }
scala> def add(x: Int) = (y: Int) => x + y
res1: Int => Int = <function1>

scala> val t = add(3)
res2: Int => Int = <function1>

scala> val u = t(3)
res3: Int = 6

OO

Class definition

class QItem

Class instance

class QItem
val item: QItem = new QItem()
val item = new QItem()
val item = new QItem

Constructor fields

class QItem(val addTime: Time, var expiry: Time, data: Array[Byte])

Accessors

class QItem(val addTime: Time, var expiry: Time, data: Array[Byte])
val item = new QItem(.....)
//val item: QItem = new QItem(.....)

scala> item.addTime
res0: ....

scala> item.expiry
res1: ....

scala> item.expiry = ....
item.expiry: Time = ....

eq/hc/serializable/copy

case class QItem(addTime: Time, expiry: Time, data: Array[Byte])

body

case class QItem(addTime: Time, expiry: Time, data: Array[Byte]) {

    val xid: Int = 0

    final def pack(opcode: Byte): ByteBuffer = pack(opcode, xid)

    final def pack(opcode: Byte, xid: Int): ByteBuffer = {
    val buffer = ByteBuffer.allocate(data.length + 21
    + (if (xid == 0) 0 else 4))
    ...
    buffer
    }
}

Constructor

case class QItem(addTime: Time, expiry: Time, data: Array[Byte]) {
    val xid: Int = 0

    def this(addTime: Time, expiry: Time) = this(addTime, expiry, 0)

    final def pack(opcode: Byte): ByteBuffer = {...}

    final def pack(opcode: Byte, xid: Int): ByteBuffer = {...}
}

Object

object QItem

Object

object QItem {

    def unpack(data: Array[Byte]): QItem = ...

}
val data:Array[Byte] = ...
    val now: Time = Now

    val item = QItem.unpack(data)

    val item = QItem unpack data

Object

object QItem {

    def unpack(data: Array[Byte]): QItem = ...

    def +(data: QItem): QItem = ...

}
val data:Array[Byte] = ...
val now: Time = Now

val item: QItem = QItem(data).+(QItem(data))

val item: QItem = QItem(data) + QItem(data)

True objects *

scala> 1.+(2)
res0: Double = 3.0

scala> (1).+(2)
res1: Int = 3

scala> 1 + 2
res2: Int = 3

scala> 1 toString
res3: String = 1

Companion Object

object QItem {
    def apply(addTime: Time, expiry: Time, data: Array[Byte]): QItem
    = new QItem(addTime, expiry, data)

    def apply(data: Array[Byte]): QItem = {
        val t: Time = Now
        new QItem(t, t + 3, data)
    }
}

Companion Object

object QItem {
    def apply(addTime: Time, expiry: Time, data: Array[Byte]): QItem
    = new QItem(addTime, expiry, data)

    def apply(data: Array[Byte]): QItem = {
        val t: Time = Now
        new QItem(t, t + 3, data)
    }
}
val data:Array[Byte] = ...
val now: Time = Now

val item = QItem(now, now + 3, data)

val item = QItem(data)

FP basics

Higher order function

Function that takes another function as an argument or it returns a function

Bubble sort

def bubleSort (arr: Array[Int], order: (Int, Int) => Boolean):
    Array[Int] {
    ...
    val o: Boolean = order(a, b)
    ...
}

First class function

Funciton which can be:

  • passed as a argument to another function
  • return from a function
  • assigned to variables

Bubble sort

pass function as argument

val arr = Array(3,4,6,7,2,8,1)

bubbleSort(arr, (a: Int, b: Int) => a > b)

bubbleSort(arr, (a: Int, b: Int) => a < b)

Bubble sort

assign function to variable

val asc: (Int, Int)=>Boolean = (x: Int, y:Int) => x > y

val desc: (Int, Int)=>Boolean = (x: Int, y:Int) => x < y

Bubble sort

assign function to variable

val asc: (Int, Int)=>Boolean = (x: Int, y:Int) => x > y

val desc: (Int, Int)=>Boolean = (x: Int, y:Int) => x < y
val arr = Array(3,4,6,7,2,8,1)

bubbleSort(arr, asc)

bubbleSort(arr, desc)

Underscore magic

val arr = Array(3,4,6,7,2,8,1)

bubbleSort(arr, _ > _)

bubbleSort(arr, _ < _)

Back to objects

Function is an object

val asc: (Int, Int)=>Boolean = (x: Int, y:Int) => x > y

val desc: (Int, Int)=>Boolean = (x: Int, y:Int) => x < y

Function is an object

val asc: (Int, Int)=>Boolean = (x: Int, y:Int) => x > y

val asc = new Function2[Int, Int, Boolean] {
    def apply(x: Int, y: Int): Boolean = x > y
}
val desc: (Int, Int)=>Boolean = (x: Int, y:Int) => x < y

val desc = new Function2[Int, Int, Boolean] {
    def apply(x: Int, y: Int): Boolean = x < y
}

Function0

trait Function0[+R] extends AnyRef
val javaVersion = () => sys.props("java.version")

val anonfun0 = new Function0[String] {
    def apply(): String = sys.props("java.version")
}

Function1

trait Function1[-T1, +R] extends AnyRef
val succ = (x: Int) => x + 1

val anonfun1 = new Function1[Int, Int] {
    def apply(x: Int): Int = x + 1
}

Function2

trait Function2[-T1, -T2, +R] extends AnyRef
val max = (x: Int, y: Int) => if (x < y) y else x

val anonfun2 = new Function2[Int, Int, Int] {
    def apply(x: Int, y: Int): Int = if (x < y) y else x
}

It's all about love Design Patterns

  • Creational

    • Factory method
    • Lazy initialization
    • Signleton
  • Structural

    • Adapter
    • Decorator
  • Behavioral

    • Value object
    • Null Object
    • Strategy
    • Command
    • Chain of responsibility
    • Dependency injeciton

Factory method

trait Animal
case class Dog extends Animal
case class Cat extends Animal
object Animal {
    def apply(type: String) {
        type match {
            case "cat" => new Cat()
            case "dog" => new Dog()
            case _ => throw new Exception
        }
    }
}
val animal = Animal("dog")

Lazy initialization

scala> val x = {println("eval"); 3}
eval
x: Int = 3

scala> x
res0: Int = 3

Lazy initialization

scala> val x = {println("eval"); 3}
eval
x: Int = 3

scala> x
res0: Int = 3
scala> lazy val x = {println("eval"); 3}
x: Int = <lazy>

scala> x
eval
res0: Int = 3

Singleton

object Connection {
    val connection: ....
}

Adapter

  • implicit classes
  • typeclasses

Adapter

trait Log {
    def warning(message: String)
    def error(message: String)
}

final class Logger {
    def log(level: Level, message: String) { /* ... */ }
}

implicit class LoggerToLogAdapter(logger: Log) extends Log {
    def warning(message: String) { logger.log(WARNING, message) }
    def error(message: String) {logger.log(ERROR, message)}
}

val: Log = new Logger()

Decorator

class Coffee {
    val sep = ", "
    def cost:Double = 1
    def ingredients: String = "Coffee"
}
trait Milk extends Coffee {
    abstract override def cost = super.cost + 0.5
    abstract override def ingredients = super.ingredients + sep + "Milk"
}
trait Whip extends Coffee {
    abstract override def cost = super.cost + 0.7
    abstract override def ingredients = super.ingredients + sep + "Whip"
}
trait Sprinkles extends Coffee {
    abstract override def cost = super.cost + 0.2
    abstract override def ingredients = super.ingredients + sep + "Sprinkles"
}

Decorator

val coffeeWithSprinkles: Coffee = new Coffee with Sprinkles

val coffeeWithMilk: Coffee = new Coffee with Sprinkles with Milk

val coffeeWithMilkAndWhip = new Coffee with Sprinkles with Milk with Whip

Value Object(?) / DTO

val point = (1,2)
type Point = (Int, Int)
val point: Point = (1,2)
val point = (1,2)

Null Object

trait Sound {
    def play()
}

class Music extends Sound {
    def play() { /* ... */ }
}

object SoundSystem {
    def getSound: Option[Sound] = if (available) Some(music) else None
}

for (sound <- SoundSystem.getSound) {
    sound.play()
}

Strategy

type Strategy = (Int, Int) => Int

class Context(computer: Strategy) {
    def use(a: Int, b: Int) { computer(a, b) }
}

val add: Strategy = _ + _
val multiply: Strategy = _ * _

new Context(multiply).use(2, 3)

Dependency Injeciton

trait Repository {
    def save(user: User)
}

trait DatabaseRepository extends Repository { /* ... */ }

trait UserService { self: Repository => // requires Repository
    def create(user: User) {
    // ...
    save(user)
    }
}

new UserService with DatabaseRepository

Joy of Immutability

List[T]

val list = 1 :: 2 :: 3 :: Nil
val list = List(1, 2, 3)
val list = head :: tail
val list = head :: (head :: (head :: tail))

Filter

scala> list.filter(x => x % 2 == 1)
res0: List[Int] = List(1,3)

scala> list
res1: List[Int] = List(1,2,3)

Filter

scala> list.filter(x => x % 2 == 1)
res0: List[Int] = List(1,3)

scala> list
res1: List[Int] = List(1,2,3)
def filter(p: (A) ⇒ Boolean): List[A]
def isOdd(x: Int) = x % 2 == 0
val isEven: Int => Boolean = i => i % 2 != 0

Transformation

def map[B](f: (A) ⇒ B): TraversableOnce[B]
val list = "Aaa" :: "Bbb" :: "Ccc" :: Nil
scala> list.map((s: String) => s.toLowerCase)

scala> list.map(s => s.toLowerCase)
scala> list.filter(x => x % 2 == 1)
res0: List[Int] = List(1,3)

scala> list
res1: List[Int] = List(1,2,3)
scala> list.map(_.length)

res0: List[Int] = List(3, 3, 3)
case class Person(val age: Int, val first: String, val last: String)
def filterByAge(in: List[Person]) =
in
.filter(_.age > 30)
.sort(_.age < _.age)
.map(_.first)
def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): TraversableOnce[B]
scala> list.flatMap(x => x.map(_.toInt))
List[Int] = List(65, 97, 97, 66, 98, 98, 67, 99, 99)

Pattern Matching

Helper methods

def add(x: Int, y: Int) = x + y
def sub(x: Int, y: Int) = x - y

Switch - Case

def calculator(op: String)(x: Int, y: Int) = op match {
case "+" => add(x, y)
case "-" => sub(x, y)
case _ => throw new IllegalStateException
}

Switch - Case

def calculator(op: String)(x: Int, y: Int) = op match {
case "+" => add(x, y)
case "-" => sub(x, y)
case _ => throw new IllegalStateException
}
scala> calculator("+")(3,2)
res0: Int = 5

calculator("-")(3,2)
res1: Int = 1

Typed pattern

def f(x: Any) = x match {
case i: Int => s"integer $i"
case d: Double => s"double $d"
case s: String => "string $s"
}

f(1)

f(2.0)

f("string")

List matching

def fun(xs: List[Int]): Int = {

def fun(xs: List[Int], acc: Int): Int = xs match {
case y :: ys => fun(ys, acc + y)
case Nil => acc

}

fun(xs, 0)
}

List matching

def sum(xs: List[Int]): Int = {

def sum(xs: List[Int], acc: Int): Int = xs match {
case y :: ys => sum(ys, acc + y)
case Nil => acc

}

sum(xs, 0)
}
scala> sum(List(1,2,3))
res0: Int = 6

List matching

def length[A](list : List[A]) : Int = list match {
case _ :: tail => 1 + length(tail)
case Nil => 0
}
def listAnalysis(list: List[Any]) = list match {
case Nil => "empty"
case 'a' :: tail => "starting by 'a'"
case (head:Int) :: _ if head > 3 => "starting by an int greater than 3"
case (head:Int) :: _ => "starting by an int"
case _ => "whatever"
}

Case classes

sealed abstract class Op
case class Const(value: Int) extends Op
case class Add(left: Op, right: Op) extends Op
case class Sub(left: Op, right: Op) extends Op

Case classes

scala> def eval(op: Op): Int = op match {
case Const(x) => x
case Add(left, right) => eval(left) + eval(right)
case Sub(left, right) => eval(left) - eval(right)
}

res0: eval: (op: Op) Int
scala> val expr = eval(Sub(Const(5), Add(Const(2), Const(1))))

res1: expr : Int = 2

Word count

type Word = String
type Count = Int
String => Map[Word, Count]
def wordCount(text: String) = Map[Word, Count] =
text
.split(" ")
.map(a => (a, 1))
.groupBy(_._1)
.map { a => a._1 -> a._2.map(_._2).sum }

Tooling

Tooling

  • IDE
  • Tools
  • Libraries

IDE

  • IDEA Intellij
  • Eclipse
  • Netbeans, emacs, vim, ...

Tools

  • sbt
  • maven - scala plugin
  • gradle
  • ensime

sbt - continuous testing

sbt> ~test

Libraries

  • Scalaz
  • Twitter Util
  • Slick (Scala Language-Integrated Connection Kit)
  • ScalaTest / Specs2 / ScalaCheck
  • algebird
  • Play2 / Lift / Wicket / Vaddin
  • BlueEyes
  • Scalatra
  • Spray
  • Kafka / Storm
  • scoobi / scalding
  • reactivemongo / casbah