【问题标题】:How to flatMap cats Applicatives如何对猫进行平面映射
【发布时间】:2020-04-01 04:14:48
【问题描述】:

我已经开始使用 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 的意义在于它没有flatMap 的能力。

标签: scala functional-programming scala-cats applicative flatmap


【解决方案1】:

Applicative提供appure,但不保证提供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

请注意,此“解决方案”违反一元法则,不通用,并且可能引起混淆,因此不要使用它。

【讨论】:

  • 对于ListT F 必须是Monad
  • @DmytroMitin 你介意回顾一下我为Validated 申请的疯狂“解决方案”吗?
  • ValidatedandThen 这基本上是“一元”flatMap 操作。
【解决方案2】:

对于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]]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-16
    • 2015-08-30
    相关资源
    最近更新 更多