Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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 softw...
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 informati...
.
ABSTRACTION
Abstraction is an emphasis on the idea, qualities and
properties rather than the particulars
The importance of...
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) =...
.
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 lon...
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(...
LUIGIS VACUUM TO COLLECT
GOOMBAS
class Vacuum {
def collect(g:Goomba) = stomp(s)
}
val vacuum = new Vacuum()
val goomba = ...
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...
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 Cag...
.
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 m...
Turn it into a trait
trait Collector {
def collect[A,B](c:Cage[A], f:A => B):Cage[B]
}
class Vacuum extends Collector {
de...
Parameterise the trait
trait Collector[F[_]] {
def collect[A,B](c:F[A], f:A => B): F[B]
}
object Vacuum extends Collector[...
.
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`...
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 E...
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[...
TYPE CLASS INSTANCES
Provide implementations for the types we care about.
Create concrete implementations of the type clas...
INTERFACES
What is exposed to clients.
Generic methods that accept instances of the type class as
implicit params.
TWO WAY...
INTERFACE OBJECTS
All methods in a singleton.
object ProtoBuff {
def toProtoBuff[A](value: A)
(implicit writer: ProtoBuffW...
INTERFACE SYNTAX
Pimp existing types with interface methods.
object ProtoBuffSyntax {
implicit class ProtoBuffWriter[A](va...
WHAT ABOUT SCALAZ?
Pimp existing types
Uses the Type classes in Ops classes
Ops classes use the Type class and provide mor...
!!WARNING!!
Implicits like warp pipes can be dangerous
SCALAZ FUNCTOR SYNTAX:
TOFUNCTOROPS
scalaz.syntax.FunctorSyntax.scala
trait ToFunctorOps extends ToFunctorOps0 with ToInva...
SCALAZ FUNCTOR SYNTAX:
FUNCTOROPS
scalaz.syntax.FunctorSyntax.scala
final class FunctorOps[F[_],A] private[syntax]
(val se...
FINALLY
scalaz.syntax package object extends Syntaxes
trait Syntaxes {
object functor extends ToFunctorOps
}
import scalaz...
CAGE FUNCTOR AGAIN
scalaz.syntax.FunctorSyntax.scala
import scalaz.Functor
implicit object CageFunctor extends Functor[Cag...
THE FUNCTOR LAWS
Mapping preserves identity
If we map the id function over a functor, the
functor that we get back should ...
DOES YOUR FUNCTOR BREAK LAWS?
import org.scalacheck.Arbitrary
import org.specs2.scalaz.Spec
import scalaz.Equal
import sca...
REMEMBER THE THREE POINTS?
Abstraction: Functor is a abstract concept
Generalization: There is a set of objects that can b...
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[...
LIST ARE ALSO FUNCTORS
sealed abstract class List[+A] { ....
final def map[B](f: (A) ⇒ B): List[B]
...
}
Scalaz provides i...
DISJUNCTIONS ARE FUNCTORS
import org.specs2.scalaz.Spec
import scalaz.scalacheck.ScalazProperties
class ListFunctorSpec ex...
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 e...
///Ordinary function
def stomp(g:Goomba) = g.stomp()
///The context
sealed trait Cage[T]
case class FullCage[T](value: T) ...
.
WORLD 1-2
Apply Land
PIRANHA PLANT
PIRANHA PLANT
case class Coin()
case class Fireball()
case class PiranhaPlant()
def shoot(plant:PiranhaPlant, fireball:Fir...
THE PLANT IS GENERATED FROM A
UNRELIABLE SOURCE
Wrap the plant in a Cage and map over it?
Cage(PiranhaPlant()).map(shoot _...
CURRING
CURRING IS PARTIAL APPLICATION
Translating the evaluation of a function that takes multiple
arguments into evaluating a se...
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, ...
APPLY
package scalaz
trait Apply[F[_]] extends Functor[F] { self =>
////
def ap[A,B](fa: => F[A])(f: => F[A => B]): F[B]
....
WHAT WOULD OUR VACUUM LOOK LIKE?
implicit object CageApply extends Apply[Cage]{
override def ap[A, B](fa: => Cage[A])
(fab...
HOW WOULD YOU USE IT?
val partialShoot = Cage(PiranhaPlant()) <*> Cage((shoot _).curried)
val optCoin = Cage(Fireball()) <...
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.scalachec...
OPTION APPLY: OPTIONINSTANCES
package scalaz
package std
override def ap[A, B](fa: => Option[A])
(f: => Option[A => B]) = ...
SHOOT THAT PIRANHA PLANT
import scalaz.std.option._
val partialShoot =
Option(PiranhaPlant()) <*> Option((shoot _).curried...
SOME NICER SYNTAX
import scalaz.std.option._
import scalaz.syntax.apply._
^(Option(PiranhaPlant()), Option(Fireball()))(sh...
LIST AS AN APPLY CONTEXT
val partial = List(PiranhaPlant(), PiranhaPlant(),
PiranhaPlant()) <*> List((shoot _).curried)
Li...
DISJUNCTION AS AN APPLY
/.right[String, Goomba](Goomba()) <*>
/.right[String, Goomba => Coin]((_:Goomba) => Coin())
//res:...
Apply lets you take a function that takes values and turn it
into a function that takes values in a context.
Write the cod...
REMEMBER THE THREE POINTS?
Abstraction: Apply is a abstract concept
Generalization: There is a set of objects that impleme...
.
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()
...
APPLICATIVE
trait Applicative[F[_]] extends Apply[F] { self =>
////
def point[A](a: => A): F[A]
...
}
implicit object Cage...
USING APPLICATIVE
import scalaz.syntax.applicative._
val cagedKoopa = ^(Fireball().point[Cage],
KoopaParatroopa().point[Ca...
SAME CODE DIFFERENT CONTEXT
val cagedKoopa = ^(Fireball().point[List],
KoopaParatroopa().point[List])(shootKP)
val cagedSh...
REMEMBER
1. Abstraction: The Applicative
2. Generalisation: The Applicative trait
3. The context: Different behaviours for...
.
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 hitMa...
HOW ABOUT THIS?
def marioWins = hitMario _ andThen hitMario andThen
hitMario andThen hitBowser andThen
hitBowser andThen h...
FAILING THE COMPUTATION?
Hits => Cage[Hits]
def hitMario2(hits: Hits):Cage[Hits] = hits match {
case ko:Hits if ko.mario +...
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: =...
CAGE MONAD
implicit object CageMonad extends Monad[Cage]{
override def bind[A, B](fa: Cage[A])(f: (A) => Cage[B]):
Cage[B]...
BINDOPS
final class BindOps[F[_],A] private[syntax](val self: F[A])
(implicit val F: Bind[F]) extends Ops[F[A]] {
...
def ...
NOW
import scalaz.syntax.monad._
Cage(Hits(0,0)) >>= hitMario2 >>= hitMario2 >>=
hitMario2 >>= hitBowser2 >>= hitBowser2 >...
FOR COMPREHENSION FLAT MAP
val x = for {
r1 <- Cage(Hits(0,0))
r2 <- hitMario2(r1)
r3 <- hitMario2(r2)
r4 <- hitMario2(r3)...
OTHER MONADS
def addTwo(x:Int) = Some(x + 2)
1.some >>= addTwo
//addTwo: (x: Int)List[Int]
def addTwo(x:Int) = List(x + 2)...
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 ...
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 co...
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]...
REFERENCES
Learn you a Haskell for Greater Good
Leaning Scalaz
Functional Programming in Scala
Bartosz Blog
http://learnyo...
.
Functor, Apply, Applicative And Monad
Upcoming SlideShare
Loading in …5
×

Functor, Apply, Applicative And Monad

2,221 views

Published on

Introduces the functional programming ideas of Functor, Apply, Applicative And Monad. Shows how to implement each in Scala with Scalaz and how to validate the implementation using property based test using specs2 and scalacheck.

Published in: Software
  • I think you need a perfect and 100% unique academic essays papers have a look once this site i hope you will get valuable papers, ⇒ www.HelpWriting.net ⇐
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD FULL BOOKS, INTO AVAILABLE Format, ......................................................................................................................... ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... 1.DOWNLOAD FULL. doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Functor, Apply, Applicative And Monad

  1. 1. FUNCTORS, APPLY, APPLICATIVE AND MONADS
  2. 2. THAT'S JUST DULL
  3. 3. SO LET'S TRY INSTEAD ...
  4. 4. .
  5. 5. WHY SHOULD YOU CARE? How do Functors, Apply, Applicative and Monad help you achieve early and continuous delivery of software?
  6. 6. 3 CONCEPTS
  7. 7. IF A PIZZA COSTS $16 AND YOU BUY TWO HOW MUCH DO YOU SPEND?
  8. 8. A MARIO KART COSTS $20 PER DAY TO RENT. HOW MUCH DOES IT COST TO RENT FOR TWO DAYS?
  9. 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. 10. .
  11. 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. 12. WHAT IS THE REALATION SHIP BETWEEN A POLOGON: A 3 sided triangle A 4 sided quadrilateral
  13. 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. 14. .
  15. 15. GENERALIZATION Relationship that holds between all members of some set of objects
  16. 16. IS A MILE A LONG WAY?
  17. 17. IS A YEAR A LARGE AMOUNT OF TIME?
  18. 18. .
  19. 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.
  20. 20. TRAINING LEVEL
  21. 21. WHAT IS FUNCTIONAL PROGRAMMING? Construct our programs using only pure functions. Pure functions have no side effects.
  22. 22. WHY IS A FUNCTION LIKE A PIPE? Some thing goes into one end and something else comes out the other end
  23. 23. Simple pipes simple can be joined together to form complex systems?
  24. 24. WHAT'S SO GOOD ABOUT NO SIDE EFFECTS? It makes it easier to reason about what's going on
  25. 25. IT'S IMPORTANT THAT FUNCTIONS LIKE PIPES DON'T LEAK
  26. 26. WORLD 1-1 Functor Land
  27. 27. GOOMBA PROBLEM
  28. 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. 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. 30. WHAT HAPPENS WHEN THE GOOMBA ESCAPES THE SUCTION? val vacuum = new Vacuum() vacuum.collect(null)
  31. 31. .
  32. 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. 33. There must be a better way
  34. 34. SOLUTION Put the Goomba in a Cage
  35. 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. 36. .
  37. 37. CAN WE GENERALIZE THE VACUUM CLASS?
  38. 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. 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. 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. 41. .
  42. 42. THE FUNCTOR A functor is basically for things that can be mapped over.
  43. 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. 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. 45. IS THERE A BETTER WAY?
  46. 46. .
  47. 47. TYPE CLASSES IN 60 SECONDS
  48. 48. WHY? Extend existing classes Without inheritance Without altering original source Keeps concerns seperate
  49. 49. HOW? 3 COMPONENTS 1. The type class 2. Instances for particular types 3. Interface methods for the api
  50. 50. THE TYPE CLASS Provide a generic type of what we want to implement. trait ProtoBuffWriter[A] { def write(value: A): Array[Byte] }
  51. 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. 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. 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. 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. 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. 56. !!WARNING!! Implicits like warp pipes can be dangerous
  57. 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. 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. 59. FINALLY scalaz.syntax package object extends Syntaxes trait Syntaxes { object functor extends ToFunctorOps } import scalaz.syntax.functor
  60. 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. 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. 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. 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. 64. CONTEXT Context is the environment the function is applied in.
  65. 65. MEET SOME MORE CONTEXTS
  66. 66. 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
  67. 67. 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
  68. 68. 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, α]})#λ]) }
  69. 69. SO WHY IS THIS SO HANDY?
  70. 70. ORDINARY FUNCTIONS ARE SIMPLER TO: read write use reason about
  71. 71. 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.
  72. 72. ///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)
  73. 73. .
  74. 74. WORLD 1-2 Apply Land
  75. 75. PIRANHA PLANT
  76. 76. PIRANHA PLANT case class Coin() case class Fireball() case class PiranhaPlant() def shoot(plant:PiranhaPlant, fireball:Fireball): Coin = Coin()
  77. 77. 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>
  78. 78. CURRING
  79. 79. 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>
  80. 80. MAP THE CURRIED SHOOT FUNCTION Cage(PiranhaPlant()) map {shoot _}.curried //Cage[Fireball => Coin] = ...
  81. 81. 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
  82. 82. 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) ... }
  83. 83. 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) }
  84. 84. 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]()
  85. 85. WHAT HAVE WE DONE? Taken a function that takes two values. Turned it into a function that takes two values in a context.
  86. 86. 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]) }
  87. 87. 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 }
  88. 88. 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())
  89. 89. 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())
  90. 90. LIST AS AN APPLY CONTEXT val partial = List(PiranhaPlant(), PiranhaPlant(), PiranhaPlant()) <*> List((shoot _).curried) List(Fireball()) <*> partial //res23: List[Coin] = List(Coin(), Coin(), Coin())
  91. 91. DISJUNCTION AS AN APPLY /.right[String, Goomba](Goomba()) <*> /.right[String, Goomba => Coin]((_:Goomba) => Coin()) //res: scalaz./[String,Coin] = /-(Coin())
  92. 92. 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
  93. 93. 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
  94. 94. .
  95. 95. WORLD 1-3 Applicative
  96. 96. KOOPA PARATROOPA
  97. 97. 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
  98. 98. .
  99. 99. 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())
  100. 100. 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))
  101. 101. 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())
  102. 102. 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())
  103. 103. REMEMBER 1. Abstraction: The Applicative 2. Generalisation: The Applicative trait 3. The context: Different behaviours for the same code
  104. 104. .
  105. 105. WORLD 1-4 Monad
  106. 106. BOWSER
  107. 107. THE RULES Mario can hit Bowser Bowser can hit Mario Mario dies if at any point hits on Mario > hits on Bowser + 2
  108. 108. 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)
  109. 109. 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
  110. 110. 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?
  111. 111. THE HITS ARE TRAPPED IN THE CAGE!!
  112. 112. MONAD
  113. 113. 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
  114. 114. 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) }
  115. 115. 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) }
  116. 116. 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))
  117. 117. 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
  118. 118. 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
  119. 119. MONAD LAWS Inherit the laws from Bind and Applicative Right Identity Left Identity
  120. 120. 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))
  121. 121. MONAD Lets us call a function that takes a value and returns a value in a context with a value in a context.
  122. 122. REMEMBER 1. Abstraction: The Monad 2. Generalisation: The Monad trait 3. The context: Different behaviours for the same code
  123. 123. 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]
  124. 124. 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/
  125. 125. .

×