给出Fun
类型的monad
type FUN[A] = Map[String, String] => (List[String], A)
val funMonad: Monad[FUN] = new Monad[FUN] {
override def flatMap[A, B](fa: FUN[A])(f: (A) => FUN[B]): FUN[B] = m => {
val (list1, a1) = fa(m)
val (list2, a2) = f(a1)(m)
(list1 ++ list2, a2)
}
override def pure[A](x: A): FUN[A] = m => (Nil, x)
}
问题是:人如何使用discipline library来测试这个Monad实例是否服从Monad Laws?
以下是部分结果,但由于编译器无法找到 CartesianTests.Isomorphisms[FUN]
的隐含结果而失败。
import cats.Monad
import cats.kernel.Eq
import org.scalacheck.rng.Seed
import org.scalacheck.{Arbitrary, Gen}
class MyMonadSpec extends FunSuite with scalatest.Discipline {
...
implicit def funEq[T: Eq]: Eq[FUN[T]] = {
val sampleInput: Map[String, String] = {
def genMap: Gen[Map[String, String]] = for {
size <- Gen.size
keys <- Gen.containerOfN[List, String](size, Arbitrary.arbitrary[String])
values <- Gen.containerOfN[List, String](size, Arbitrary.arbitrary[String])
} yield keys.zip(values).toMap
genMap(Gen.Parameters.default.withSize(10), Seed.apply(123L)).get
}
Eq.instance[FUN[T]] ((f1, f2) => f1(sampleInput) == f2(sampleInput))
}
import cats.kernel.instances.int._
import cats.kernel.instances.tuple._
import cats.laws.discipline.MonadTests
checkAll("Int", MonadTests[FUN](funMonad).monad[Int, Int, Int])
//Error: could not find implicit value for parameter iso: cats.laws.discipline.CartesianTests.Isomorphisms[[A]scala.collection.immutable.Map[String,String] => (List[String], A)]
答案 0 :(得分:4)
通常你可以把你的实例放到范围内并执行此操作(你可以通过导入cats.instances.all._
来清理它,但我明确表示清楚):
import cats.instances.int._
import cats.instances.list._
import cats.instances.map._
import cats.instances.string._
import cats.instances.tuple._
import cats.laws.discipline.MonadTests
import cats.laws.discipline.eq._
MonadTests[FUN].monad[Int, Int, Int].all.check
您可以免费获得Isomorphisms
个实例,因为它只需要一个Invariant
实例,Monad
隐含该实例。另请注意,您不需要定义自己的Eq
个实例 - eq
包提供适合您测试的Function1
实例。
在这种情况下,编译器实际上不会找到Invariant
实例(这可能是一个SI-2712问题,或者它可能与我的头顶的别名有关''我不确定),似乎你想测试monad实例而不将其放入隐式范围。一种简单的方法是提供您自己的Isomorphisms
:
import cats.laws.discipline.CartesianTests.Isomorphisms
implicit val funIsomorphisms: Isomorphisms[FUN] = Isomorphisms.invariant(funMonad)
或者作为完整的工作示例(在Cats 0.7.2上):
import cats.Monad
import cats.instances.int._
import cats.instances.list._
import cats.instances.map._
import cats.instances.string._
import cats.instances.tuple._
import cats.instances.map._
import cats.laws.discipline.CartesianTests.Isomorphisms
import cats.laws.discipline.MonadTests
import cats.laws.discipline.eq._
type FUN[A] = Map[String, String] => (List[String], A)
val funMonad: Monad[FUN] = new Monad[FUN] {
def flatMap[A, B](fa: FUN[A])(f: (A) => FUN[B]): FUN[B] = m => {
val (list1, a1) = fa(m)
val (list2, a2) = f(a1)(m)
(list1 ++ list2, a2)
}
def pure[A](x: A): FUN[A] = m => (Nil, x)
def tailRecM[A, B](a: A)(f: A => FUN[Either[A, B]]): FUN[B] = defaultTailRecM(a)(f)
}
implicit val funIsomorphisms: Isomorphisms[FUN] = Isomorphisms.invariant(funMonad)
然后:
scala> MonadTests[FUN](funMonad).monad[Int, Int, Int].all.check
+ monad.ap consistent with product + map: OK, passed 100 tests.
+ monad.applicative homomorphism: OK, passed 100 tests.
+ monad.applicative identity: OK, passed 100 tests.
+ monad.applicative interchange: OK, passed 100 tests.
+ monad.applicative map: OK, passed 100 tests.
+ monad.apply composition: OK, passed 100 tests.
+ monad.cartesian associativity: OK, passed 100 tests.
+ monad.covariant composition: OK, passed 100 tests.
+ monad.covariant identity: OK, passed 100 tests.
+ monad.flatMap associativity: OK, passed 100 tests.
+ monad.flatMap consistent apply: OK, passed 100 tests.
+ monad.followedBy consistent flatMap: OK, passed 100 tests.
+ monad.invariant composition: OK, passed 100 tests.
+ monad.invariant identity: OK, passed 100 tests.
+ monad.map flatMap coherence: OK, passed 100 tests.
+ monad.monad left identity: OK, passed 100 tests.
+ monad.monad right identity: OK, passed 100 tests.
+ monad.monoidal left identity: OK, passed 100 tests.
+ monad.monoidal right identity: OK, passed 100 tests.
+ monad.mproduct consistent flatMap: OK, passed 100 tests.
+ monad.tailRecM consistent flatMap: OK, passed 100 tests.
(您也可以使用checkAll
- 我只是在执行.all.check
,因为它不需要您使用ScalaTest或实例化FunSuite
。)