list - 用语义上等效的猫功能替换 scalaz ListT

标签 list scala scalaz monad-transformers scala-cats

猫会做 not提供 ListT monad transformer 那么我们如何重写以下 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

这是我利用 flatMapflatTraverse 的尝试

import cats.implicits._

seeds
  .flatMap {
    _.flatTraverse { seed =>
      grow(seed)
        .flatMap {
          _.flatTraverse { plant =>
            family(seed, plant)
          }
        }
    }
  }

这种重构似乎可以满足类型检查器的要求,但我不确定快乐的编译器是否能确保 100% 的语义等价。

最佳答案

Cats 不提供 ListT,因为它违反了结合性 Monad 定律。参见 Cats FAQassociated 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

关于list - 用语义上等效的猫功能替换 scalaz ListT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60244629/

相关文章:

android - 比较 Android 中的两个 List<String> 值

scala - 结合 `OptionT` 和 `EitherT` 来处理 `Future[Either[Error, Option[T]]]`

带有箭头符号的 Map 上的 Scala 模式匹配

scala - 随机作为 scalaz.Monad 的实例

c++ - C++比较 vector 和列表

Java - 遍历包含列表的 map

java - scala/Serializable NoClassDefFoundError (RMI w/Scala)

Scalaz:将ValidationNel的ValidationNel转换为ValidationNel

scala - Scala 中带有两个参数的类型构造函数的仿函数实例

带有 2 个列表/变量的 Python "for loop"