用语义上等效的猫功能替换 scalaz ListT
Replace scalaz ListT with semantically equivalent cats functionality
cats provide ListT
monad transformer so how could we rewrite the following snippet 使用 scalaz ListT
来理解 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
的尝试
import cats.implicits._
seeds
.flatMap {
_.flatTraverse { seed =>
grow(seed)
.flatMap {
_.flatTraverse { plant =>
family(seed, plant)
}
}
}
}
这种重构似乎可以满足类型检查器的要求,但我不确定快乐的编译器是否能确保 100% 的语义等价。
Cats 不提供 ListT,因为它违反了结合性 Monad 定律。参见 Cats FAQ and associated proof using scalaz ListT。
根据您建议的 .flatTraverse
以下 ListT
实现仍然通过了所有 cats-core 法则测试(错误?)。
我没有软件验证经验,但您可能会发现成功的测试足以将这 2 种实现视为等效。
ListT 实现
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
cats ListT
monad transformer so how could we rewrite the following snippet 使用 scalaz ListT
来理解 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
import cats.implicits._
seeds
.flatMap {
_.flatTraverse { seed =>
grow(seed)
.flatMap {
_.flatTraverse { plant =>
family(seed, plant)
}
}
}
}
这种重构似乎可以满足类型检查器的要求,但我不确定快乐的编译器是否能确保 100% 的语义等价。
Cats 不提供 ListT,因为它违反了结合性 Monad 定律。参见 Cats FAQ and associated proof using scalaz ListT。
根据您建议的 .flatTraverse
以下 ListT
实现仍然通过了所有 cats-core 法则测试(错误?)。
我没有软件验证经验,但您可能会发现成功的测试足以将这 2 种实现视为等效。
ListT 实现
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