SlideShare une entreprise Scribd logo
1  sur  126
Télécharger pour lire hors ligne
FUNCTORS, APPLY,
APPLICATIVE AND
MONADS
THAT'S JUST DULL
SO LET'S TRY INSTEAD ...
.
WHY SHOULD YOU
CARE?
How do Functors, Apply, Applicative and Monad help you
achieve early and continuous delivery of software?
3 CONCEPTS
IF A PIZZA COSTS $16 AND YOU BUY
TWO HOW MUCH DO YOU SPEND?
A MARIO KART COSTS $20 PER DAY
TO RENT. HOW MUCH DOES IT COST
TO RENT FOR TWO DAYS?
DON'T JUST THINK ABOUT THE
MATHS
Think about the process you are going through before you
get to the maths.
What information are you keeping and what information are
you dropping?
.
ABSTRACTION
Abstraction is an emphasis on the idea, qualities and
properties rather than the particulars
The importance of abstraction is derived from its ability to
hide irrelevant details
It doesn't matter if its pizza, mario carts or anything else we
take the cost and muliply it by some factor.
WHAT IS THE REALATION SHIP
BETWEEN A POLOGON:
A 3 sided triangle
A 4 sided quadrilateral
WHAT HAS AM × AN = AM+N GOT IN
COMMON WITH:
a2 × a3 = (a × a) × (a × a × a) = a5
a3 × a4 = (a × a × a) × (a × a × a × a) = a7
.
GENERALIZATION
Relationship that holds between all members of some set of
objects
IS A MILE A LONG WAY?
IS A YEAR A LARGE AMOUNT OF
TIME?
.
IT ALL DEPENDS ON
CONTEXT
A mile is along way if you are an Ant but not if you are
flying in an aeroplane.
A year is a long time for a Mayfly that lives for only 5
minutes. But in geological time it's insignificant.
TRAINING LEVEL
WHAT IS FUNCTIONAL
PROGRAMMING?
Construct our programs using only pure
functions.
Pure functions have no side effects.
WHY IS A FUNCTION
LIKE A PIPE?
Some thing goes into one end and something else comes
out the other end
Simple pipes simple can be joined together to form complex
systems?
WHAT'S SO GOOD ABOUT NO SIDE
EFFECTS?
It makes it easier to reason about what's going on
IT'S IMPORTANT THAT FUNCTIONS
LIKE PIPES DON'T LEAK
WORLD 1-1
Functor Land
GOOMBA PROBLEM
HOW TO DEFEAT A GOOMBA
Stomp it and it turns into a coin
case class Coin()
case class Goomba()
def stomp(g:Goomba) = Coin()
The function stomp is our pipe, that transforms from a
Goomba to a Coin.
LUIGIS VACUUM TO COLLECT
GOOMBAS
class Vacuum {
def collect(g:Goomba) = stomp(s)
}
val vacuum = new Vacuum()
val goomba = new Goomba()
vacuum.collect(goomba)
//Coin()
WHAT HAPPENS WHEN THE
GOOMBA ESCAPES THE SUCTION?
val vacuum = new Vacuum()
vacuum.collect(null)
.
CHECK FOR NULLS
class Vacuum {
def collect(s:Goomba) = if (s == null) null else stomp(s)
}
But then the calling class runs the risk of NullPointer
exceptions.
There must be a better way
SOLUTION
Put 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 GENERALIZE
THE VACUUM CLASS?
WHY LIMIT OURSELF TO JUST
STOMPING 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 trait
trait 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 trait
trait 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 FUNCTOR
A functor is basically for things that can be
mapped over.
THE FUNCTOR
DEFINITION IN SCALAZ
package 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 BETTER
WAY?
.
TYPE CLASSES IN 60
SECONDS
WHY?
Extend existing classes
Without inheritance
Without altering original source
Keeps concerns seperate
HOW?
3 COMPONENTS
1. The type class
2. Instances for particular types
3. Interface methods for the api
THE TYPE CLASS
Provide a generic type of what we want to implement.
trait ProtoBuffWriter[A] {
def write(value: A): Array[Byte]
}
TYPE CLASS INSTANCES
Provide implementations for the types we care about.
Create concrete implementations of the type class and mark
them as implicit.
object DefaultProtoBuffWriters {
implicit val coinWriter = ProtoBuffWriter[Coin] { .... }
implicit val goombaWriter = ProtoBuffWriter[Goomba] { .... }
// etc ...
}
INTERFACES
What is exposed to clients.
Generic methods that accept instances of the type class as
implicit params.
TWO WAYS OF DOING IT
INTERFACE OBJECTS
All 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 SYNTAX
Pimp 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 methods
import scala.language.implicitConversions
sealed 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 only
class 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.scala
trait 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 the
FunctorOps to F[A]
SCALAZ FUNCTOR SYNTAX:
FUNCTOROPS
scalaz.syntax.FunctorSyntax.scala
final 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 the
map method to the Functor[F] in scope
FINALLY
scalaz.syntax package object extends Syntaxes
trait Syntaxes {
object functor extends ToFunctorOps
}
import scalaz.syntax.functor
CAGE FUNCTOR AGAIN
scalaz.syntax.FunctorSyntax.scala
import scalaz.Functor
implicit 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.functor
Cage(Goomba()).map(stomp)
THE FUNCTOR LAWS
Mapping preserves identity
If we map the id function over a functor, the
functor that we get back should be the
same as the original functor
Mapping respects composition
Composing two functions and then
mapping the resulting function over a
functor should be the same as first
mapping one function over the functor and
then mapping the other one
DOES YOUR FUNCTOR BREAK LAWS?
import org.scalacheck.Arbitrary
import org.specs2.scalaz.Spec
import scalaz.Equal
import 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 concept
Generalization: There is a set of objects that can be
mapped over
What about the context?
CONTEXT
Context is the environment the function is applied in.
MEET SOME MORE
CONTEXTS
SCALA.OPTION
sealed 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 trait
import scalaz.std.option._
import scalaz.std.anyVal._
checkAll(ScalazProperties.functor.laws[Option])
Option is context for a computation that might fail
LIST ARE ALSO FUNCTORS
sealed abstract class List[+A] { ....
final def map[B](f: (A) ⇒ B): List[B]
...
}
Scalaz provides implicits to convert List to a Functor trait
import scalaz.std.list._
import scalaz.std.anyVal._
import org.specs2.scalaz.Spec
import 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 FUNCTORS
import org.specs2.scalaz.Spec
import 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 SO
HANDY?
ORDINARY FUNCTIONS ARE SIMPLER
TO:
read
write
use
reason about
FUNCTIONS IN A CONTEXT HAVE
USEFUL PROPERTIES
Functors let us write ordinary functions
Then promote those functions into every context that might
need that code
As new contexts arise we just define new functors to
promote our ordinary code to work in those contexts.
///Ordinary function
def stomp(g:Goomba) = g.stomp()
///The context
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)
}
///Promote into context
import scalaz.Functor
implicit 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))
}
}
///use
import scalaz.syntax.functor
Cage(Goomba()).map(stomp)
.
WORLD 1-2
Apply Land
PIRANHA PLANT
PIRANHA PLANT
case class Coin()
case class Fireball()
case class PiranhaPlant()
def shoot(plant:PiranhaPlant, fireball:Fireball): Coin = Coin()
THE PLANT IS GENERATED FROM A
UNRELIABLE SOURCE
Wrap the plant in a Cage and map over it?
Cage(PiranhaPlant()).map(shoot _)
<console>:31: error: type mismatch;
found : (PiranhaPlant, Fireball) => Coin
required: PiranhaPlant => ?
Cage(PiranhaPlant()).map(shoot _)
</console>
CURRING
CURRING IS PARTIAL APPLICATION
Translating the evaluation of a function that takes multiple
arguments into evaluating a sequence of functions, each
with a single argument
(shoot _).curried
//res41: PiranhaPlant => (Fireball => Coin) = <function1>
</function1>
MAP THE CURRIED SHOOT FUNCTION
Cage(PiranhaPlant()) map {shoot _}.curried
//Cage[Fireball => Coin] = ...
WHAT IF THE FIREBALL PARAMETER
GENERATION IS IN A CONTEXT?
Functor only support mapping functions over functor
def map[A, B](fa: F[A])(f: A => B): F[B]
We need to map function in a functor over a value in a
functor
APPLY
package scalaz
trait Apply[F[_]] extends Functor[F] { self =>
////
def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B]
...
}
package scalaz
package 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 LAWS
import org.scalacheck.Arbitrary
import org.specs2.scalaz.Spec
import scalaz.Equal
import 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: OPTIONINSTANCES
package scalaz
package 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 PLANT
import scalaz.std.option._
val partialShoot =
Option(PiranhaPlant()) <*> Option((shoot _).curried)
val optCoin = Option(Fireball()) <*> partialShoot
//optCoin: Option[Coin] = Some(Coin())
SOME NICER SYNTAX
import scalaz.std.option._
import scalaz.syntax.apply._
^(Option(PiranhaPlant()), Option(Fireball()))(shoot)
//res69: Option[Coin] = Some(Coin())
import scalaz.Apply
Apply[Option]
.lift2(shoot)(Option(PiranhaPlant()), Option(Fireball()))
//res70: Option[Coin] = Some(Coin())
LIST AS AN APPLY CONTEXT
val 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 it
into 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 concept
Generalization: There is a set of objects that implement
the Apply trait
Context: How the funciton is used depends on the Apply
Specialization we are using
.
WORLD 1-3
Applicative
KOOPA PARATROOPA
HAS TO BE:
1. Koopa Paratroopa shot to a Koopa Troopa
2. Koopa Troopa is shot to a Shell
3. Shell is shot to a Coin
.
THE CODE
case 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())
APPLICATIVE
trait 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 map
override def map[A, B](fa: F[A])(f: A => B): F[B] =
ap(fa)(point(f))
USING APPLICATIVE
import 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 CONTEXT
val 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())
REMEMBER
1. Abstraction: The Applicative
2. Generalisation: The Applicative trait
3. The context: Different behaviours for the same code
.
WORLD 1-4
Monad
BOWSER
THE RULES
Mario can hit Bowser
Bowser can hit Mario
Mario dies if at any point hits on Mario > hits on Bowser + 2
FIRST TRY
case 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
MONAD
trait 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 MONAD
implicit 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)
}
BINDOPS
final 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)
}
NOW
import 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 MAP
val 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 MONADS
def 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 LAWS
Inherit the laws from Bind and Applicative
Right Identity
Left Identity
HANG ON I CAN'T REUSE THESE
FUNCTIONS
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))
MONAD
Lets us call a function that takes a value and returns a value
in a context with a value in a context.
REMEMBER
1. Abstraction: The Monad
2. Generalisation: The Monad trait
3. 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]
REFERENCES
Learn 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/
.

