Last active
May 25, 2018 05:25
-
-
Save machuz/fe8b063fcbea56fbf46d25c81f2a67d0 to your computer and use it in GitHub Desktop.
scalacheckのmonad.lawsで自作Maybeのテスト
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scalaz.Functor | |
// Functor(F)と、計算値(A)を型パラメータでとる | |
// mapとflatMapを持ち、Pureなら関数適用、InpureならFunctorを使ってFの計算値(A)にfを適用する | |
sealed abstract class Free[F[_], A] { | |
import Free._ | |
def map[B](f: A => B)(implicit F: Functor[F]): Free[F, B] = | |
flatMap(a => Free.Pure(f(a))) | |
def flatMap[B](f: A => Free[F, B])(implicit F: Functor[F]): Free[F, B] = | |
this match { | |
case Free.Pure(a) => f(a) | |
case Free.Impure(ff) => Free.Impure(F.map(ff)(_.flatMap(f))) | |
} | |
} | |
object Free { | |
// Freeの状態クラス | |
// scalazのFreeはユーザーの入力待時などに一時計算を止めるsuspend,resumeを | |
// 考慮した作りになっていて便利だが煩雑なのでresumeを考慮しないサンプルを書く | |
case class Pure[F[_], A](a: A) extends Free[F, A] | |
case class Impure[F[_], A](ff: F[Free[F, A]]) extends Free[F, A] | |
def lift[F[_], A](fa: F[A])(implicit F: Functor[F]): Free[F, A] = | |
Impure(F.map(fa)(a => Pure(a))) | |
//関数を適用して、`implicit F: Functor[F]`を明示的に渡している。※FreeはFunctorを持っているのでここではFreeをわたす | |
} | |
trait Maybe[+A] extends Functor[Maybe] { | |
def isEmpty: Boolean | |
def get: A | |
def map[A, B](fa: Maybe[A])(f: A => B): Maybe[B] = | |
if (fa.isEmpty) Empty else Just(f(fa.get)) | |
} | |
final case class Just[+A](x: A) extends Maybe[A] { | |
def isEmpty = false | |
def get = x | |
} | |
case object Empty extends Maybe[Nothing] { | |
def isEmpty = true | |
def get = throw new NoSuchElementException("Empty.get") | |
} | |
implicit val MaybeFunctor: Functor[Maybe] = | |
new Functor[Maybe] { | |
def map[A, B](fa: Maybe[A])(f: A => B): Maybe[B] = | |
if (fa.isEmpty) Empty else Just(f(fa.get)) | |
} | |
type MaybeM[A] = Free[Maybe, A] | |
trait MaybeMInstances { | |
implicit val maybeMInstance = new scalaz.Monad[MaybeM] { | |
// (a) 注入関数の実装 | |
def point[A](x: => A): MaybeM[A] = MaybeM.just(x) | |
/** | |
* (b) 連鎖関数の実装 | |
* | |
* b1. Counter モナド fa からタプルの内容を取得 | |
* b2. x の値に f を適用した結果の Counter モナドからタプルの内容を取得 | |
* b3. y の値とカウンター値 d に fa のカウンター値 c を加算した値を持つ Counter モナドを返す | |
*/ | |
def bind[A, B](fa: MaybeM[A])(f: (A) => MaybeM[B]): MaybeM[B] = { | |
fa.flatMap(f) | |
} | |
} | |
} | |
// (2) Monad のインスタンスを定義 | |
case object MaybeM extends MaybeMInstances { | |
def just[A](a: A): MaybeM[A] = Free.Pure(a) | |
def empty[A]: MaybeM[A] = Free.lift[Maybe, A](Empty) | |
} | |
val r = for { | |
x <- MaybeM.just(1) | |
y <- MaybeM.just(2) | |
z <- MaybeM.empty[Int] | |
} yield x + y + z | |
implicit def maybeEqual[A]: Equal[MaybeM[A]] = Equal.equalA | |
implicit val maybeFunctor = new Functor[MaybeM] { | |
def map[A, B](fa: MaybeM[A])(f: A => B): MaybeM[B] = fa match { | |
case Free.Impure(_) => MaybeM.empty | |
case Free.Pure(c) => MaybeM.just(f(c)) | |
} | |
} | |
// monad則をちぇっく | |
import scalaz._, Scalaz._, scalacheck.ScalazProperties._, scalacheck.ScalazArbitrary._, scalacheck.ScalaCheckBinding._ | |
import org.scalacheck.{ Gen, Arbitrary } | |
implicit def maybeMArbiterary[A](implicit a: Arbitrary[A]): Arbitrary[MaybeM[A]] = | |
a.map { a => | |
MaybeM.just(a): MaybeM[A] | |
} | |
implicit val mm = MaybeM.maybeMInstance | |
monad.laws[MaybeM].check | |
// + monad.applicative.apply.functor.invariantFunctor.identity: OK, passed 100 | |
// tests. | |
// + monad.applicative.apply.functor.invariantFunctor.composite: OK, passed 10 | |
// 0 tests. | |
// + monad.applicative.apply.functor.identity: OK, passed 100 tests. | |
// + monad.applicative.apply.functor.composite: OK, passed 100 tests. | |
// + monad.applicative.apply.composition: OK, passed 100 tests. | |
// + monad.applicative.identity: OK, passed 100 tests. | |
// + monad.applicative.homomorphism: OK, passed 100 tests. | |
// + monad.applicative.interchange: OK, passed 100 tests. | |
// + monad.applicative.map consistent with ap: OK, passed 100 tests. | |
// + monad.bind.apply.functor.invariantFunctor.identity: OK, passed 100 tests. | |
// + monad.bind.apply.functor.invariantFunctor.composite: OK, passed 100 tests | |
// . | |
// + monad.bind.apply.functor.identity: OK, passed 100 tests. | |
// + monad.bind.apply.functor.composite: OK, passed 100 tests. | |
// + monad.bind.apply.composition: OK, passed 100 tests. | |
// + monad.bind.associativity: OK, passed 100 tests. | |
// + monad.bind.ap consistent with bind: OK, passed 100 tests. | |
// + monad.right identity: OK, passed 100 tests. | |
// + monad.left identity: OK, passed 100 tests. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment