INTRO • Signatures • Behaviors o Left identity o Build with apply then flatMap a function is the same as calling the function o Right identity o FlatMap the apply doesn’t change the calling instance o Associativity o Nesting of a chain of functions does not matter
BENEFITS • Instance implementers o Constrain possible implementations o Disallow incoherent approaches o Confidence that algorithms can use • Algorithm implementers o Provide safe assumptions o Allow expectations about behavior o Instance will behave 9
Monoid Left and Right Identity zero does not change the value Functor Identity map(apply) does not change value Functor Composition safe to rewrite chains as composition Monad Left Identity flatMap uses the function result Monad Right Identity apply creates consistent context Monad Coherence map and flatMap are consistent Assumptions BENEFITS 10
Anyway? TYPECLASSES IN SCALA • External implementation of an interface o No need to extend directly o Possible without access to class source • Introduce via a context bound or implicit parameter • Implement in terms of of other instances o Adder[Pair[Int]] defined in terms of Adder[Int] 12
a Typeclass TYPECLASSES IN SCALA • Define an interface • Implement for your class • Introduce to implicit scope • Pass to function as implicit parameter OR as type constraint • Call methods from the interface 13
LAW TESTING class PairMonoidSpec extends FlatSpec with Matchers // From scalaz.SpecLite def checkAll(props: Properties): Unit = ??? it should "test laws" in { checkAll(monoid.laws[Pair[Int]]) } 25
LAW TESTING class PairMonoidSpec extends FlatSpec with Matchers // From scalaz.SpecLite def checkAll(props: Properties): Unit = ??? it should "test laws" in { checkAll(monoid.laws[Pair[Int]]) } 26
LAW TESTING class PairMonoidSpec extends FlatSpec with Matchers // From scalaz.SpecLite def checkAll(props: Properties): Unit = ??? it should "test laws" in { checkAll(monoid.laws[Pair[Int]]) } 27
• Typelevel project • Builds on Scalacheck • Integrates with Scalatest or Specs2 • Base trait for laws: RuleSet • Compute unique properties to check 29
LAW TESTING sealed trait MyOption[+A] case object MyNone extends MyOption[Nothing] final case class MySome[A](value: A) extends MyOption[A] implicit val myOptionMonad = new Monad[MyOption] { def pure[A](value: A): MyOption[A] = ??? def flatMap[A, B](opt: MyOption[A]) (fn: A => MyOption[B]): MyOption[B] = ??? def tailRecM[A, B](a: A) (f: A => MyOption[Either[A, B]]): MyOption[B] = ??? } 32
Laws separate good behavior from domain logic • Testing lawfulness ensures your implementations will behave properly • Property testing provides a mechanism for testing lawfulness • Cats and scalaz include a suite of laws for their typeclasses 38