Scala
Functioneel programmeren
in een object geörienteerde wereld
Werner Hofstra
Over mij
• Werner Hofstra
• Software dev @ Enshore
• Java, Scala, Clojure, …
Wie is bekend met
• Java
• Object georiënteerd programmeren
• Functioneel programmeren
• Scala
Functioneel programmeren
• Focus op (pure) functies
• Vermijden van side effects
• Immutability
• Vermijden van (re)assignment
Scala
• Static typing
• Object georiënteerd
• Functioneel
• Compileert naar Java bytecode
• Pattern matching
Beloftes
• Elegant
• Expressief
• Concurrency
• Samensmelten OO en FP
Wie gebruikt Scala?
Syntax
object Sounds extends App {
Animal dog = new Dog("Beike")
println(s"Dog: ${dog.name}")
println(dog.makeSound)
}
trait Animal {
def makeSound: String
}
class Dog(val name: String) extends Animal {
override def makeSound: String =
s"$name says WOOF!"
}
public class Sounds {
public static void main(String[] args) {
Animal dog = new Dog("Beike");
System.out.println("Dog: " + dog.getName());
System.out.println(dog.makeSound());
}
}
interface Animal {
String makeSound();
}
class Dog extends Animal {
private String name;
public Dog(String name) {
this.name = name;
}
@Override public String makeSound() {
return getName() + " says WOOF!";
}
public String getName() {
return name;
}
}
Basis
• var
• val
• def
• if
• for
• while
val fordTColor = "Black"
var peugeotColor = "Blue"
peugeotColor = "Gray"
val ferrariColor =
if (ferrari.isRed) "Red"
else "Yellow"
val colors = Array(
"Blue",
"Black",
"Gray",
"White",
)
def printAllColors: Unit = {
for (color <- colors) {
println(color)
}
}
Waarden en
variabelen
• variable
• value
val one = 1
one = 2 // Compileert niet
val oneToFive = 1 to 5
val twoToSix = oneToFive map { _ + 1 }
var two = 2
two = 1 // Ok, maar raar
Immutability
• 4 + 5 = 9
• 4 += 1
• Math.pi = 0
• werner = peter
• Time.now() += 2 hours
Object
Oriented
Programming
• Alles is een object
1 + 2
1.+(2)
"hello".endsWith("lo")
"hello" endsWith "lo"
Object Oriented
Programming
• Alles is een object
• Classes
class Person(val name: String, val age: Int)
Object Oriented
Programming
• Alles is een object
• Classes
• Traits
class Person(val name: String, val age: Int)
trait InsaneSkills {
def listSkills: List(String)
}
Object Oriented Programming
• Alles is een object
• Classes
• Traits
• Inheritance
class Person(val name: String, val age: Int)
trait InsaneSkills {
def listSkills: List(String)
}
class Programmer(
override val name: String,
override val age: Int,
language: String)
extends Person(name, age)
with InsaneSkills {
def listSkills = List(
s"Programming ${language}",
"Giving talks")
}
Traits
• Interfaces
• Implementaties
• Mixins
trait Logger {
def log(msg: String): Unit = {}
}
trait PrintLogger extends Logger {
override def log(msg: String): Unit =
println(msg)
}
class CoffeeMaker extends Logger {
def brew(kind: String): Unit =
log(s"Brewing coffee: $kind")
}
val silently = new CoffeeMaker
silently brew "espresso"
// (geen output)
val loudly = new CoffeeMaker with PrintLogger
loudly brew "espresso"
// Brewing coffee: espresso
Singletons
• Slechts 1 instantie
• Voor static methoden
• Geen constructor args
object MyRandom {
import scala.util.Random
def nextIntBetween(from: Int, to: Int): Int =
Random.nextInt(to - from) + from
}
Types
• Type inference
• Static types
// hello :: String
val hello = "Hello"
// world :: String
val world: String = "World"
// strings :: List[String]
val strings = List(hello, world)
// werner :: Programmer
val werner = new Programmer(
"werner",
28,
"Scala")
Tuples
// tuple :: (Int, String)
val tupleOne = (1, "one")
// tupleTwo :: (Programmer, (Int, String))
val tupleTwo = (werner, tupleOne)
Functies
• ‘First class citizens’
• Hebben ook types
// addOne :: Int => Int
def addOne(x: Int) = x + 2
// addLengths :: (String, String) => Int
def addLengths(s1: String, s2: String) =
s1.length + s2.length
// addLengths2 :: String => (String => Int)
def addLengths2(s1: String)(s2: String) =
s1.length + s2.length
Anonieme
functies
• Functies zonder naam
• Handig voor HOF
val add =
(x: Int, y: Int) => x + y
add(1, 2)
// 3
Hogere orde
functies
• Functies in argumenten
• Functies uit functies
Hogere orde functies
• Functies uit functies
// makeAdder :: Int => (Int => Int)
val makeAdder = {
x: Int => {
y: Int => x + y
}
}
// add5 :: Int => Int
val add5 = makeAdder(5)
// eleven :: Int
val eleven = add5(6)
// 11
Hogere orde functies
• Functies uit functies
• Functies in argumenten
def foreach(fn: Int => Unit, xs: List[Int]) =
for (x <- xs) fn(x)
foreach(println, List(1, 2, 3))
Hogere orde functies
val strings = List("one", "two", "three")
strings map { str => str.toUpperCase }
// List("ONE", "TWO", "THREE")
strings filter { _.length == 3 }
// List("one", "two")
strings.foldLeft("zero") { _ + " " + _ }
// "zero one two three"
strings foreach println
// ()
Hogere orde functies
val strings = List("one", "two", "three")
val numbers = List(1, 2, 3)
(numbers zip strings).toMap
// Map(1 -> "one", 2 -> "two", 3 -> "three")
strings partition { _.length < 4 }
// (List("one", "two"), List("three"))
strings takeWhile { _.length < 4 }
// List("one", "two")
strings dropWhile { _.length < 4 }
// List("three")
Pattern
matching
• Switch on steroids
1 match {
case 1 => "one"
case 2 => "two"
}
// "one"
(1, "one") match {
case (2, string) => "first"
case (1, string) => "second"
case (_, "one") => "third"
case (1, "one") => "fourth"
case _ => "no match"
}
Pattern matching
sealed trait Job
case object Sales extends Job
case object Boss extends Job
case class Programmer(lang: String) extends Job
case class Employee(name: String, job: Job)
val henry = Employee("Henry", Programmer("Scala"))
val james = Employee("James", Boss)
val peter = Employee("Peter", Sales)
henry.job match {
case Boss => "bossing around"
case Sales => "selling stuff"
case Programmer(lang) => s"programming $lang"
}
null
new Garage().getCar(“Ferrari”)
.navigationSystem()
.routeTo("Berlin")
.plan();
null
RoutePlan plan = null;
Car car = new Garage().getCar("Ferrari");
if (car != null) {
NavSystem nav = ferrari.navSystem();
if (nav != null) {
Route route = nav.routeTo("Berlin");
if (route != null) {
plan = route.plan();
}
}
}
Option
• Geeft aan dat iets optioneel is
• Trait
• Case classes: Some(x: Any), None
null vs Option
val garage = new Garage()
val routePlan = for {
//Car <- Option[Car]
car <- garage.getCar("Ferrari")
//NavSystem <- Option[NavSystem]
nav <- car.navigationSystem
//Route <- Option[Route]
route <- nav.routeTo("Berlin")
//RoutePlan <- Option[RoutePlan]
plan <- route.plan
//RoutePlan -> Option[RoutePlan]
} yield plan
null vs Option
val garage = new Garage()
val routePlan = for {
car <- garage.getCar("Ferrari")
nav <- car.navigationSystem
route <- nav.routeTo("Berlin")
plan <- route.plan
} yield plan
QuickSort
• Sorteeralgoritme
• O(n log n) performance
QuickSort
• Neem een lijst met getallen
• Neem een getal ‘x’ uit deze lijst
• Uit de rest van de lijst:
• Zet de getallen lager dan ‘x’ voor ‘x’ en sorteer
• Zet de getallen hoger dan ‘x’ na ‘x’ en sorteer
QuickSort
• Recursieve functie
• Base case: Lege lijst
• Andere cases: Niet-lege lijst
• Manier om lijst op te delen o.b.v. predikaat
• Hogere orde functies!
QuickSort
• Lijst: [5, 3, 10, 7, 1]
• x: 5, rest: [3, 10, 7, 1]
• sort([3, 1]) 5 sort([10, 7])
QuickSort
• [3, 1]
• x: 3, rest: [1]
• sort([1]) 3 sort([])
QuickSort
• [1]
• x: 1, rest: []
• sort([]) 1 sort([])
• [] 1 []
• [1]
QuickSort
• [3, 1]
• x: 3, rest: [1]
• sort([1]) 3 sort([])
• [1] 3 []
• [1, 3]
QuickSort
• Lijst: [5, 3, 10, 7, 1]
• x: 5, rest: [3, 10, 7, 1]
• sort([3, 1]) 5 sort([10, 7])
• [1, 3] 5 sort([10, 7])
• [1, 3] 5 [7, 10]
• [1, 3, 5, 7, 10]
QuickSort
def qsort(ints: List[Int]): List[Int] =
ints match {
case Nil => Nil
case head :: tail => {
val (lower, higher) = tail partition { _ < head }
qsort(lower) ++ (head :: qsort(higher))
}
}
QuickSort
def qsort(ints: List[Int]): List[Int] =
ints match {
case Nil => Nil
case head :: tail => {
val (lower, higher) = tail partition { _ < head }
qsort(lower) ++ (head :: qsort(higher))
}
}
QuickSort
def qsort(ints: List[Int]): List[Int] =
ints match {
case Nil => Nil
case head :: tail => {
val (lower, higher) = tail partition { _ < head }
qsort(lower) ++ (head :: qsort(higher))
}
}
QuickSort
def qsort(ints: List[Int]): List[Int] =
ints match {
case Nil => Nil
case head :: tail => {
val (lower, higher) = tail partition { _ < head }
qsort(lower) ++ (head :: qsort(higher))
}
}
QuickSort
def qsort(ints: List[Int]): List[Int] =
ints match {
case Nil => Nil
case head :: tail => {
val (lower, higher) = tail partition { _ < head }
qsort(lower) ++ (head :: qsort(higher))
}
}
QuickSort
def qsort(ints: List[Int]): List[Int] =
ints match {
case Nil => Nil
case head :: tail => {
val (lower, higher) = tail partition { _ < head }
qsort(lower) ++ (head :: qsort(higher))
}
}
Vragen?