Contenu connexe

Tendances

Tendances (20)

Exploring ZIO Prelude: The game changer for typeclasses in Scala
Exploring ZIO Prelude: The game changer for typeclasses in ScalaExploring ZIO Prelude: The game changer for typeclasses in Scala
Exploring ZIO Prelude: The game changer for typeclasses in Scala
 
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
N-Queens Combinatorial Problem - Polyglot FP for Fun and Profit – Haskell and...
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
 
Applicative Functor
Applicative FunctorApplicative Functor
Applicative Functor
 
Left and Right Folds - Comparison of a mathematical definition and a programm...
Left and Right Folds- Comparison of a mathematical definition and a programm...Left and Right Folds- Comparison of a mathematical definition and a programm...
Left and Right Folds - Comparison of a mathematical definition and a programm...
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1
 
Zio in real world
Zio in real worldZio in real world
Zio in real world
 
Lambda Expressions in Java 8
Lambda Expressions in Java 8Lambda Expressions in Java 8
Lambda Expressions in Java 8
 
Thirteen ways of looking at a turtle
Thirteen ways of looking at a turtleThirteen ways of looking at a turtle
Thirteen ways of looking at a turtle
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
 
Why functional programming and category theory strongly matters
Why functional programming and category theory strongly mattersWhy functional programming and category theory strongly matters
Why functional programming and category theory strongly matters
 
