scala - 如何 flatMap cats Applicatives

标签 scala functional-programming scala-cats applicative flatmap

我已经开始使用 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提供appure,但不保证提供flatMap,由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

但是猫似乎没有提供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.


这是一个你不应该使用的疯狂解决方案的尝试。考虑只有 Applicative 实例的 Validated。让我们提供一个 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

注意这个“解法”违反一元法则,不通用,容易引起混淆,所以不要使用。

关于scala - 如何 flatMap cats Applicatives,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59225506/

相关文章:

scala - 使用EitherT来评估使用共享错误类型继承的操作结果?

scala - 标记案例类上的 LabelledGeneric 实例生成使我在无形中产生错误

scala - 如何在 Spark 中以小块迭代大型 Cassandra 表

bind 返回的仿函数的 C++ 类型

functional-programming - 函数式编程二叉搜索树作业

Scala-Cats 已验证 : value mapN is not a member of ValidatedNel tuple

scala - 字符串插值,转义引号

scala - 调用具有(显式)隐式参数的方法时 "prose"和 "dot notation"的差异

transactions - 在 Clojure 中计算中止的事务

scala - 猫从 monad 堆栈中获取值(value)