Scala: Functioneel programmeren in een object georiënteerde wereld

  • 1.
    Scala Functioneel programmeren in eenobject geörienteerde wereld Werner Hofstra
  • 2.
    Over mij • WernerHofstra • Software dev @ Enshore • Java, Scala, Clojure, …
  • 3.
    Wie is bekendmet • Java • Object georiënteerd programmeren • Functioneel programmeren • Scala
  • 4.
    Functioneel programmeren • Focusop (pure) functies • Vermijden van side effects • Immutability • Vermijden van (re)assignment
  • 5.
    Scala • Static typing •Object georiënteerd • Functioneel • Compileert naar Java bytecode • Pattern matching
  • 6.
    Beloftes • Elegant • Expressief •Concurrency • Samensmelten OO en FP
  • 7.
  • 8.
    Syntax object Sounds extendsApp { Animal dog = new Dog("Beike") println(s"Dog: ${dog.name}") println(dog.makeSound) } trait Animal { def makeSound: String } class Dog(val name: String) extends Animal { override def makeSound: String = s"$name says WOOF!" } public class Sounds { public static void main(String[] args) { Animal dog = new Dog("Beike"); System.out.println("Dog: " + dog.getName()); System.out.println(dog.makeSound()); } } interface Animal { String makeSound(); } class Dog extends Animal { private String name; public Dog(String name) { this.name = name; } @Override public String makeSound() { return getName() + " says WOOF!"; } public String getName() { return name; } }
  • 9.
    Basis • var • val •def • if • for • while val fordTColor = "Black" var peugeotColor = "Blue" peugeotColor = "Gray" val ferrariColor = if (ferrari.isRed) "Red" else "Yellow" val colors = Array( "Blue", "Black", "Gray", "White", ) def printAllColors: Unit = { for (color <- colors) { println(color) } }
  • 10.
    Waarden en variabelen • variable •value val one = 1 one = 2 // Compileert niet val oneToFive = 1 to 5 val twoToSix = oneToFive map { _ + 1 } var two = 2 two = 1 // Ok, maar raar
  • 11.
    Immutability • 4 +5 = 9 • 4 += 1 • Math.pi = 0 • werner = peter • Time.now() += 2 hours
  • 12.
    Object Oriented Programming • Alles iseen object 1 + 2 1.+(2) "hello".endsWith("lo") "hello" endsWith "lo"
  • 13.
    Object Oriented Programming • Allesis een object • Classes class Person(val name: String, val age: Int)
  • 14.
    Object Oriented Programming • Allesis een object • Classes • Traits class Person(val name: String, val age: Int) trait InsaneSkills { def listSkills: List(String) }
  • 15.
    Object Oriented Programming •Alles is een object • Classes • Traits • Inheritance class Person(val name: String, val age: Int) trait InsaneSkills { def listSkills: List(String) } class Programmer( override val name: String, override val age: Int, language: String) extends Person(name, age) with InsaneSkills { def listSkills = List( s"Programming ${language}", "Giving talks") }
  • 16.
    Traits • Interfaces • Implementaties •Mixins trait Logger { def log(msg: String): Unit = {} } trait PrintLogger extends Logger { override def log(msg: String): Unit = println(msg) } class CoffeeMaker extends Logger { def brew(kind: String): Unit = log(s"Brewing coffee: $kind") } val silently = new CoffeeMaker silently brew "espresso" // (geen output) val loudly = new CoffeeMaker with PrintLogger loudly brew "espresso" // Brewing coffee: espresso
  • 17.
    Singletons • Slechts 1instantie • Voor static methoden • Geen constructor args object MyRandom { import scala.util.Random def nextIntBetween(from: Int, to: Int): Int = Random.nextInt(to - from) + from }
  • 18.
    Types • Type inference •Static types // hello :: String val hello = "Hello" // world :: String val world: String = "World" // strings :: List[String] val strings = List(hello, world) // werner :: Programmer val werner = new Programmer( "werner", 28, "Scala")
  • 19.
    Tuples // tuple ::(Int, String) val tupleOne = (1, "one") // tupleTwo :: (Programmer, (Int, String)) val tupleTwo = (werner, tupleOne)
  • 20.
    Functies • ‘First classcitizens’ • Hebben ook types // addOne :: Int => Int def addOne(x: Int) = x + 2 // addLengths :: (String, String) => Int def addLengths(s1: String, s2: String) = s1.length + s2.length // addLengths2 :: String => (String => Int) def addLengths2(s1: String)(s2: String) = s1.length + s2.length
  • 21.
    Anonieme functies • Functies zondernaam • Handig voor HOF val add = (x: Int, y: Int) => x + y add(1, 2) // 3
  • 22.
    Hogere orde functies • Functiesin argumenten • Functies uit functies
  • 23.
    Hogere orde functies •Functies uit functies // makeAdder :: Int => (Int => Int) val makeAdder = { x: Int => { y: Int => x + y } } // add5 :: Int => Int val add5 = makeAdder(5) // eleven :: Int val eleven = add5(6) // 11
  • 24.
    Hogere orde functies •Functies uit functies • Functies in argumenten def foreach(fn: Int => Unit, xs: List[Int]) = for (x <- xs) fn(x) foreach(println, List(1, 2, 3))
  • 25.
    Hogere orde functies valstrings = List("one", "two", "three") strings map { str => str.toUpperCase } // List("ONE", "TWO", "THREE") strings filter { _.length == 3 } // List("one", "two") strings.foldLeft("zero") { _ + " " + _ } // "zero one two three" strings foreach println // ()
  • 26.
    Hogere orde functies valstrings = List("one", "two", "three") val numbers = List(1, 2, 3) (numbers zip strings).toMap // Map(1 -> "one", 2 -> "two", 3 -> "three") strings partition { _.length < 4 } // (List("one", "two"), List("three")) strings takeWhile { _.length < 4 } // List("one", "two") strings dropWhile { _.length < 4 } // List("three")
  • 27.
    Pattern matching • Switch onsteroids 1 match { case 1 => "one" case 2 => "two" } // "one" (1, "one") match { case (2, string) => "first" case (1, string) => "second" case (_, "one") => "third" case (1, "one") => "fourth" case _ => "no match" }
  • 28.
    Pattern matching sealed traitJob case object Sales extends Job case object Boss extends Job case class Programmer(lang: String) extends Job case class Employee(name: String, job: Job) val henry = Employee("Henry", Programmer("Scala")) val james = Employee("James", Boss) val peter = Employee("Peter", Sales) henry.job match { case Boss => "bossing around" case Sales => "selling stuff" case Programmer(lang) => s"programming $lang" }
  • 29.
  • 30.
    null RoutePlan plan =null; Car car = new Garage().getCar("Ferrari"); if (car != null) { NavSystem nav = ferrari.navSystem(); if (nav != null) { Route route = nav.routeTo("Berlin"); if (route != null) { plan = route.plan(); } } }
  • 31.
    Option • Geeft aandat iets optioneel is • Trait • Case classes: Some(x: Any), None
  • 32.
    null vs Option valgarage = new Garage() val routePlan = for { //Car <- Option[Car] car <- garage.getCar("Ferrari") //NavSystem <- Option[NavSystem] nav <- car.navigationSystem //Route <- Option[Route] route <- nav.routeTo("Berlin") //RoutePlan <- Option[RoutePlan] plan <- route.plan //RoutePlan -> Option[RoutePlan] } yield plan
  • 33.
    null vs Option valgarage = new Garage() val routePlan = for { car <- garage.getCar("Ferrari") nav <- car.navigationSystem route <- nav.routeTo("Berlin") plan <- route.plan } yield plan
  • 34.
  • 35.
    QuickSort • Neem eenlijst met getallen • Neem een getal ‘x’ uit deze lijst • Uit de rest van de lijst: • Zet de getallen lager dan ‘x’ voor ‘x’ en sorteer • Zet de getallen hoger dan ‘x’ na ‘x’ en sorteer
  • 36.
    QuickSort • Recursieve functie •Base case: Lege lijst • Andere cases: Niet-lege lijst • Manier om lijst op te delen o.b.v. predikaat • Hogere orde functies!
  • 37.
    QuickSort • Lijst: [5,3, 10, 7, 1] • x: 5, rest: [3, 10, 7, 1] • sort([3, 1]) 5 sort([10, 7])
  • 38.
    QuickSort • [3, 1] •x: 3, rest: [1] • sort([1]) 3 sort([])
  • 39.
    QuickSort • [1] • x:1, rest: [] • sort([]) 1 sort([]) • [] 1 [] • [1]
  • 40.
    QuickSort • [3, 1] •x: 3, rest: [1] • sort([1]) 3 sort([]) • [1] 3 [] • [1, 3]
  • 41.
    QuickSort • Lijst: [5,3, 10, 7, 1] • x: 5, rest: [3, 10, 7, 1] • sort([3, 1]) 5 sort([10, 7]) • [1, 3] 5 sort([10, 7]) • [1, 3] 5 [7, 10] • [1, 3, 5, 7, 10]
  • 42.
    QuickSort def qsort(ints: List[Int]):List[Int] = ints match { case Nil => Nil case head :: tail => { val (lower, higher) = tail partition { _ < head } qsort(lower) ++ (head :: qsort(higher)) } }
  • 43.
    QuickSort def qsort(ints: List[Int]):List[Int] = ints match { case Nil => Nil case head :: tail => { val (lower, higher) = tail partition { _ < head } qsort(lower) ++ (head :: qsort(higher)) } }
  • 44.
    QuickSort def qsort(ints: List[Int]):List[Int] = ints match { case Nil => Nil case head :: tail => { val (lower, higher) = tail partition { _ < head } qsort(lower) ++ (head :: qsort(higher)) } }
  • 45.
    QuickSort def qsort(ints: List[Int]):List[Int] = ints match { case Nil => Nil case head :: tail => { val (lower, higher) = tail partition { _ < head } qsort(lower) ++ (head :: qsort(higher)) } }
  • 46.
    QuickSort def qsort(ints: List[Int]):List[Int] = ints match { case Nil => Nil case head :: tail => { val (lower, higher) = tail partition { _ < head } qsort(lower) ++ (head :: qsort(higher)) } }
  • 47.
    QuickSort def qsort(ints: List[Int]):List[Int] = ints match { case Nil => Nil case head :: tail => { val (lower, higher) = tail partition { _ < head } qsort(lower) ++ (head :: qsort(higher)) } }
  • 48.