Idiomatic Kotlin
Idiomatic KotlinIdiomatic Kotlin
Idiomatic Kotlin
 
Production-ready GraphQL with Caliban
Production-ready GraphQL with CalibanProduction-ready GraphQL with Caliban
Production-ready GraphQL with Caliban
 
Functional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayFunctional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 Way
 
Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)Functional Design Patterns (DevTernity 2018)
Functional Design Patterns (DevTernity 2018)
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
 

Similaire à Functor, Apply, Applicative And Monad

Столпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай МозговойСтолпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай Мозговой
Sigma Software
 

Similaire à Functor, Apply, Applicative And Monad (20)

Things about Functional JavaScript
Things about Functional JavaScriptThings about Functional JavaScript
Things about Functional JavaScript
 
Столпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай МозговойСтолпы функционального программирования для адептов ООП, Николай Мозговой
Столпы функционального программирования для адептов ООП, Николай Мозговой
 
Contravariant functors in scala
Contravariant functors in scalaContravariant functors in scala
Contravariant functors in scala
 
Journey of a C# developer into Javascript
Journey of a C# developer into JavascriptJourney of a C# developer into Javascript
Journey of a C# developer into Javascript
 
Coding in Style
Coding in StyleCoding in Style
Coding in Style
 
Writing DSL with Applicative Functors
Writing DSL with Applicative FunctorsWriting DSL with Applicative Functors
Writing DSL with Applicative Functors
 
