问题描述
cats 不提供ListT
monad转换器,所以我们如何重写以下代码段,它使用了scalaz ListT
来理解猫中的语义等效代码段
cats does not provide ListT
monad transformer so how could we rewrite the following snippet which uses scalaz ListT
in a for-comprehension to a semantically equivalent snippet in cats
import scalaz._
import ListT._
import scalaz.std.option._
val seeds: Option[List[String]] = Some(List("apple", "orange", "tomato"))
def grow(seed: String): Option[List[String]] = Some(List(seed.toUpperCase))
def family(seed: String, plant: String): Option[List[(String, String)]] = Some(List(seed -> plant))
(for {
seed <- listT(seeds)
plant <- listT(grow(seed))
result <- listT(family(seed, plant))
} yield result).run
这是我尝试使用flatMap
和 flatTraverse
Here is my attempt utilising flatMap
and flatTraverse
import cats.implicits._
seeds
.flatMap {
_.flatTraverse { seed =>
grow(seed)
.flatMap {
_.flatTraverse { plant =>
family(seed, plant)
}
}
}
}
这种重构似乎可以满足类型检查器的要求,但是我不确定快乐的编译器是否可以确保100%的语义等效性.
This refactoring seems to satisfy the typechecker however I am unsure if happy compiler ensures 100% semantic equivalence.
推荐答案
Cats不提供ListT,因为它违反了Monad的关联性法则.请参见猫常见问题解答和使用scalaz ListT的关联证明.
Cats does not provide ListT because it breaks the associativity Monad law. See Cats FAQ and associated proof using scalaz ListT.
建议您通过以下所有基于.flatTraverse
的ListT
实现,并通过所有cats-core法律测试(错误?).
Still the following ListT
implementation based on .flatTraverse
as you suggest passes all cats-core law tests (a bug?).
我没有软件验证方面的经验,但您可能会发现成功的测试足以将这两种实现视为等同.
I have no experience with software proving but you might find the successful tests good enough to consider the 2 implementations as equivalent.
case class ListT[M[_], A](value: M[List[A]])
implicit def listTMonad[M[_]: Monad] = new Monad[ListT[M, *]] {
override def flatMap[A, B](fa: ListT[M, A])(f: A => ListT[M, B]): ListT[M, B] =
ListT(
Monad[M].flatMap[List[A], List[B]](fa.value)(
list => Traverse[List].flatTraverse[M, A, B](list)(a => f(a).value)
)
)
override def pure[A](a: A): ListT[M, A] = ListT(Monad[M].pure(List(a)))
// unsafe impl, can be ignored for this question
override def tailRecM[A, B](a: A)(f: A => ListT[M, Either[A, B]]): ListT[M, B] =
flatMap(f(a)) {
case Right(b) => pure(b)
case Left(nextA) => tailRecM(nextA)(f)
}
}
sbt
name := "listT_tests"
version := "0.1"
scalaVersion := "2.11.12"
scalacOptions += "-Ypartial-unification"
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "2.0.0",
"org.scalaz" %% "scalaz-core" % "7.2.30",
"org.scalacheck" %% "scalacheck" % "1.14.1" % "test",
"org.scalatest" %% "scalatest" % "2.2.6" % "test",
"org.typelevel" %% "discipline-scalatest" % "1.0.1",
"org.typelevel" %% "discipline-core" % "1.0.2",
"org.typelevel" %% "cats-laws" % "2.0.0" % Test,
"com.github.alexarchambault" %% "scalacheck-shapeless_1.14" % "1.2.3" % Test
)
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full)
法律测试
class TreeLawTests extends AnyFunSpec with Checkers with FunSpecDiscipline {
implicit def listTEq[M[_], A] = Eq.fromUniversalEquals[ListT[M, A]]
checkAll("ListT Monad Laws", MonadTests[ListT[Option, *]].stackUnsafeMonad[Int, Int, String])
}
法律测试结果
- monad (stack-unsafe).ap consistent with product + map
- monad (stack-unsafe).applicative homomorphism
- monad (stack-unsafe).applicative identity
- monad (stack-unsafe).applicative interchange
- monad (stack-unsafe).applicative map
- monad (stack-unsafe).applicative unit
- monad (stack-unsafe).apply composition
- monad (stack-unsafe).covariant composition
- monad (stack-unsafe).covariant identity
- monad (stack-unsafe).flatMap associativity
- monad (stack-unsafe).flatMap consistent apply
- monad (stack-unsafe).flatMap from tailRecM consistency
- monad (stack-unsafe).invariant composition
- monad (stack-unsafe).invariant identity
- monad (stack-unsafe).map flatMap coherence
- monad (stack-unsafe).map2/map2Eval consistency
- monad (stack-unsafe).map2/product-map consistency
- monad (stack-unsafe).monad left identity
- monad (stack-unsafe).monad right identity
- monad (stack-unsafe).monoidal left identity
- monad (stack-unsafe).monoidal right identity
- monad (stack-unsafe).mproduct consistent flatMap
- monad (stack-unsafe).productL consistent map2
- monad (stack-unsafe).productR consistent map2
- monad (stack-unsafe).semigroupal associativity
- monad (stack-unsafe).tailRecM consistent flatMap
这篇关于用语义上等效的cat功能替换scalaz ListT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!