126
FUNCTORS, APPLY, APPLICATIVE AND MONADS

Functor, Apply, Applicative And Monad

Embed Size (px)

Citation preview

Page 1: Functor, Apply, Applicative And Monad

FUNCTORS, APPLY,APPLICATIVE AND

MONADS

Page 2: Functor, Apply, Applicative And Monad

THAT'S JUST DULL

Page 3: Functor, Apply, Applicative And Monad

SO LET'S TRY INSTEAD ...

Page 4: Functor, Apply, Applicative And Monad

.

Page 5: Functor, Apply, Applicative And Monad

WHY SHOULD YOUCARE?

How do Functors, Apply, Applicative and Monad help youachieve early and continuous delivery of software?

Page 6: Functor, Apply, Applicative And Monad

3 CONCEPTS

Page 7: Functor, Apply, Applicative And Monad

IF A PIZZA COSTS $16 AND YOU BUYTWO HOW MUCH DO YOU SPEND?

Page 8: Functor, Apply, Applicative And Monad

A MARIO KART COSTS $20 PER DAYTO RENT. HOW MUCH DOES IT COST

TO RENT FOR TWO DAYS?

Page 9: Functor, Apply, Applicative And Monad

DON'T JUST THINK ABOUT THEMATHS

Think about the process you are going through before youget to the maths.

What information are you keeping and what information areyou dropping?

Page 10: Functor, Apply, Applicative And Monad

.

Page 11: Functor, Apply, Applicative And Monad

ABSTRACTIONAbstraction is an emphasis on the idea, qualities and

properties rather than the particulars

The importance of abstraction is derived from its ability tohide irrelevant details

It doesn't matter if its pizza, mario carts or anything else wetake the cost and muliply it by some factor.

Page 12: Functor, Apply, Applicative And Monad

WHAT IS THE REALATION SHIPBETWEEN A POLOGON:

A 3 sided triangleA 4 sided quadrilateral

Page 13: Functor, Apply, Applicative And Monad

WHAT HAS AM × AN = AM+N GOT INCOMMON WITH:

a2 × a3 = (a × a) × (a × a × a) = a5

a3 × a4 = (a × a × a) × (a × a × a × a) = a7

Page 14: Functor, Apply, Applicative And Monad

.

Page 15: Functor, Apply, Applicative And Monad

GENERALIZATIONRelationship that holds between all members of some set of

objects

Page 16: Functor, Apply, Applicative And Monad

IS A MILE A LONG WAY?

Page 17: Functor, Apply, Applicative And Monad

IS A YEAR A LARGE AMOUNT OFTIME?

Page 18: Functor, Apply, Applicative And Monad

.

Page 19: Functor, Apply, Applicative And Monad

IT ALL DEPENDS ONCONTEXT

A mile is along way if you are an Ant but not if you areflying in an aeroplane.A year is a long time for a Mayfly that lives for only 5minutes. But in geological time it's insignificant.

Page 20: Functor, Apply, Applicative And Monad

TRAINING LEVEL

Page 21: Functor, Apply, Applicative And Monad

WHAT IS FUNCTIONALPROGRAMMING?

Construct our programs using only purefunctions.

Pure functions have no side effects.

Page 22: Functor, Apply, Applicative And Monad

WHY IS A FUNCTIONLIKE A PIPE?

Some thing goes into one end and something else comesout the other end

Page 23: Functor, Apply, Applicative And Monad

Simple pipes simple can be joined together to form complexsystems?

Page 24: Functor, Apply, Applicative And Monad

WHAT'S SO GOOD ABOUT NO SIDEEFFECTS?

It makes it easier to reason about what's going on

Page 25: Functor, Apply, Applicative And Monad

IT'S IMPORTANT THAT FUNCTIONSLIKE PIPES DON'T LEAK

Page 26: Functor, Apply, Applicative And Monad

WORLD 1-1Functor Land

Page 27: Functor, Apply, Applicative And Monad

GOOMBA PROBLEM

Page 28: Functor, Apply, Applicative And Monad

HOW TO DEFEAT A GOOMBA

Stomp it and it turns into a coincase class Coin()case class Goomba()

def stomp(g:Goomba) = Coin()

The function stomp is our pipe, that transforms from aGoomba to a Coin.

Page 29: Functor, Apply, Applicative And Monad