Extreme Swift
Extreme SwiftExtreme Swift
Extreme Swift
 
Talk - Query monad
Talk - Query monad Talk - Query monad
Talk - Query monad
 
Javascript And J Query
Javascript And J QueryJavascript And J Query
Javascript And J Query
 
Cocoa Design Patterns in Swift
Cocoa Design Patterns in SwiftCocoa Design Patterns in Swift
Cocoa Design Patterns in Swift
 
Introduction to Client-Side Javascript
Introduction to Client-Side JavascriptIntroduction to Client-Side Javascript
Introduction to Client-Side Javascript
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
Scala for curious
Scala for curiousScala for curious
Scala for curious
 
K is for Kotlin
K is for KotlinK is for Kotlin
K is for Kotlin
 
Writer Monad for logging execution of functions
Writer Monad for logging execution of functionsWriter Monad for logging execution of functions
Writer Monad for logging execution of functions
 
SystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features SummarySystemVerilog OOP Ovm Features Summary
SystemVerilog OOP Ovm Features Summary
 
Scala ntnu
Scala ntnuScala ntnu
Scala ntnu
 
EcmaScript unchained
EcmaScript unchainedEcmaScript unchained
EcmaScript unchained
 
Joy of scala
Joy of scalaJoy of scala
Joy of scala
 
Advanced JavaScript
Advanced JavaScript Advanced JavaScript
Advanced JavaScript
 

Dernier

The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
shinachiaurasa2
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
VishalKumarJha10
 

Dernier (20)

%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdfThe Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
BUS PASS MANGEMENT SYSTEM USING PHP.pptx
BUS PASS MANGEMENT SYSTEM USING PHP.pptxBUS PASS MANGEMENT SYSTEM USING PHP.pptx
BUS PASS MANGEMENT SYSTEM USING PHP.pptx
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 

