Functional Programming from OO perspective (Sayeret Lambda lecture)

Post on 07-May-2015

868 views 1 download

description

A lecture about some FP concepts from an (impure) OO perspective.

transcript

26-Oct-11

Ittay Dror

ittayd@tikalk.com

@ittayd

http://www.tikalk.com/blogs/ittayd

Functional Programming

From OOP perspective

2 WWW.TIKALK.COM

What is FP?

3 WWW.TIKALK.COM

“a programming paradigm that

treats computation as the

evaluation of mathematical

functions and avoids state and

mutable data”

4 WWW.TIKALK.COM

• Functions as values • Immutable data structures • Mathematical models • Referential transparency

5 WWW.TIKALK.COM

Why Should We Care?

6 WWW.TIKALK.COM

Not everything is an

object

7 WWW.TIKALK.COM

service.init val data = service.get service.close println(data)

May Forget

Nothing to return?

May forget / call twice

8 WWW.TIKALK.COM

IOC

9 WWW.TIKALK.COM

class Service { def withData[T](f: Data => T) { init if (thereIsData) f(getData) close } }

service.witData(println)

10 WWW.TIKALK.COM

Simplification

11 WWW.TIKALK.COM

In OOP: trait DataConsumer[T] { def withData(data: Data): T {

In FP:

Data => T

12 WWW.TIKALK.COM

Factories are partially

applied functions

13 WWW.TIKALK.COM

val factory_: File => String => User = file => name => readFromFile(name) val factory: String => User = factory_(usersFile)

Look ma, no interfaces

14 WWW.TIKALK.COM

Security

15 WWW.TIKALK.COM

Immutable data is

sharable

16 WWW.TIKALK.COM

Instead of modifying,

create new

17 WWW.TIKALK.COM

Abstraction

18 WWW.TIKALK.COM

Functors, Applicatives,

Monads

19 WWW.TIKALK.COM

Usual way of describing:

WTF??

20 WWW.TIKALK.COM

OO way: Design Patterns

21 WWW.TIKALK.COM

Values in a context

22 WWW.TIKALK.COM

Option[X]

List[X]

Future[X]

23 WWW.TIKALK.COM

trait Future[A] { def get: A {

def findUser(name: String): Future[User] = … … val future = findUser(userName) val user = future.get println(user)

24 WWW.TIKALK.COM

val future = findUser(userName) future.onReady{user => println(user)}

trait Future[A] { def onReady(f: A => Unit) }

25 WWW.TIKALK.COM

Functor

26 WWW.TIKALK.COM

trait Future[A] { def map[B](f: A => B): Future[B] {

val user = findUser(userName) val age: Future[Int] = user.map{user => user.age}

27 WWW.TIKALK.COM

list.map(compute)

Vs.

val result = new ArrayList(list.size) for (i <- 0 to (list.size - 1)) { result += compute(list(i)) } result

28 WWW.TIKALK.COM

list.par.map(compute)

Vs.

class ComputeTask extends RecursiveTask { def compute = // split and call invokeAll... { val pool = new ForkJoinPool() val task = new ComputeTask() pool.invoke(task)

29 WWW.TIKALK.COM

trait Future[A] { def get: A def map[B](f: A => B) = new Future[B] { def get = f(Future.this.get)

{ {

30 WWW.TIKALK.COM

def marry(man: User, woman: User): Family = ⠄⠄⠄ val joe = findUser("joe") val jane = findUser("jane")

Get Joe and Jane married

31 WWW.TIKALK.COM

joe.map{joe => jane.map{jane => marry(joe, jane)}}

User => Family

Future[Family]

User => Future[Family]

Future[Future[Family]]

32 WWW.TIKALK.COM

trait Future[A] { def apply[B, C](other: Future[B], f: (A, B) => C): Future[C] }

joe.apply(jane, marry)

Attempt I

33 WWW.TIKALK.COM

It is easier to work with

single argument functions

34 WWW.TIKALK.COM

Curried Functions

val func: Int => Int => Int = a => b => a + b

(marry _).curried

35 WWW.TIKALK.COM

trait Future[A] { ... def apply[B](f: Future[A => B]): Future[B] {

Attempt II

36 WWW.TIKALK.COM

joe.apply(jane.map((marry _).curried))

User => User => Family

Future[User => Family]

User => Future[Family]

37 WWW.TIKALK.COM

We can do better

38 WWW.TIKALK.COM

val marry: Future[User => User => Family] = Future(marry) val partial: Future[User => Family] = futureMarry.apply(joe) val family: Future[Family] = partial.apply(jane)

Future(marry).apply(joe).apply(jane) Future(marry)(joe)(jane)

Using apply compiles iff A is a function

39 WWW.TIKALK.COM

Applicative Functors

40 WWW.TIKALK.COM

• Put value in context • Apply function in a context to value in a context

41 WWW.TIKALK.COM

trait Future[A] { def apply[B, C](b: Future[B]) (implicit ev: A <:< (B => C)) : Future[C] {

object Future { def apply[A](a: A): Future[A] }

Future((marry _).curried)(joe)(jane)

42 WWW.TIKALK.COM

trait Future[A] { def apply[B, C](b: Future[B]) (implicit ev: A <:< (B => C)) = new Future[C] { def get = Future.this.get(b.get)

{ {

object Future { def apply[A](a: A) = new Future[A] { def get = a } } }

43 WWW.TIKALK.COM

Composing Functions that

Return Value in a Context

44 WWW.TIKALK.COM

Composing Special

Functions

def findUser(name: String): Future[User] def findProfile(user: User): Future[Profile]

findProfile(findUser("joe"))

45 WWW.TIKALK.COM

Monads

46 WWW.TIKALK.COM

trait Future[A] { ... def flatMap[B](f: A => Future[B]): Future[B] }

findUser("joe").flatMap(findProfile)

47 WWW.TIKALK.COM

trait Future[A] { ... def flatMap[B](f: A => Future[B]): Future[B] = new Future[B] { def get = f(Future.this.get).get } } }

48 WWW.TIKALK.COM

All Together

trait Context[A] { def map[B](f: A => B): Context[B] def apply[B, C](b: Context[B]) (implicit ev: A <:< (B => C)): Context[C] def flatMap[B](f: A => Context[B]): Context[B] }

49 WWW.TIKALK.COM

Laws

50 WWW.TIKALK.COM

Idiomatic “interface” for

context classes

51 WWW.TIKALK.COM

Monoid • For type A to be (have) a Monoid:

• Binary operation „•„: (A, A) => A • Identity element „∅‟: A

52 WWW.TIKALK.COM

• Int is a Monoid (2 ways): • Binary operation: + or * • Identity element: 0 or 1

• String is a Monoid (2 ways): • Binary operation: append or prepend • Identity element: “”

53 WWW.TIKALK.COM

• Future[A] is a monoid, if A is a monoid • Binary operation: Future(A#•)

• (Future as applicative) • Identity element: Future(A#identity)

54 WWW.TIKALK.COM

Monoids generalize folds

55 WWW.TIKALK.COM

Folds: reduce values to

one

• List(x,y,z) => ∅ • x • y • z • Option(x) => if Some ∅ • x else ∅

56 WWW.TIKALK.COM

Unfolds: Create values

from initial value and

function • 1, if (a < 3) Some(a, a+1) else None

• Some(1, 2), Some(2, 3), None • ∅ • 1 • 2 • 3 • If our monoid is a List:

• Nil ++ List(1) ++ List(2) ++ List(3) • List(1,2,3)

57 WWW.TIKALK.COM

Since many things are

monoids, we can have

many generic algorithms

58 WWW.TIKALK.COM

Problems with subtyping:

• Namespace pollution • Need to control the class • Describes behavior, not „is-a‟ • Sometimes ability is conditional:

• Future is a monoid if A is a monoid • Sometimes there are different definitions

• E.g., 2 monoids for integers • Maybe a class has several facets

• E.g. context of 2 values

59 WWW.TIKALK.COM

Typeclass

• Define an API • Create instance for each class that can conform to the API

60 WWW.TIKALK.COM

Java example:

Comparator

61 WWW.TIKALK.COM

trait Functor[F[_]] { def map[A, B](fa: F[A], f: A => B): F[B] {

implicit object FutureHasFunctor extends Functor[Future] { def map[A, B](t: Future[A], f: A => B) = new Future[B] { def get = f(t.get) } } {

def age(name: String) = { val functor = implicitly[Functor[Future]] functor.map(findUser(name), {u: User => u.age}) }

62 WWW.TIKALK.COM

def age(name: String) (implicit functor: Functor[Future]) { functor.map(findUser(name), {u: User => u.age}) }

63 WWW.TIKALK.COM

def age[F[_] : Functor](name: String) { functor.map(findUser(name), {u: User => u.age}) }

64 WWW.TIKALK.COM

Generic Programming

65 WWW.TIKALK.COM

def sequence[A, AP[_] : Applicative] (apas: List[AP[A]]): AP[List[A]]

val futureOfList = sequence(listOfFutures)

66 WWW.TIKALK.COM

trait Applicative[AP[_]] { def pure[A](a: A) : AP[A] def apply[A, B](ap: AP[A => B], a: AP[A]): AP[B] {

67 WWW.TIKALK.COM

It is NOT important if you

don’t understand the next

slide

68 WWW.TIKALK.COM

def sequence[A, AP[_] : Applicative](apas: List[AP[A]]): AP[List[A]] = { val applicative = implicitly[Applicative[AP]] import applicative._ val cons = pure{x: A => xs: List[A] => x :: xs} apas match { case Nil => pure(Nil) case apa :: apas => val recursion = sequence(apas) // AP[List[A]] val partial = apply(cons, apa) // AP[List[A]=>List[A]] apply(partial, recursion) } }

69 WWW.TIKALK.COM

Beyond

70 WWW.TIKALK.COM

val m = Map[Int, Int]() m.map{case (k, v) => k + v}

71 WWW.TIKALK.COM

def map[B, That](f: A => B) (implicit bf: CanBuildFrom[Repr, B, That]) : That

72 WWW.TIKALK.COM

?