如何 flatMap cats Applicatives
How to flatMap cats Applicatives
我已经开始使用 Cats 学习函数式编程,并且坚持使用 flatMapping(合并)应用程序 F[List]
。
在纯 Scala 中非常简单的是像这样的列表的平面映射列表:
val animals = List("Dog", "Cat", "Bird")
def getBreads(animal: String): List[String] = ...
val allAnimalsBreads = animals.flatMap(animal => getBread(animal)) // this will be just List[String]
如果所有内容都用 applicative 包装,我如何做同样的事情?:
val animals = List("Dog", "Cat", "Bird").pure[F]
def getBreads(animal: String): F[List[String]] = ...
val allAnimalsBreads = ? // this should be F[List[String]]
Applicative provides ap
and pure
, but does not guarantee to provide flatMap
, which is provided by Monad:
Monad
extends the Applicative
type class with a new function flatten
.
如果 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
不过cats好像没有提供ListT:
A naive implementation of ListT
suffers from associativity issues; ... It’s possible to create a ListT
that doesn’t
have these issues, but it tends to be pretty inefficient. For many
use-cases, Nested
can be used to achieve the desired results.
这是您不应该使用的疯狂解决方案的尝试。考虑 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
注意"solution"违反一元法则,不通用,容易造成混淆,请勿使用。
对于Applicative
这是不可能的。对于Monad
,如果你不想使用非常规变压器ListT
,你可以
import cats.syntax.traverse._
import cats.syntax.applicative._
import cats.syntax.functor._
import cats.syntax.flatMap._
import cats.instances.list._
val allAnimalsBreads: F[List[String]] =
animals.map(_.map(getBreads)) // F[List[F[List[String]]]]
.map(_.sequence) // F[F[List[List[String]]]]
.flatten // F[List[List[String]]]
.map(_.flatten) // F[List[String]]
我已经开始使用 Cats 学习函数式编程,并且坚持使用 flatMapping(合并)应用程序 F[List]
。
在纯 Scala 中非常简单的是像这样的列表的平面映射列表:
val animals = List("Dog", "Cat", "Bird")
def getBreads(animal: String): List[String] = ...
val allAnimalsBreads = animals.flatMap(animal => getBread(animal)) // this will be just List[String]
如果所有内容都用 applicative 包装,我如何做同样的事情?:
val animals = List("Dog", "Cat", "Bird").pure[F]
def getBreads(animal: String): F[List[String]] = ...
val allAnimalsBreads = ? // this should be F[List[String]]
Applicative provides ap
and pure
, but does not guarantee to provide flatMap
, which is provided by Monad:
Monad
extends theApplicative
type class with a new functionflatten
.
如果 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
不过cats好像没有提供ListT:
A naive implementation of
ListT
suffers from associativity issues; ... It’s possible to create aListT
that doesn’t have these issues, but it tends to be pretty inefficient. For many use-cases,Nested
can be used to achieve the desired results.
这是您不应该使用的疯狂解决方案的尝试。考虑 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
注意"solution"违反一元法则,不通用,容易造成混淆,请勿使用。
对于Applicative
这是不可能的。对于Monad
,如果你不想使用非常规变压器ListT
,你可以
import cats.syntax.traverse._
import cats.syntax.applicative._
import cats.syntax.functor._
import cats.syntax.flatMap._
import cats.instances.list._
val allAnimalsBreads: F[List[String]] =
animals.map(_.map(getBreads)) // F[List[F[List[String]]]]
.map(_.sequence) // F[F[List[List[String]]]]
.flatten // F[List[List[String]]]
.map(_.flatten) // F[List[String]]