// === sbt.version=1.2.8 ThisBuild / scalaVersion := "2.13.0" ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / organization := "com.example" ThisBuild / organizationName := "example" lazy val root = (project in file(".")) .settings( name := "check_cont_monad_law", libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.14.0" % Test ) // === package example import org.scalacheck.{Prop, Properties} final case class Cont[R, A](run: (A => R) => R) { def map[B](f: A => B): Cont[R, B] = Cont(br => run(f andThen br)) def flatMap[B](f: A => Cont[R, B]): Cont[R, B] = Cont(br => run(f(_).run(br))) } object Cont { def pure[R, A](a: A): Cont[R, A] = Cont(_(a)) } class ContMonadSpec extends Properties("Monad[Cont[R, ?]]") { def inc(i: Int): Cont[Int, Int] = Cont(_(i + 1)) def add_![R](s: String): Cont[String, String] = Cont(_(s + "!")) def add_?[R](s: String): Cont[String, String] = Cont(_(s + "?")) property("rightIdentity") = Prop.forAll { i: Int => inc(i).flatMap(Cont.pure).run(identity) == inc(i).run(identity) } property("leftIdentity") = Prop.forAll { i: Int => Cont.pure[Int, Int](i).flatMap(inc).run(identity) == inc(i).run(identity) } property("associativity") = Prop.forAll { s: String => Cont.pure(s).flatMap(add_!).flatMap(add_?).run(identity) == Cont.pure(s).flatMap(a => add_!(a).flatMap(add_?)).run(identity) } } // === sbt:check_cont_monad_law> test [info] + Monad[Cont[R, ?]].rightIdentity: OK, passed 100 tests. [info] + Monad[Cont[R, ?]].leftIdentity: OK, passed 100 tests. [info] + Monad[Cont[R, ?]].associativity: OK, passed 100 tests. [info] Passed: Total 3, Failed 0, Errors 0, Passed 3 [success] Total time: 1 s, completed 2019/09/15 19:49:36