KEMBAR78
Functional programming in Scala | PDF
d.jureczko@gmail.com
@DamianJureczko
FUNCTIONAL PROGRAMMING IN
SCALA
AGENDA
Scala - a quick intro
Functional programming
Functional constructions in Scala
SCALA
Created by Martin Odersky
First release in 2004
SUPPORTS OBJECT-ORIENTED
PROGRAMMING
Everything is an object
val x = 10
x.toString
Operators are functions
val y = x + 10
val z = x.+(4)
Functions like operators (infix notation)
names.mkString(",")
names mkString ","
Define your own types
abstract class Vehicle {
def move(): Unit
}
class Car extends Vehicle {
override def move(): Unit = {
println("drive")
}
}
SUPPORTS FUNCTIONAL PROGRAMMING
First-class functions, and more...
// function type
(Int, Int) => Int
// anonymous function
(x: Int, y: Int) => x + y
RUNS ON JVM
Interoperates with Java libraries
import java.time.Instant
val now = Instant.now()
import com.google.common.cache.CacheBuilder
val usersCache: Cache[String, User] = CacheBuilder.newBuilder()
.maximumSize(100).build()
STATICALLY TYPED
Find errors early
var name = "John"
name = "Mark"
name = 2 // compilation error !!!
def abs(x: Int): Int
abs("Adam") // compilation error !!!
SCALA IS ABOUT CONCISENESS
Express more with less code
class User(email: String, password: String)
TYPE INFERENCE
Compiler already knows the type
val firstName: String = "John"
val lastName = "Smith" // better
val age = 30
It's everywhere
val add: (Int, Int) => Int = (x: Int, y: Int) => x + y
// we know the type of x and y
val mul: (Int, Int) => Int = (x, y) => x * y
val sum: Int = add(2, 3)
// we know the type of the result
val product = mul(2, 3)
SCALA GOODIES
Case classes
Pattern matching
Collections
CASE CLASSES
Immutable and easy to use data structures
// declare
case class User(email: String, password: String)
// create
val admin = User("admin@company.com", "buddy")
// access fields
val adminEmail = admin.email
// create copy
val otherAdmin = admin.copy(email = "admin2@company.com")
// compare
assert(admin != otherAdmin)
PATTERN MATCHING
Powerful matching technique
something match {
case "value" => println("it's String equal to 'value'")
case 10 => println("it's Int equal to 10")
case s: String => println("it's String with value: " + s)
case _ => println("it's something else")
}
Does magic with case classes
user match {
case User("admin@company.com", "buddy") =>
println("it's administrator")
case User(email, password) =>
println("it's " + email + ", his password is: " + password)
}
COLLECTIONS
lists, sets, maps, ...
immutable
mutable
functional API
Easy to create
val students = List("Adam", "John", "Andrew")
val studentGrades = Map("Adam" -> 4, "John" -> 3, "Andrew" -> 5)
val cities = Set("London", "Berlin", "Warsaw")
Immutable - for elegance
val students = List("Adam", "John", "Andrew")
// new collection
val moreStudents = "Michael" :: students
Mutable - for speed
val students = mutable.ArrayBuffer("Adam", "John", "Andrew")
students += "Michael"
Easy to use functional API
val students = List("Adam", "John", "Andrew")
students.find(_ == "John") // Some(John)
students.filter(_.length > 4) // List(Andrew)
students.map(_.length) // List(4, 4, 6)
Programming paradigm
FUNCTIONAL PROGRAMMING
Imperative vs. Functional
statements functions/expressions
side effects no side effects
mutable program state immutable data
IMPERATIVE
How things should be done
List<Student> students;
int counter = 0;
for (Student student : students) {
if (student.getGrade() > 3) {
counter++;
}
}
FUNCTIONAL
What should be done
val counter = students.count(_.grade > 3)
FIRST-CLASS FUNCTIONS
function is a first-class citizen
can be assigned to a variable
can be passed as an argument of a function
can be returned from a function
HIGH-ORDER FUNCTIONS
take other functions as argument
return functions as result
Assign function to a variable
case class Student(name: String, grade: Int)
// function object
val goodStudent: Student => Boolean = student => student.grade > 3
assert(goodStudent(Student("Smith", 3)) == false)
assert(goodStudent(Student("Jones", 4)) == true)
Pass function to a high-order function
// List high-order funtion
def count(predicate: Student => Boolean): Int
val students = List(Student("Smith", 3), Student("Jones", 4))
val counter = students.count(goodStudent)
assert(counter == 1)
Return function from a high-order function
// high-order function
def gradeHigherThen(threshold: Int): Student => Boolean =
student => student.grade > threshold
val above3 = gradeHigherThen(3)
val above4 = gradeHigherThen(4)
val counter = students.count(above3)
PURE FUNCTIONS
No side effects
Computes a result based of its inputs
def add(x: Int, y: Int) = x + y
Referential transparency
expression is referentially transparent if it
can be replaced with is corresponding
value without changing the program's
behavior
Possible replacement
val sum1 = add(2, 2)
val sum2 = add(2, 2)
val sum1 = 4
val sum2 = 4
Impure function
var acc = 0
def impureAdd(x : Int, y: Int) = {
acc = acc + x + y
acc
}
Not referentially transparent
val sum1 = impureAdd(2, 2)
val sum2 = impureAdd(2, 2)
val sum1 = 4 // here it's 4
val sum2 = 4 // here it's 8 and the order matters
Pure functions simplify
reasoning about program behavior
composing programs
proving correctness - testing
optimizing - caching, parallelization, reordering
FUNCTIONAL DATA STRUCTURES
Immutable
Operated by pure functions
val numbers = List(1, 2, 3, 4, 5)
val oddNumbers = numbers.filter(_ % 2 == 0) // new list
Quick copy
val list1 = 1 :: Nil
assert(list1.head == 1)
assert(list1.tail == Nil)
val list2 = 2 :: list1
assert(list2.head == 2)
assert(list2.tail == List(1))
Thread safety
Immutable data can be safely shared between threads
def changeGrade(student: Student): Future[Student] = Future {
student.copy(grade = 5)
}
FUNCTIONAL CONSTRUCTIONS IN
SCALA
OPTIONAL VALUE
Option[+A]
Has two subclasses
val someName: Option[String] = Some("John")
val noneName: Option[String] = None
Optional result of a function
def findUser(email: String): Option[User]
better then
def findUser(email: String): User
val user = findUser("john.smith@company.com")
if (user != null) {
...
}
Pattern matching Options
val user = findUser("john.smith@company.com")
user match {
case Some(User(email, password)) =>
case None =>
}
MONADS
Containers
trait M[T] {
def map(f: T => S): M[S]
def flatMap(f: T => M[S]): M[S]
}
Option is a monad
Option[+A] {
def map[B](f: (A) ⇒ B): Option[B] // only if nonempty
def flatMap[B](f: (A) ⇒ Option[B]): Option[B] // only if nonempty
}
Map Option
val user: Option[User] = findUser("john.smith@company.com")
val password: Option[String] = user.map(u => u.password)
FlatMap Option
val user: Option[User] = findUser("john.smith@company.com")
val password: Option[String] = user.flatMap { u =>
if (u.password.nonEmpty) Some(u.password) else None
}
Transforming collections
val names = List("John", "Mark", "Andrew", "Micheal")
names.map(_.length) // List(4, 4, 6, 7)
ERROR HANDLING
Try - catch, the non functional way
try {
val user = findUser("john.smith@company.com") // may fail
user.password // possible NullPointerException
} catch {
case t: Throwable => // handle error
}
Try
Try[+T]
def findUser(email: String): Try[User]
findUser("john.smith@company.com") match {
case Success(User(email, password)) =>
case Failure(exception) =>
}
Try is a monad
val user = findUser("john.smith@company.com")
val password = user.map(user => user.password)
Either error or correct value
Either[+A, +B]
case class Error(message: String)
def findUser(email: String): Either[Error, User]
Pattern matching Either
findUser("john.smith@company.com") match {
case Right(User(email, password)) =>
case Left(Error(message)) =>
}
Right-biased Either (coming soon in Scala 2.12.0)
val user = findUser("john.smith@company.com")
val password = user.map(u => u.password) // only if user is Right
TAKEAWAYS
Scala is a powerful programing language
Functional programming simplifies your life
THANK YOU

