functor, apply, applicative and monad

Post on 17-Feb-2017

1.413 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

FUNCTORS, APPLY,APPLICATIVE AND

MONADS

THAT'S JUST DULL

SO LET'S TRY INSTEAD ...

.

WHY SHOULD YOUCARE?

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

3 CONCEPTS

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

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

TO RENT FOR TWO DAYS?

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?

.

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.

WHAT IS THE REALATION SHIPBETWEEN A POLOGON:

A 3 sided triangleA 4 sided quadrilateral

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

.

GENERALIZATIONRelationship that holds between all members of some set of

objects

IS A MILE A LONG WAY?

IS A YEAR A LARGE AMOUNT OFTIME?

.

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.

TRAINING LEVEL

WHAT IS FUNCTIONALPROGRAMMING?

Construct our programs using only purefunctions.

Pure functions have no side effects.

WHY IS A FUNCTIONLIKE A PIPE?

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

Simple pipes simple can be joined together to form complexsystems?

WHAT'S SO GOOD ABOUT NO SIDEEFFECTS?

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

IT'S IMPORTANT THAT FUNCTIONSLIKE PIPES DON'T LEAK

WORLD 1-1Functor Land

GOOMBA PROBLEM

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.

LUIGIS VACUUM TO COLLECTGOOMBAS

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

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

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

vacuum.collect(null)

.

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.

There must be a better way

SOLUTIONPut the Goomba in a Cage

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]()

.

CAN WE GENERALIZETHE VACUUM CLASS?

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))

}}

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)) }}

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)) }}

.

THE FUNCTORA functor is basically for things that can be

mapped over.

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] ...}

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)

IS THERE A BETTERWAY?

.

TYPE CLASSES IN 60SECONDS

WHY?Extend existing classesWithout inheritanceWithout altering original sourceKeeps concerns seperate

HOW?3 COMPONENTS

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

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

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 ...}

INTERFACESWhat is exposed to clients.

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

TWO WAYS OF DOING IT

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())

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

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))}

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

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]

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

FINALLYscalaz.syntax package object extends Syntaxes

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

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)

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

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")

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

What about the context?

CONTEXTContext is the environment the function is applied in.

MEET SOME MORECONTEXTS

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

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

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, α]})#λ])}

SO WHY IS THIS SOHANDY?

ORDINARY FUNCTIONS ARE SIMPLERTO:

readwriteusereason about

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.

///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)

.

WORLD 1-2Apply Land

PIRANHA PLANT

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

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

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>

CURRING

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>

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

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

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)...}

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)}

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]()

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

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

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])}

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 }

SHOOT THAT PIRANHA PLANTimport scalaz.std.option._

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

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())

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

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

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

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

.

WORLD 1-3Applicative

KOOPA PARATROOPA

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

.

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())

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))

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())

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())

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

.

WORLD 1-4Monad

BOWSER

THE RULESMario can hit Bowser

Bowser can hit Mario

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

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)

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

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?

THE HITS ARE TRAPPED IN THE CAGE!!

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

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)}

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)}

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))

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

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

MONAD LAWSInherit the laws from Bind and Applicative

Right IdentityLeft Identity

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))

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

in a context with a value in a context.

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

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]

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/

.

top related