Demystifying functional programming with Scala

60
Scala functional programming demystified @_denisftw_ http://appliedscala.com

Transcript of Demystifying functional programming with Scala

Scalafunctional programming demystified

@_denisftw_http://appliedscala.com

Roadmap

1. Introduction2. Language fundamentals3. Functional programming

UsersTwitter (Ruby)Foursquare (Java/.NET)Tumblr (PHP)Guardian (Java)

Klout (PHP)Coursera (PHP)LinkedIn* Netflix* (JVM)

History

2003 - Released in EPFL2008 - ‘Programming in Scala’2010 - Version 2.82011 - Typesafe Inc. founded2012 - First course on Coursera2013 - Version 2.10

Recipe

● Take Java-like syntax● Remove specifics and

unnecessary limitations● Mix in functional features*

Complexity

● About 40 reserved words● Only 2 namespaces vs. 4 in Java (fields, methods,

packages, types)● Methods instead of operators● No primitives (AnyVal)

Hierarchy

scala

Doublescala

Unit

scala

AnyVal

scala

Any

scala

AnyRef

java.lang

Stringscala.collection.immutable

List

scala

Null

scala

Nothing

scala

Int

Collections

s.c

Seqs.c

Set

scala.collection

Iterable

scala.collection

Traversable

s.c

Map

s.c

Lists.c

Buffers.c

IndexedSeq

● Redesigned in 2.8● 100500+ methods● Immutable by default● Easy conversions● Buffers

Basics

val a = 123

var b: Int = 124

def increment = (a: Int) => a + 1

Basics

val list = new List[Int](1, 2, 3, 4)

val first = list(0)

Classes

class User (var id: Int, val name: String) {

}

Classes

class User (var id: Int, val name: String) {

override def toString = {

id + ". " + name

}

}

scala> new User(1, "Simon")

Objects

object User {

}

Objects

object User {

def apply(id: Int, name: String) =

new User(id, name)

}

scala> User.apply(2, "Bob")

Objects

object User {

def apply(id: Int, name: String) =

new User(id, name)

}

scala> User.apply(2, "Bob")

scala> User(3, "Chris")

Language tricksval arr = Array[String]("a", "b")

val el = arr(1)

// arr.apply(1)

Language tricksdef abs(a: Int) = if (a >= 0) a else -a

abs(12)

abs{-13}

Language tricksdef greet(implicit name: String) = "Hello" + name

implicit val n = "Joe"

greet

Functional features● Functional data types● Tail recursion● Immutability● Higher-order functions● Currying● Pattern matching● Lazy evaluation

Functional data typesval l = 1 :: 2 :: 3 :: Nil

// List(1, 2, 3)

l.head // 1

l.tail // List(2, 3)

l.isEmpty // false s.c.i

Nils.c.i

::[T]

scala.collection.immutable

List[+T]

Functional data typesval l = 1 :: 2 :: 3 :: Nil

val l2 = 0 :: l

1 2 3 Nil

0

Imperative example

def concat(list: List[Any]): String = {

}

Imperative example

def concat(list: List[Any]): String = {

val iter = list.iterator

var result = ""

while (iter.hasNext) {

result += iter.next

}

result

}

Tail recursion

def concat(l: List[Any]): String =

if (l.isEmpty)

else

Tail recursion

def concat(l: List[Any]): String =

if (l.isEmpty)

""

else

l.head + concat(l.tail)

Tail recursion

def concatT(s: String, list: List[Any]): String =

if (list.isEmpty)

s

else

concatT(s + list.head, list.tail)

Tail recursiondef concat(l: List[Any]): String = {

@tailrec

def concatT(s: String, list: List[Any]): String =

if (list.isEmpty)

s

else

concatT(s + list.head, list.tail)

concatT("", l)

}

Pattern matching

def concat(l: List[Any]): String =

l match {

case Nil =>

case head :: tail =>

}

Pattern matching

def concat(l: List[Any]): String =

l match {

case Nil => ""

case head :: tail =>

head + concat(tail)

}

Lazy evaluation

lazy val now = new Date // 13:52:24

now // 13:55:05

Lazy evaluation

def debug(on: Boolean, msg:=> String) = {

if (on) {

doSomething(msg)

}

}

Lazy evaluation

val numbers = Stream.from(0)

numbers.take(5).toArray

// Array[Int](0, 1, 2, 3, 4)

Higher-order functions

val l = List(1, 2, 3, 4, 5)

l.map(el => el + 1)

// List(2, 3, 4, 5, 6)

Higher-order functions

val l = List(1, 2, 3, 4, 5)

l.filter(el => el % 2 == 0)

// List(2, 4)

Higher-order functions

val l = List(1, 2, 3, 4, 5)

l.foreach{ el => print(el) }

// 12345

Currying

def sum(x: Int)(y: Int) = x + y

sum(4)(5)

Currying

val l = List(1, 2, 3, 4, 5)

list.fold(0) { (a, b) =>

a + b

}

Currying

def tx[A](block: Session => A)

(implicit ctx: Context)

Currying

import scalikejdbc._

val statement = …

DB.tx { session =>

SQL(statement).execute

}

flatMapclass C[T]

def map[R](block: T => R): C[R] = …

def flatMap[R](block: T => C[R]): C[R] = …

flatMapval l1 = List(1, 2, 3)

val l2 = List("a", "b")

l1.flatMap{ el1 =>

l2.map{ el2 => el1 + el2 }

}

// 1a, 1b, 2a, 2b, 3a, 3b

Monads

A monad is just a monoid in the category of endofunctors

What's the problem?

for expressions● map● flatMap● filter● foreach*

for expressionsl1.flatMap { el1 =>

l2.map { el2 => el1 + el2 }

}

for expressionsl1.flatMap { el1 =>

l2.map { el2 => el1 + el2 }

}

for {

el1 <- l1

el2 <- l2

} yield el1 + el2

Stream

val numbers = Stream.from(0)

scala.collection.immutable

Stream[+T]

scala.collection

Traversable[+T]

Stream

val numbers = Stream.from(0)

val even = for {

num <- numbers if num % 2 == 0

} yield num

Option

isDefined: Boolean

get: T

getOrElse[T](default: T): T

scala

Nonescala

Some[+T]

scala

Option[+T]

Option

def lookup(login: String, password: String):

Option[User]

val maybeUser: Option[User] = …

Option

maybeUser.map { user =>

user.name

}.map { name =>

"Welcome back," + name

}.getOrElse("Hi")

Option[User]

Option[String]

Option[String]

String

map

map

getOrElse

Option

for {

user <- maybeUser

name <- Some(user.name)

} yield

"Welcome back," + name

Trydef parse(json: Js): String

try {

parse(js)

}

catch {

case e: Exception => …

}

Trydef parse(js: Js): String

val nameT = Try {

parse(js)

}

val maybeName = nameT.toOption

scala.util

Failure[+T]scala.util

Success[+T]

scala.util

Try[+T]

Trydef parse(js: Js): String

def store(name: String): Int

for {

name <- Try(parse(json))

id <- Try(store(name))

} yield id

scala.util

Failure[+T]scala.util

Success[+T]

scala.util

Try[+T]

Futuredef query(q: String): List[String]

val resF = Future { query(q) }

resF.onSuccess {

case list => …

}

resF.onFailure {

case ex => …

}

scala.concurrent

Future[+T]

Futuredef query(q: String): List[String]

def store(l: List[String]): Int

queryF.onSuccess {

case list => {

val storeF = Future { store(list) }

storeF.onSuccess { case id => … }

}

}

scala.concurrent

Future[+T]

Futureval resultsF = Future{ query("WTF") }

for {

results <- resultsF

id <- Future{ store(results) }

} yield id

scala.concurrent

Future[+T]

Wait... there’s more!

● object-oriented programming● arcane things● actors● ecosystem● tools and frameworks

The book● Suited for people with no prior

experience in Scala● Includes functional programming tutorial● Focuses on Play 2.5● ScalikeJDBC, MacWire, Akka● Webpack, Sass, React, EcmaScript 6

Questions?