LUIGIS VACUUM TO COLLECTGOOMBAS

class Vacuum { def collect(g:Goomba) = stomp(s)}

val vacuum = new Vacuum()val goomba = new Goomba()vacuum.collect(goomba)//Coin()

Page 30: Functor, Apply, Applicative And Monad

WHAT HAPPENS WHEN THEGOOMBA ESCAPES THE SUCTION?val vacuum = new Vacuum()

vacuum.collect(null)

Page 31: Functor, Apply, Applicative And Monad

.

Page 32: Functor, Apply, Applicative And Monad

CHECK FOR NULLSclass Vacuum { def collect(s:Goomba) = if (s == null) null else stomp(s)}

But then the calling class runs the risk of NullPointerexceptions.

Page 33: Functor, Apply, Applicative And Monad

There must be a better way

Page 34: Functor, Apply, Applicative And Monad

SOLUTIONPut the Goomba in a Cage

Page 35: Functor, Apply, Applicative And Monad

sealed trait Cage[T]case class FullCage[T](value: T) extends Cage[T]case class EmptyCage[T]() extends Cage[T]object Cage { def apply[T](x: T):Cage[T] = if (x == null) EmptyCage[T]() else FullCage(x)}

class Vacuum { def collect(c:Cage[Goomba]):Cage[Coin] = c match { case EmptyCage() => EmptyCage[Coin]() case FullCage(s) => FullCage(stomp(s)) }}

val vac = new Vacuum()

vac.collect(Cage(Goomba()))//FullCage[Coin](Coin())

vac.collect(Cage(null))//EmptyCage[Coin]()

Page 36: Functor, Apply, Applicative And Monad

.

Page 37: Functor, Apply, Applicative And Monad

CAN WE GENERALIZETHE VACUUM CLASS?

Page 38: Functor, Apply, Applicative And Monad

WHY LIMIT OURSELF TO JUSTSTOMPING GOOMBAS IN THE CAGE?class Vacuum {

def collect[A,B](c:Cage[A], f:A => B):Cage[B] = c match {

case EmptyCage() => EmptyCage[B]()

case FullCage(s) => FullCage(f(s))

}}

Page 39: Functor, Apply, Applicative And Monad

Turn it into a traittrait Collector { def collect[A,B](c:Cage[A], f:A => B):Cage[B]}

class Vacuum extends Collector { def collect[A,B](c:Cage[A], f:A => B):Cage[B] = c match { case EmptyCage() => EmptyCage[B]() case FullCage(s) => FullCage(f(s)) }}

Page 40: Functor, Apply, Applicative And Monad

Parameterise the traittrait Collector[F[_]] {

def collect[A,B](c:F[A], f:A => B): F[B]

}

object Vacuum extends Collector[Cage] { def collect[A,B](c:Cage[A], f:A => B):Cage[B] = c match { case EmptyCage() => EmptyCage[B]() case FullCage(s) => FullCage(f(s)) }}

Page 41: Functor, Apply, Applicative And Monad

.

Page 42: Functor, Apply, Applicative And Monad

THE FUNCTORA functor is basically for things that can be

mapped over.

Page 43: Functor, Apply, Applicative And Monad

THE FUNCTORDEFINITION IN SCALAZpackage scalaz

trait Functor[F[_]] extends InvariantFunctor[F] { self => ... /** Lift ̀ f̀ into ̀ F̀ and apply to ̀F[A]̀. */ def map[A, B](fa: F[A])(f: A => B): F[B] ...}

Page 44: Functor, Apply, Applicative And Monad

HOW DO YOU USE IT?

object CageFunctor extends Functor[Cage] {

def map[A,B](c:Cage[A])(f:A => B):Cage[B] = c match { case EmptyCage() => EmptyCage[B]() case FullCage(s) => FullCage(f(s)) }

}

CageFunctor.map(Cage(Gummba()))(stomp)

Page 45: Functor, Apply, Applicative And Monad

IS THERE A BETTERWAY?

Page 46: Functor, Apply, Applicative And Monad

.

Page 47: Functor, Apply, Applicative And Monad

TYPE CLASSES IN 60SECONDS

Page 48: Functor, Apply, Applicative And Monad

WHY?Extend existing classesWithout inheritanceWithout altering original sourceKeeps concerns seperate

Page 49: Functor, Apply, Applicative And Monad

