Scala: Functioneel programmeren in een object georiënteerde wereld
-
Upload
werner-hofstra -
Category
Software
-
view
128 -
download
1
Transcript of Scala: Functioneel programmeren in een object georiënteerde wereld
ScalaFunctioneel 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?
Syntaxobject 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 functiesval 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 matchingsealed 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 Optionval 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?