Functor, Apply, Applicative And Monad

  • 3. SO LET'S TRY INSTEAD ...
  • 4. .
  • 5. WHY SHOULD YOU CARE? How do Functors, Apply, Applicative and Monad help you achieve early and continuous delivery of software?
  • 7. IF A PIZZA COSTS $16 AND YOU BUY TWO HOW MUCH DO YOU SPEND?
  • 8. A MARIO KART COSTS $20 PER DAY TO RENT. HOW MUCH DOES IT COST TO RENT FOR TWO DAYS?
  • 9. DON'T JUST THINK ABOUT THE MATHS Think about the process you are going through before you get to the maths. What information are you keeping and what information are you dropping?
  • 10. .
  • 11. ABSTRACTION Abstraction is an emphasis on the idea, qualities and properties rather than the particulars The importance of abstraction is derived from its ability to hide irrelevant details It doesn't matter if its pizza, mario carts or anything else we take the cost and muliply it by some factor.
  • 12. WHAT IS THE REALATION SHIP BETWEEN A POLOGON: A 3 sided triangle A 4 sided quadrilateral
  • 13. WHAT HAS AM × AN = AM+N GOT IN COMMON WITH: a2 × a3 = (a × a) × (a × a × a) = a5 a3 × a4 = (a × a × a) × (a × a × a × a) = a7
  • 14. .
  • 15. GENERALIZATION Relationship that holds between all members of some set of objects
  • 16. IS A MILE A LONG WAY?
  • 17. IS A YEAR A LARGE AMOUNT OF TIME?
  • 18. .
  • 19. IT ALL DEPENDS ON CONTEXT A mile is along way if you are an Ant but not if you are flying in an aeroplane. A year is a long time for a Mayfly that lives for only 5 minutes. But in geological time it's insignificant.
  • 21. WHAT IS FUNCTIONAL PROGRAMMING? Construct our programs using only pure functions. Pure functions have no side effects.
  • 22. WHY IS A FUNCTION LIKE A PIPE? Some thing goes into one end and something else comes out the other end
  • 23. Simple pipes simple can be joined together to form complex systems?
  • 24. WHAT'S SO GOOD ABOUT NO SIDE EFFECTS? It makes it easier to reason about what's going on
  • 25. IT'S IMPORTANT THAT FUNCTIONS LIKE PIPES DON'T LEAK
  • 28. HOW TO DEFEAT A GOOMBA Stomp it and it turns into a coin case class Coin() case class Goomba() def stomp(g:Goomba) = Coin() The function stomp is our pipe, that transforms from a Goomba to a Coin.
  • 29. LUIGIS VACUUM TO COLLECT GOOMBAS class Vacuum { def collect(g:Goomba) = stomp(s) } val vacuum = new Vacuum() val goomba = new Goomba() vacuum.collect(goomba) //Coin()
  • 30. WHAT HAPPENS WHEN THE GOOMBA ESCAPES THE SUCTION? val vacuum = new Vacuum() vacuum.collect(null)
  • 31. .
  • 32. CHECK FOR NULLS class Vacuum { def collect(s:Goomba) = if (s == null) null else stomp(s) } But then the calling class runs the risk of NullPointer exceptions.
  • 33. There must be a better way
  • 35. 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]()
  • 36. .
  • 37. CAN WE GENERALIZE THE VACUUM CLASS?
  • 38. WHY LIMIT OURSELF TO JUST STOMPING 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)) } }
  • 39. Turn it into a trait trait 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)) } }
  • 40. Parameterise the trait trait 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)) } }
  • 41. .
  • 42. THE FUNCTOR A functor is basically for things that can be mapped over.
  • 43. THE FUNCTOR DEFINITION IN SCALAZ package 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] ... }
  • 44. 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)
  • 45. IS THERE A BETTER WAY?
  • 46. .
  • 47. TYPE CLASSES IN 60 SECONDS
  • 48. WHY? Extend existing classes Without inheritance Without altering original source Keeps concerns seperate
  • 49. HOW? 3 COMPONENTS 1. The type class 2. Instances for particular types 3. Interface methods for the api
  • 50. THE TYPE CLASS Provide a generic type of what we want to implement. trait ProtoBuffWriter[A] { def write(value: A): Array[Byte] }
  • 51. TYPE CLASS INSTANCES Provide implementations for the types we care about. Create concrete implementations of the type class and mark them as implicit. object DefaultProtoBuffWriters { implicit val coinWriter = ProtoBuffWriter[Coin] { .... } implicit val goombaWriter = ProtoBuffWriter[Goomba] { .... } // etc ... }
  • 52. INTERFACES What is exposed to clients. Generic methods that accept instances of the type class as implicit params. TWO WAYS OF DOING IT
  • 53. INTERFACE OBJECTS All 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())
  • 54. INTERFACE SYNTAX Pimp 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
  • 55. WHAT ABOUT SCALAZ? Pimp existing types Uses the Type classes in Ops classes Ops classes use the Type class and provide more methods import scala.language.implicitConversions sealed 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 only class 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)) }
  • 56. !!WARNING!! Implicits like warp pipes can be dangerous
  • 57. SCALAZ FUNCTOR SYNTAX: TOFUNCTOROPS scalaz.syntax.FunctorSyntax.scala trait 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 the FunctorOps to F[A]
  • 58. SCALAZ FUNCTOR SYNTAX: FUNCTOROPS scalaz.syntax.FunctorSyntax.scala final 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 the map method to the Functor[F] in scope
  • 59. FINALLY scalaz.syntax package object extends Syntaxes trait Syntaxes { object functor extends ToFunctorOps } import scalaz.syntax.functor
  • 60. CAGE FUNCTOR AGAIN scalaz.syntax.FunctorSyntax.scala import scalaz.Functor implicit 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.functor Cage(Goomba()).map(stomp)
  • 61. THE FUNCTOR LAWS Mapping preserves identity If we map the id function over a functor, the functor that we get back should be the same as the original functor Mapping respects composition Composing two functions and then mapping the resulting function over a functor should be the same as first mapping one function over the functor and then mapping the other one
  • 62. DOES YOUR FUNCTOR BREAK LAWS? import org.scalacheck.Arbitrary import org.specs2.scalaz.Spec import scalaz.Equal import 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" )
  • 63. REMEMBER THE THREE POINTS? Abstraction: Functor is a abstract concept Generalization: There is a set of objects that can be mapped over What about the context?
  • 64. CONTEXT Context is the environment the function is applied in.
  • 65.
  • 67. SCALA.OPTION sealed 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 trait import scalaz.std.option._ import scalaz.std.anyVal._ checkAll(ScalazProperties.functor.laws[Option]) Option is context for a computation that might fail
  • 68. LIST ARE ALSO FUNCTORS sealed abstract class List[+A] { .... final def map[B](f: (A) ⇒ B): List[B] ... } Scalaz provides implicits to convert List to a Functor trait import scalaz.std.list._ import scalaz.std.anyVal._ import org.specs2.scalaz.Spec import 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
  • 69. DISJUNCTIONS ARE FUNCTORS import org.specs2.scalaz.Spec import 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, α]})#λ]) }
  • 70. SO WHY IS THIS SO HANDY?
  • 71. ORDINARY FUNCTIONS ARE SIMPLER TO: read write use reason about
  • 72. FUNCTIONS IN A CONTEXT HAVE USEFUL PROPERTIES Functors let us write ordinary functions Then promote those functions into every context that might need that code As new contexts arise we just define new functors to promote our ordinary code to work in those contexts.
  • 73. ///Ordinary function def stomp(g:Goomba) = g.stomp() ///The context 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) } ///Promote into context import scalaz.Functor implicit 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)) } } ///use import scalaz.syntax.functor Cage(Goomba()).map(stomp)
  • 74. .
  • 77. PIRANHA PLANT case class Coin() case class Fireball() case class PiranhaPlant() def shoot(plant:PiranhaPlant, fireball:Fireball): Coin = Coin()
  • 78. THE PLANT IS GENERATED FROM A UNRELIABLE SOURCE Wrap the plant in a Cage and map over it? Cage(PiranhaPlant()).map(shoot _) <console>:31: error: type mismatch; found : (PiranhaPlant, Fireball) => Coin required: PiranhaPlant => ? Cage(PiranhaPlant()).map(shoot _) </console>
  • 80. CURRING IS PARTIAL APPLICATION Translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument (shoot _).curried //res41: PiranhaPlant => (Fireball => Coin) = <function1> </function1>
  • 81. MAP THE CURRIED SHOOT FUNCTION Cage(PiranhaPlant()) map {shoot _}.curried //Cage[Fireball => Coin] = ...
  • 82. WHAT IF THE FIREBALL PARAMETER GENERATION IS IN A CONTEXT? Functor only support mapping functions over functor def map[A, B](fa: F[A])(f: A => B): F[B] We need to map function in a functor over a value in a functor
  • 83. APPLY package scalaz trait Apply[F[_]] extends Functor[F] { self => //// def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B] ... } package scalaz package 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) ... }
  • 84. 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) }
  • 85. 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]()
  • 86. WHAT HAVE WE DONE? Taken a function that takes two values. Turned it into a function that takes two values in a context.
  • 87. TESTING THE LAWS import org.scalacheck.Arbitrary import org.specs2.scalaz.Spec import scalaz.Equal import 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]) }
  • 88. OPTION APPLY: OPTIONINSTANCES package scalaz package 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 }
  • 89. SHOOT THAT PIRANHA PLANT import scalaz.std.option._ val partialShoot = Option(PiranhaPlant()) <*> Option((shoot _).curried) val optCoin = Option(Fireball()) <*> partialShoot //optCoin: Option[Coin] = Some(Coin())
  • 90. SOME NICER SYNTAX import scalaz.std.option._ import scalaz.syntax.apply._ ^(Option(PiranhaPlant()), Option(Fireball()))(shoot) //res69: Option[Coin] = Some(Coin()) import scalaz.Apply Apply[Option] .lift2(shoot)(Option(PiranhaPlant()), Option(Fireball())) //res70: Option[Coin] = Some(Coin())
  • 91. LIST AS AN APPLY CONTEXT val partial = List(PiranhaPlant(), PiranhaPlant(), PiranhaPlant()) <*> List((shoot _).curried) List(Fireball()) <*> partial //res23: List[Coin] = List(Coin(), Coin(), Coin())
  • 92. DISJUNCTION AS AN APPLY /.right[String, Goomba](Goomba()) <*> /.right[String, Goomba => Coin]((_:Goomba) => Coin()) //res: scalaz./[String,Coin] = /-(Coin())
  • 93. Apply lets you take a function that takes values and turn it into a function that takes values in a context. Write the code once and reuse it in the context you need
  • 94. REMEMBER THE THREE POINTS? Abstraction: Apply is a abstract concept Generalization: There is a set of objects that implement the Apply trait Context: How the funciton is used depends on the Apply Specialization we are using
  • 95. .
  • 98. HAS TO BE: 1. Koopa Paratroopa shot to a Koopa Troopa 2. Koopa Troopa is shot to a Shell 3. Shell is shot to a Coin
  • 99. .
  • 100. THE CODE case 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())
  • 101. APPLICATIVE trait 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 map override def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(point(f))
  • 102. USING APPLICATIVE import 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())
  • 103. SAME CODE DIFFERENT CONTEXT val 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())
  • 104. REMEMBER 1. Abstraction: The Applicative 2. Generalisation: The Applicative trait 3. The context: Different behaviours for the same code
  • 105. .
  • 107. BOWSER
  • 108. THE RULES Mario can hit Bowser Bowser can hit Mario Mario dies if at any point hits on Mario > hits on Bowser + 2
  • 109. FIRST TRY case 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)
  • 110. 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
  • 111. 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?
  • 112. THE HITS ARE TRAPPED IN THE CAGE!!
  • 113. MONAD
  • 114. MONAD trait 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
  • 115. CAGE MONAD implicit 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) }
  • 116. BINDOPS final 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) }
  • 117. NOW import 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))
  • 118. FOR COMPREHENSION FLAT MAP val 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
  • 119. OTHER MONADS def 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
  • 120. MONAD LAWS Inherit the laws from Bind and Applicative Right Identity Left Identity
  • 121. HANG ON I CAN'T REUSE THESE FUNCTIONS 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))
  • 122. MONAD Lets us call a function that takes a value and returns a value in a context with a value in a context.
  • 123. REMEMBER 1. Abstraction: The Monad 2. Generalisation: The Monad trait 3. The context: Different behaviours for the same code
  • 124. 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]
  • 125. REFERENCES Learn 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/
  • 126. .