λ Tony's Blog λ

Monad Laws using Reductio (Scala)

Posted on June 26, 2008

In the spirit of yesterday’s post that denoted the Functor Laws using Reductio, I will also give the Monad Laws. Before I do however, here is an example use of the FunctorLaws object that runs 600 unit tests when testing a Functor implementation for scala.Option[A], scala.List and finally, for the scala.Either[A, _] functor just to make it a bit quirky and probably confuse a few of you (in that case, ignore it for now!) :)

val prop_OptionIdentity = identity[Option, Int]
val prop_OptionComposition = composition[Option, Int, String, Long]

val prop_ListIdentity = identity[List, Int]
val prop_ListComposition = composition[List, Int, String, Long]

val prop_EitherIdentity = identity[Apply1Of2[Either, String]#Apply, Int]
val prop_EitherComposition = composition[Apply1Of2[Either, String]#Apply, Int, String, Long]

// OK passed 100 tests. (appears 6 times -- once per property)

…and the obligatory Monad Laws follow (import statements omitted this time, but they are the same as previous)…

object MonadLaws {
  def leftIdentity[M[_], A, B](implicit m: Monad[M],
                                     am: Arbitrary[M[B]],
                                     aa: Arbitrary[A],
                                     ca: Coarbitrary[A]) =
    prop((a: A, f: A => M[B]) =>
      m.bind(f, m.unit(a)) === f(a))

  def rightIdentity[M[_], A](implicit m: Monad[M],
                                      am: Arbitrary[M[A]]) =
    prop((ma: M[A]) => m.bind((a: A) =>
      m.unit(a), ma) === ma)

  def associativity[M[_], A, B, C](implicit m: Monad[M],
                                           am: Arbitrary[M[A]],
                                           ca: Coarbitrary[A],
                                           cb: Coarbitrary[B],
                                           amb: Arbitrary[M[B]],
                                           amc: Arbitrary[M[C]]) =
    prop((ma: M[A], f: A => M[B], g: B => M[C]) =>
      m.bind(g, m.bind(f, ma)) === m.bind((a: A) => m.bind(g, f(a)), ma))
}

Woot! Woot!