Functional programming in Scala

  • 1.
  • 2.
    AGENDA Scala - aquick intro Functional programming Functional constructions in Scala
  • 3.
    SCALA Created by MartinOdersky First release in 2004
  • 4.
  • 5.
    Operators are functions valy = x + 10 val z = x.+(4)
  • 6.
    Functions like operators(infix notation) names.mkString(",") names mkString ","
  • 7.
    Define your owntypes abstract class Vehicle { def move(): Unit } class Car extends Vehicle { override def move(): Unit = { println("drive") } }
  • 8.
    SUPPORTS FUNCTIONAL PROGRAMMING First-classfunctions, and more... // function type (Int, Int) => Int // anonymous function (x: Int, y: Int) => x + y
  • 9.
    RUNS ON JVM Interoperateswith Java libraries import java.time.Instant val now = Instant.now() import com.google.common.cache.CacheBuilder val usersCache: Cache[String, User] = CacheBuilder.newBuilder() .maximumSize(100).build()
  • 10.
    STATICALLY TYPED Find errorsearly var name = "John" name = "Mark" name = 2 // compilation error !!! def abs(x: Int): Int abs("Adam") // compilation error !!!
  • 11.
    SCALA IS ABOUTCONCISENESS Express more with less code class User(email: String, password: String)
  • 12.
    TYPE INFERENCE Compiler alreadyknows the type val firstName: String = "John" val lastName = "Smith" // better val age = 30
  • 13.
    It's everywhere val add:(Int, Int) => Int = (x: Int, y: Int) => x + y // we know the type of x and y val mul: (Int, Int) => Int = (x, y) => x * y val sum: Int = add(2, 3) // we know the type of the result val product = mul(2, 3)
  • 14.
  • 15.
    CASE CLASSES Immutable andeasy to use data structures // declare case class User(email: String, password: String) // create val admin = User("admin@company.com", "buddy") // access fields val adminEmail = admin.email // create copy val otherAdmin = admin.copy(email = "admin2@company.com") // compare assert(admin != otherAdmin)
  • 16.
    PATTERN MATCHING Powerful matchingtechnique something match { case "value" => println("it's String equal to 'value'") case 10 => println("it's Int equal to 10") case s: String => println("it's String with value: " + s) case _ => println("it's something else") }
  • 17.
    Does magic withcase classes user match { case User("admin@company.com", "buddy") => println("it's administrator") case User(email, password) => println("it's " + email + ", his password is: " + password) }
  • 18.
    COLLECTIONS lists, sets, maps,... immutable mutable functional API
  • 19.
    Easy to create valstudents = List("Adam", "John", "Andrew") val studentGrades = Map("Adam" -> 4, "John" -> 3, "Andrew" -> 5) val cities = Set("London", "Berlin", "Warsaw")
  • 20.
    Immutable - forelegance val students = List("Adam", "John", "Andrew") // new collection val moreStudents = "Michael" :: students
  • 21.
    Mutable - forspeed val students = mutable.ArrayBuffer("Adam", "John", "Andrew") students += "Michael"
  • 22.
    Easy to usefunctional API val students = List("Adam", "John", "Andrew") students.find(_ == "John") // Some(John) students.filter(_.length > 4) // List(Andrew) students.map(_.length) // List(4, 4, 6)
  • 23.
  • 24.
    Imperative vs. Functional statementsfunctions/expressions side effects no side effects mutable program state immutable data
  • 25.
    IMPERATIVE How things shouldbe done List<Student> students; int counter = 0; for (Student student : students) { if (student.getGrade() > 3) { counter++; } }
  • 26.
    FUNCTIONAL What should bedone val counter = students.count(_.grade > 3)
  • 27.
    FIRST-CLASS FUNCTIONS function isa first-class citizen can be assigned to a variable can be passed as an argument of a function can be returned from a function
  • 28.
    HIGH-ORDER FUNCTIONS take otherfunctions as argument return functions as result
  • 29.
    Assign function toa variable case class Student(name: String, grade: Int) // function object val goodStudent: Student => Boolean = student => student.grade > 3 assert(goodStudent(Student("Smith", 3)) == false) assert(goodStudent(Student("Jones", 4)) == true)
  • 30.
    Pass function toa high-order function // List high-order funtion def count(predicate: Student => Boolean): Int val students = List(Student("Smith", 3), Student("Jones", 4)) val counter = students.count(goodStudent) assert(counter == 1)
  • 31.
    Return function froma high-order function // high-order function def gradeHigherThen(threshold: Int): Student => Boolean = student => student.grade > threshold val above3 = gradeHigherThen(3) val above4 = gradeHigherThen(4) val counter = students.count(above3)
  • 32.
  • 33.
    Computes a resultbased of its inputs def add(x: Int, y: Int) = x + y
  • 34.
    Referential transparency expression isreferentially transparent if it can be replaced with is corresponding value without changing the program's behavior
  • 35.
    Possible replacement val sum1= add(2, 2) val sum2 = add(2, 2) val sum1 = 4 val sum2 = 4
  • 36.
    Impure function var acc= 0 def impureAdd(x : Int, y: Int) = { acc = acc + x + y acc }
  • 37.
    Not referentially transparent valsum1 = impureAdd(2, 2) val sum2 = impureAdd(2, 2) val sum1 = 4 // here it's 4 val sum2 = 4 // here it's 8 and the order matters
  • 38.
    Pure functions simplify reasoningabout program behavior composing programs proving correctness - testing optimizing - caching, parallelization, reordering
  • 39.
  • 40.
    Operated by purefunctions val numbers = List(1, 2, 3, 4, 5) val oddNumbers = numbers.filter(_ % 2 == 0) // new list
  • 41.
    Quick copy val list1= 1 :: Nil assert(list1.head == 1) assert(list1.tail == Nil) val list2 = 2 :: list1 assert(list2.head == 2) assert(list2.tail == List(1))
  • 42.
    Thread safety Immutable datacan be safely shared between threads def changeGrade(student: Student): Future[Student] = Future { student.copy(grade = 5) }
  • 43.
  • 44.
  • 45.
    Has two subclasses valsomeName: Option[String] = Some("John") val noneName: Option[String] = None
  • 46.
    Optional result ofa function def findUser(email: String): Option[User] better then def findUser(email: String): User val user = findUser("john.smith@company.com") if (user != null) { ... }
  • 47.
    Pattern matching Options valuser = findUser("john.smith@company.com") user match { case Some(User(email, password)) => case None => }
  • 48.
    MONADS Containers trait M[T] { defmap(f: T => S): M[S] def flatMap(f: T => M[S]): M[S] }
  • 49.
    Option is amonad Option[+A] { def map[B](f: (A) ⇒ B): Option[B] // only if nonempty def flatMap[B](f: (A) ⇒ Option[B]): Option[B] // only if nonempty }
  • 50.
    Map Option val user:Option[User] = findUser("john.smith@company.com") val password: Option[String] = user.map(u => u.password)
  • 51.
    FlatMap Option val user:Option[User] = findUser("john.smith@company.com") val password: Option[String] = user.flatMap { u => if (u.password.nonEmpty) Some(u.password) else None }
  • 52.
    Transforming collections val names= List("John", "Mark", "Andrew", "Micheal") names.map(_.length) // List(4, 4, 6, 7)
  • 53.
  • 54.
    Try - catch,the non functional way try { val user = findUser("john.smith@company.com") // may fail user.password // possible NullPointerException } catch { case t: Throwable => // handle error }
  • 55.
    Try Try[+T] def findUser(email: String):Try[User] findUser("john.smith@company.com") match { case Success(User(email, password)) => case Failure(exception) => }
  • 56.
    Try is amonad val user = findUser("john.smith@company.com") val password = user.map(user => user.password)
  • 57.
    Either error orcorrect value Either[+A, +B] case class Error(message: String) def findUser(email: String): Either[Error, User]
  • 58.
    Pattern matching Either findUser("john.smith@company.com")match { case Right(User(email, password)) => case Left(Error(message)) => }
  • 59.
    Right-biased Either (comingsoon in Scala 2.12.0) val user = findUser("john.smith@company.com") val password = user.map(u => u.password) // only if user is Right
  • 60.
    TAKEAWAYS Scala is apowerful programing language Functional programming simplifies your life
  • 61.