HOW?3 COMPONENTS

1. The type class2. Instances for particular types3. Interface methods for the api

Page 50: Functor, Apply, Applicative And Monad

THE TYPE CLASSProvide a generic type of what we want to implement.trait ProtoBuffWriter[A] { def write(value: A): Array[Byte]}

Page 51: Functor, Apply, Applicative And Monad

TYPE CLASS INSTANCESProvide implementations for the types we care about.

Create concrete implementations of the type class and markthem as implicit.

object DefaultProtoBuffWriters { implicit val coinWriter = ProtoBuffWriter[Coin] { .... } implicit val goombaWriter = ProtoBuffWriter[Goomba] { .... } // etc ...}

Page 52: Functor, Apply, Applicative And Monad

INTERFACESWhat is exposed to clients.

Generic methods that accept instances of the type class asimplicit params.

TWO WAYS OF DOING IT

Page 53: Functor, Apply, Applicative And Monad

INTERFACE OBJECTSAll methods in a singleton.

object ProtoBuff { def toProtoBuff[A](value: A) (implicit writer: ProtoBuffWriter[A]): Array[Byte] { writer.write(value) }}

import DefaultProtoBuffWriters._val protoBuff: Array[Byte] = ProtoBuff.toProtoBuff(Coin())

Page 54: Functor, Apply, Applicative And Monad

INTERFACE SYNTAXPimp existing types with interface methods.

object ProtoBuffSyntax { implicit class ProtoBuffWriter[A](value: A) { def toProtoBuff(implicit writer: ProtoBuffWriter[A]) : Array[Byte] = { writer.write(value) } }}

import DefaultProtoBuffWriters._import ProtBuffSyntax._

val protoBuff: Array[Byte] = Coin().toProtoBuff

Page 55: Functor, Apply, Applicative And Monad

WHAT ABOUT SCALAZ?Pimp existing types

Uses the Type classes in Ops classes

Ops classes use the Type class and provide more methodsimport scala.language.implicitConversionssealed trait ToProtoBuffWriterOps { implicit def ToProtoBuffWriterOps[A](v: A) (implicit F: ProtoBuffWriter[A]) = new ProtoBuffWriterOps(v)}

object protoBuffWriter extends ToProtoBuffWriterOps

import sun.misc.BASE64Encoder //for example onlyclass ProtoBuffWriterOps[A](val self: A)(implicit val F: ProtoBuffWriter[A]) { def write(value: A) = F.write(value) def writeBase64(value: A) = new BASE64Encoder().encodeBuffer(write(value))}

Page 56: Functor, Apply, Applicative And Monad

!!WARNING!!Implicits like warp pipes can be dangerous

Page 57: Functor, Apply, Applicative And Monad

SCALAZ FUNCTOR SYNTAX:TOFUNCTOROPS

scalaz.syntax.FunctorSyntax.scalatrait ToFunctorOps extends ToFunctorOps0 with ToInvariantFunctorOps{implicit def ToFunctorOps[F[_],A](v: F[A])(implicit F0: Functor[F])= new FunctorOps[F,A](v)...}

Given a F[A] and a implicit Functor[F] in scope add all theFunctorOps to F[A]

Page 58: Functor, Apply, Applicative And Monad

SCALAZ FUNCTOR SYNTAX:FUNCTOROPS

scalaz.syntax.FunctorSyntax.scalafinal class FunctorOps[F[_],A] private[syntax](val self: F[A])(implicit val F: Functor[F]) extends Ops[F[A]] {... final def map[B](f: A => B): F[B] = F.map(self)(f)...}

Given a F[A] and a implicit Functor[F] in scope delegate themap method to the Functor[F] in scope

Page 59: Functor, Apply, Applicative And Monad

FINALLYscalaz.syntax package object extends Syntaxes

trait Syntaxes { object functor extends ToFunctorOps}import scalaz.syntax.functor

Page 60: Functor, Apply, Applicative And Monad

CAGE FUNCTOR AGAINscalaz.syntax.FunctorSyntax.scala

import scalaz.Functorimplicit object CageFunctor extends Functor[Cage] {

def map[A,B](c:Cage[A])(f:A => B):Cage[B] = c match { case EmptyCage() => EmptyCage[B]() case FullCage(s) => FullCage(f(s)) }}

import scalaz.syntax.functorCage(Goomba()).map(stomp)

Page 61: Functor, Apply, Applicative And Monad

THE FUNCTOR LAWSMapping preserves identity

If we map the id function over a functor, thefunctor that we get back should be the

same as the original functor

Mapping respects composition

Composing two functions and thenmapping the resulting function over a

functor should be the same as firstmapping one function over the functor and

then mapping the other one

Page 62: Functor, Apply, Applicative And Monad

DOES YOUR FUNCTOR BREAK LAWS?import org.scalacheck.Arbitraryimport org.specs2.scalaz.Specimport scalaz.Equalimport scalaz.scalacheck.ScalazProperties

class CageFunctorSpec extends Spec { implicit val abrCage = Arbitrary[Cage[Int]] { for { ns <- Arbitrary.arbInt.arbitrary } yield Cage(ns) }

implicit val cageEqual = Equal.equal[Cage[Int]]((a, b) => a == b)

checkAll(ScalazProperties.functor.laws[Cage])}

val scalazVersion = "7.1.3"

libraryDependencies ++= Seq( "org.scalaz" %% "scalaz-core" % scalazVersion, "org.specs2" %% "specs2-core" % "2.4" % "test", "org.typelevel" %% "scalaz-specs2" % "0.3.0" % "test")

Page 63: Functor, Apply, Applicative And Monad

REMEMBER THE THREE POINTS?Abstraction: Functor is a abstract conceptGeneralization: There is a set of objects that can bemapped over

What about the context?

Page 64: Functor, Apply, Applicative And Monad

CONTEXTContext is the environment the function is applied in.

Page 65: Functor, Apply, Applicative And Monad
Page 66: Functor, Apply, Applicative And Monad

MEET SOME MORECONTEXTS

Page 67: Functor, Apply, Applicative And Monad

SCALA.OPTIONsealed abstract class Option[+A] extends Product with Serializable {...final def map[B](f: A => B): Option[B] = if (isEmpty) None else Some(f(this.get))...}

Scalaz provides implicits to convert Option to a Functor traitimport scalaz.std.option._import scalaz.std.anyVal._

checkAll(ScalazProperties.functor.laws[Option])

Option is context for a computation that might fail

Page 68: Functor, Apply, Applicative And Monad

LIST ARE ALSO FUNCTORSsealed abstract class List[+A] { ....final def map[B](f: (A) ⇒ B): List[B] ...}

Scalaz provides implicits to convert List to a Functor traitimport scalaz.std.list._import scalaz.std.anyVal._import org.specs2.scalaz.Specimport scalaz.scalacheck.ScalazProperties

class ListFunctorSpec extends Spec { checkAll(ScalazProperties.functor.laws[List])}

If 6 is deterministic and having one value.The List context such as List(1,10,3,4) can be thought of as

having multipule values at once.

Or no values if empty

Page 69: Functor, Apply, Applicative And Monad

DISJUNCTIONS ARE FUNCTORSimport org.specs2.scalaz.Specimport scalaz.scalacheck.ScalazProperties

class ListFunctorSpec extends Spec { implicit val abrStringIntEither = Arbitrary[\/[String, Int]] { for { ns <- Arbitrary.arbInt.arbitrary } yield \/-(ns) }

implicit val disjuncEqual = Equal.equal[\/[String, Int]]((a, b) => { (a,b) match { case(-\/(l1), -\/(l2)) => l1 == l2 case(\/-(r1), \/-(r2)) => r1 == r2 case _ => false } }) //left type param is fixed checkAll(ScalazProperties.functor.laws[({type λ[α] = \/[String, α]})#λ])}

Page 70: Functor, Apply, Applicative And Monad

SO WHY IS THIS SOHANDY?

Page 71: Functor, Apply, Applicative And Monad

ORDINARY FUNCTIONS ARE SIMPLERTO:

readwriteusereason about

Page 72: Functor, Apply, Applicative And Monad

FUNCTIONS IN A CONTEXT HAVEUSEFUL PROPERTIES

Functors let us write ordinary functions

Then promote those functions into every context that mightneed that code

As new contexts arise we just define new functors topromote our ordinary code to work in those contexts.

Page 73: Functor, Apply, Applicative And Monad

///Ordinary functiondef stomp(g:Goomba) = g.stomp()

///The contextsealed trait Cage[T]case class FullCage[T](value: T) extends Cage[T]case class EmptyCage[T]() extends Cage[T]object Cage { def apply[T](x: T):Cage[T] = if (x == null) EmptyCage[T]() else FullCage(x)}

///Promote into contextimport scalaz.Functorimplicit object CageFunctor extends Functor[Cage] { def map[A,B](c:Cage[A])(f:A => B):Cage[B] = c match { case EmptyCage() => EmptyCage[B]() case FullCage(s) => FullCage(f(s)) }}

///useimport scalaz.syntax.functorCage(Goomba()).map(stomp)

Page 74: Functor, Apply, Applicative And Monad

.

Page 75: Functor, Apply, Applicative And Monad

WORLD 1-2Apply Land

Page 76: Functor, Apply, Applicative And Monad

PIRANHA PLANT

Page 77: Functor, Apply, Applicative And Monad

PIRANHA PLANTcase class Coin()case class Fireball()case class PiranhaPlant()

def shoot(plant:PiranhaPlant, fireball:Fireball): Coin = Coin()

Page 78: Functor, Apply, Applicative And Monad

THE PLANT IS GENERATED FROM AUNRELIABLE SOURCE

Wrap the plant in a Cage and map over it?Cage(PiranhaPlant()).map(shoot _)

<console>:31: error: type mismatch;found : (PiranhaPlant, Fireball) => Coinrequired: PiranhaPlant => ? Cage(PiranhaPlant()).map(shoot _) </console>

Page 79: Functor, Apply, Applicative And Monad

CURRING

Page 80: Functor, Apply, Applicative And Monad

CURRING IS PARTIAL APPLICATIONTranslating the evaluation of a function that takes multiple

arguments into evaluating a sequence of functions, eachwith a single argument

(shoot _).curried//res41: PiranhaPlant => (Fireball => Coin) = <function1> </function1>

Page 81: Functor, Apply, Applicative And Monad

MAP THE CURRIED SHOOT FUNCTIONCage(PiranhaPlant()) map {shoot _}.curried//Cage[Fireball => Coin] = ...

Page 82: Functor, Apply, Applicative And Monad

WHAT IF THE FIREBALL PARAMETERGENERATION IS IN A CONTEXT?

Functor only support mapping functions over functordef map[A, B](fa: F[A])(f: A => B): F[B]

We need to map function in a functor over a value in afunctor

Page 83: Functor, Apply, Applicative And Monad

APPLYpackage scalaztrait Apply[F[_]] extends Functor[F] { self => //// def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B] ...}

package scalazpackage syntax

final class ApplyOps[F[_],A] private[syntax](val self: F[A])(implicit val F: Apply[F]) extends Ops[F[A]] { final def <*>[B](f: F[A => B]): F[B] = F.ap(self)(f)...}

trait ToApplyOps extends ToApplyOps0 with ToFunctorOps { implicit def ToApplyOps[F[_],A](v: F[A])(implicit F0: Apply[F]) = new ApplyOps[F,A](v)...}

Page 84: Functor, Apply, Applicative And Monad

WHAT WOULD OUR VACUUM LOOK LIKE?implicit object CageApply extends Apply[Cage]{ override def ap[A, B](fa: => Cage[A]) (fab: => Cage[(A) => B]): Cage[B] = fab match {

case FullCage(f) => fa match { case FullCage(x) => FullCage(f(x)) case EmptyCage() => EmptyCage[B]() } case EmptyCage() => EmptyCage[B]() }

override def map[A, B](fa: Cage[A]) (f: (A) => B): Cage[B] = CageFunctor.map(fa)(f)}

Page 85: Functor, Apply, Applicative And Monad

HOW WOULD YOU USE IT?val partialShoot = Cage(PiranhaPlant()) <*> Cage((shoot _).curried)

val optCoin = Cage(Fireball()) <*> partialShoot//optCoin: Cage[Coin] = FullCage(Coin())

val optCoin = EmptyCage[Fireball]() <*> partialShoot//optCoin: Cage[Coin] = EmptyCage[Coin]()

Page 86: Functor, Apply, Applicative And Monad

WHAT HAVE WE DONE?Taken a function that takes two values.

Turned it into a function that takes two values in a context.

Page 87: Functor, Apply, Applicative And Monad

TESTING THE LAWSimport org.scalacheck.Arbitraryimport org.specs2.scalaz.Specimport scalaz.Equalimport scalaz.scalacheck.ScalazProperties

class CageApplySpec extends Spec {

implicit val abrCage = Arbitrary[Cage[Int]] { for { ns <- Arbitrary.arbInt.arbitrary } yield Cage(ns) }

implicit val arbCageIntToInt = Arbitrary[Cage[Int => Int]] { for{ multi <- Arbitrary.arbInt.arbitrary } yield Cage((x:Int) => x * multi) }

implicit val cageEqual = Equal.equal[Cage[Int]]((a, b) => a == b)

checkAll(ScalazProperties.apply.laws[Cage])}

Page 88: Functor, Apply, Applicative And Monad

OPTION APPLY: OPTIONINSTANCESpackage scalazpackage std

override def ap[A, B](fa: => Option[A]) (f: => Option[A => B]) = f match { case Some(f) => fa match { case Some(x) => Some(f(x)) case None => None } case None => None }

Page 89: Functor, Apply, Applicative And Monad

SHOOT THAT PIRANHA PLANTimport scalaz.std.option._

val partialShoot = Option(PiranhaPlant()) <*> Option((shoot _).curried)val optCoin = Option(Fireball()) <*> partialShoot//optCoin: Option[Coin] = Some(Coin())

Page 90: Functor, Apply, Applicative And Monad

SOME NICER SYNTAXimport scalaz.std.option._import scalaz.syntax.apply._

(̂Option(PiranhaPlant()), Option(Fireball()))(shoot)//res69: Option[Coin] = Some(Coin())

import scalaz.ApplyApply[Option].lift2(shoot)(Option(PiranhaPlant()), Option(Fireball()))//res70: Option[Coin] = Some(Coin())

Page 91: Functor, Apply, Applicative And Monad

LIST AS AN APPLY CONTEXTval partial = List(PiranhaPlant(), PiranhaPlant(), PiranhaPlant()) <*> List((shoot _).curried)List(Fireball()) <*> partial//res23: List[Coin] = List(Coin(), Coin(), Coin())

Page 92: Functor, Apply, Applicative And Monad

DISJUNCTION AS AN APPLY\/.right[String, Goomba](Goomba()) <*> \/.right[String, Goomba => Coin]((_:Goomba) => Coin()) //res: scalaz.\/[String,Coin] = \/-(Coin())

Page 93: Functor, Apply, Applicative And Monad

Apply lets you take a function that takes values and turn itinto a function that takes values in a context.

Write the code once and reuse it in the context you need

Page 94: Functor, Apply, Applicative And Monad

REMEMBER THE THREE POINTS?Abstraction: Apply is a abstract conceptGeneralization: There is a set of objects that implementthe Apply traitContext: How the funciton is used depends on the ApplySpecialization we are using

Page 95: Functor, Apply, Applicative And Monad

.

Page 96: Functor, Apply, Applicative And Monad

WORLD 1-3Applicative

Page 97: Functor, Apply, Applicative And Monad

KOOPA PARATROOPA

Page 98: Functor, Apply, Applicative And Monad

HAS TO BE:1. Koopa Paratroopa shot to a Koopa Troopa2. Koopa Troopa is shot to a Shell3. Shell is shot to a Coin

Page 99: Functor, Apply, Applicative And Monad

.

Page 100: Functor, Apply, Applicative And Monad

THE CODEcase class KoopaParatroopa()case class KoopaTroopa()case class Shell()case class Coin()case class Fireball()

def shootKP(fb: Fireball, kt:KoopaParatroopa) = KoopaTroopa()def shootKT(fb: Fireball, kt:KoopaTroopa) = Shell()def shootS(fb: Fireball, kt:Shell) = Coin()

val cagedKoopa = ̂(Cage(Fireball()), Cage(KoopaParatroopa()))(shootKP)val cagedShell = ̂(Cage(Fireball()), cagedKoopa)(shootKT)val cagedCoin = ̂(Cage(Fireball()), cagedShell)(shootS)//cagedCoin: Cage[Coin] = FullCage(Coin())

Page 101: Functor, Apply, Applicative And Monad

APPLICATIVEtrait Applicative[F[_]] extends Apply[F] { self => //// def point[A](a: => A): F[A] ...}

implicit object CageApplicative extends Applicative[Cage] { override def ap[A, B](fa: => Cage[A]) (fab: => Cage[(A) => B]): Cage[B] = fab match { case FullCage(f) => fa match { case FullCage(x) => FullCage(f(x)) case EmptyCage() => EmptyCage[B]() } case EmptyCage() => EmptyCage[B]() }

override def point[A](a: => A): Cage[A] = Cage(a)}

We no longer need to define mapoverride def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(point(f))

Page 102: Functor, Apply, Applicative And Monad

USING APPLICATIVEimport scalaz.syntax.applicative._val cagedKoopa = ̂(Fireball().point[Cage], KoopaParatroopa().point[Cage])(shootKP)val cagedShell = ̂(Fireball().point[Cage], cagedKoopa)(shootKT)val cagedCoin = ̂(Fireball().point[Cage], cagedShell)(shootS)

//cagedCoin: Cage[Coin] = FullCage(Coin())

Page 103: Functor, Apply, Applicative And Monad

SAME CODE DIFFERENT CONTEXTval cagedKoopa = ̂(Fireball().point[List], KoopaParatroopa().point[List])(shootKP)val cagedShell = ̂(Fireball().point[List], cagedKoopa)(shootKT)val cagedCoin = ̂(Fireball().point[List], cagedShell)(shootS)

//cagedCoin: List[Coin] = List(Coin())

Page 104: Functor, Apply, Applicative And Monad

REMEMBER1. Abstraction: The Applicative2. Generalisation: The Applicative trait3. The context: Different behaviours for the same code

Page 105: Functor, Apply, Applicative And Monad

.

Page 106: Functor, Apply, Applicative And Monad

WORLD 1-4Monad

Page 107: Functor, Apply, Applicative And Monad

BOWSER

Page 108: Functor, Apply, Applicative And Monad

THE RULESMario can hit Bowser

Bowser can hit Mario

Mario dies if at any point hits on Mario > hits on Bowser + 2

Page 109: Functor, Apply, Applicative And Monad

FIRST TRYcase class Hits(mario:Int, bowser:Int)

def hitBowser(hits: Hits) = hits.copy(bowser = hits.bowser + 1)def hitMario(hits: Hits) = hits.copy(mario = hits.mario + 1)

def marioWins = hitMario _ andThen hitBowser andThen hitBowser andThen hitBowser

marioWins(Hits(0,0))//Hits = Hits(1,3)

Page 110: Functor, Apply, Applicative And Monad

HOW ABOUT THIS?def marioWins = hitMario _ andThen hitMario andThen hitMario andThen hitBowser andThen hitBowser andThen hitBowser

marioWins(Hits(0,0))//hits = Hits(3,3)marioWins(Hits(3,0))//marioWins(Hits(6,3))

Mario should have died

Page 111: Functor, Apply, Applicative And Monad

FAILING THE COMPUTATION?Hits => Cage[Hits]

def hitMario2(hits: Hits):Cage[Hits] = hits match { case ko:Hits if ko.mario + 1 - ko.bowser > 2 => EmptyCage[Hits]() case Hits(mario, bowser) => Cage(Hits(mario + 1, bowser))}

def hitBowser2(hits: Hits):Cage[Hits] = hits match { case ko:Hits if ko.mario + 1- ko.bowser > 2 => EmptyCage[Hits]() case Hits(mario, bowser) => Cage(Hits(mario, bowser + 1))}

What's the problem?

Page 112: Functor, Apply, Applicative And Monad

THE HITS ARE TRAPPED IN THE CAGE!!

Page 113: Functor, Apply, Applicative And Monad

MONAD

Page 114: Functor, Apply, Applicative And Monad

MONADtrait Bind[F[_]] extends Apply[F] { self => def bind[A, B](fa: F[A])(f: A => F[B]): F[B]

override def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B] = { lazy val fa0 = fa bind(f)(map(fa0)) } ...}trait Monad[F[_]] extends Applicative[F] with Bind[F] { self =>... override def map[A,B](fa: F[A])(f: A => B) = bind(fa)(a => point(f(a)))...}

Define point and bind and we get map and ap for free

Page 115: Functor, Apply, Applicative And Monad

CAGE MONADimplicit object CageMonad extends Monad[Cage]{ override def bind[A, B](fa: Cage[A])(f: (A) => Cage[B]): Cage[B] = fa match { case FullCage(a) => f(a) case EmptyCage() => EmptyCage[B]() }

override def point[A](a: => A): Cage[A] = Cage(a)}

Page 116: Functor, Apply, Applicative And Monad

BINDOPSfinal class BindOps[F[_],A] private[syntax](val self: F[A]) (implicit val F: Bind[F]) extends Ops[F[A]] {

...

def flatMap[B](f: A => F[B]) = F.bind(self)(f)

def >>=[B](f: A => F[B]) = F.bind(self)(f) ...}

trait ToBindOps extends ToBindOps0 with ToApplyOps { implicit def ToBindOps[F[_],A](v: F[A])(implicit F0: Bind[F]) = new BindOps[F,A](v)}

Page 117: Functor, Apply, Applicative And Monad

NOWimport scalaz.syntax.monad._

Cage(Hits(0,0)) >>= hitMario2 >>= hitMario2 >>= hitMario2 >>= hitBowser2 >>= hitBowser2 >>= hitBowser2//Cage[Hits] = EmptyCage()Cage(Hits(0,2)) >>= hitMario2 >>= hitMario2 >>= hitMario2 >>= hitBowser2 >>= hitBowser2 >>= hitBowser2//Cage[Hits] = FullCage(Hits(3,5))

Page 118: Functor, Apply, Applicative And Monad

FOR COMPREHENSION FLAT MAPval x = for { r1 <- Cage(Hits(0,0)) r2 <- hitMario2(r1) r3 <- hitMario2(r2) r4 <- hitMario2(r3) r5 <- hitBowser2(r4) r6 <- hitBowser2(r5) result <- hitBowser2(r6)} yield result

Page 119: Functor, Apply, Applicative And Monad

OTHER MONADSdef addTwo(x:Int) = Some(x + 2)1.some >>= addTwo//addTwo: (x: Int)List[Int]

def addTwo(x:Int) = List(x + 2)List(1,2,3) >>= addTwo//List[Int] = List(3, 4, 5)

Reader Monad

Writer Monad

State Monad

Page 120: Functor, Apply, Applicative And Monad

MONAD LAWSInherit the laws from Bind and Applicative

Right IdentityLeft Identity

Page 121: Functor, Apply, Applicative And Monad

HANG ON I CAN'T REUSE THESEFUNCTIONS

def hitMario3[F[_]](hits: Hits)(implicit F0: Monad[F]) :F[Hits] = hits match { case ko:Hits if ko.mario + 1 - ko.bowser > 2 => F0.point(null: Hits) case Hits(mario, bowser) => F0.point(Hits(mario + 1, bowser))}

def hitBowser3[F[_]](hits: Hits)(implicit F0: Monad[F]) :F[Hits] = hits match { case ko:Hits if ko.mario + 1- ko.bowser > 2 => F0.point(null: Hits) case Hits(mario, bowser) => F0.point(Hits(mario, bowser + 1))}

Cage(Hits(1,2)) >>= hitMario3[Cage]//Cage[Hits] = FullCage(Hits(2,2))Cage(Hits(1,2)) >>= hitBowser3[Cage]//Cage[Hits] = FullCage(Hits(1,3))

Page 122: Functor, Apply, Applicative And Monad

MONADLets us call a function that takes a value and returns a value

in a context with a value in a context.

Page 123: Functor, Apply, Applicative And Monad

REMEMBER1. Abstraction: The Monad2. Generalisation: The Monad trait3. The context: Different behaviours for the same code

Page 124: Functor, Apply, Applicative And Monad

SUMMARY

Functor

Apply

Applicative

Monad

def map[A, B](fa: F[A])(f: A => B): F[B]

def ap[A,B](fa: => F[A]) (f: => F[A => B]): F[B]

def point[A](a: => A): F[A]

def bind[A, B](fa: F[A]) (f: A => F[B]): F[B]

Page 125: Functor, Apply, Applicative And Monad

REFERENCESLearn you a Haskell for Greater Good

Leaning Scalaz Functional Programming in Scala

Bartosz Blog

http://learnyouahaskell.com/http://eed3si9n.com/learning-scalaz/

https://www.manning.com/books/functional-programming-in-scala

http://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/

Page 126: Functor, Apply, Applicative And Monad

.