Applicative提供ap和pure,但不保证提供flatMap,由Monad提供:
Monad 使用新函数 flatten 扩展了 Applicative 类型类。
如果F 是一个monad,那么至少在scalaz 中我们可以使用ListT,例如,
import scalaz._
import ListT._
import scalaz.std.option._
val animals: Option[List[String]] = Some(List("Dog", "Cat", "Bird"))
def getBreeds(animal: String): Option[List[String]] = ???
(for {
animal <- listT(animals)
breed <- listT(getBreeds(animal))
} yield breed).run
不过猫似乎不提供ListT:
ListT 的幼稚实现存在关联性问题; ... 可以创建一个 ListT
有这些问题,但它往往效率很低。对于很多
用例,Nested 可用于实现所需的结果。
这是一个你应该不使用的疯狂解决方案的尝试。考虑Validated,它只有Applicative 实例。让我们提供一个Monad 实例,即使Validated is not a Monad:
implicit def validatedMonad[E]: Monad[Validated[E, *]] =
new Monad[Validated[E, *]] {
def flatMap[A, B](fa: Validated[E, A])(f: A => Validated[E, B]): Validated[E, B] =
fa match {
case Valid(a) => f(a)
case i @ Invalid(_) => i
}
def pure[A](x: A): Validated[E, A] = Valid(x)
def tailRecM[A, B](a: A)(f: A => Validated[E, Either[A, B]]) = ???
}
validatedMonad的实现取自scala-exercises.org/cats/validated。
接下来让我们通过 shims 互操作层在猫中使用 scalaz 的 listT
libraryDependencies += "com.codecommit" %% "shims" % "2.1.0"
综上所述,我们有
import cats._
import cats.Monad
import cats.data.Validated.{Invalid, Valid}
import cats.data.{Nested, OptionT, Validated, ValidatedNec}
import cats.implicits._
import scalaz.ListT._
import shims._
implicit def validatedMonad[E]: Monad[Validated[E, *]] =
new Monad[Validated[E, *]] {
def flatMap[A, B](fa: Validated[E, A])(f: A => Validated[E, B]): Validated[E, B] =
fa match {
case Valid(a) => f(a)
case i @ Invalid(_) => i
}
def pure[A](x: A): Validated[E, A] = Valid(x)
def tailRecM[A, B](a: A)(f: A => Validated[E, Either[A, B]]) = ???
}
val animals: Validated[String, List[String]] = List("Dog", "Cat", "Bird").valid
def getBreeds(animal: String): Validated[String, List[String]] = ???
(for {
animal <- listT(animals)
breed <- listT(getBreeds(animal))
} yield breed).run
请注意,此“解决方案”违反一元法则,不通用,并且可能引起混淆,因此不要使用它。