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
Here is my attempt utilising flatMap
and flatTraverse
import cats.implicits._
seeds
.flatMap {
_.flatTraverse { seed =>
grow(seed)
.flatMap {
_.flatTraverse { plant =>
family(seed, plant)
}
}
}
}
This refactoring seems to satisfy the typechecker however I am unsure if happy compiler ensures 100% semantic equivalence.
Cats does not provide ListT because it breaks the associativity Monad law. See Cats FAQ and associated proof using scalaz ListT.
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.
ListT implementation
sbt
law tests
law tests results