【问题标题】:Why is Functor in scala cats required为什么需要 scala 猫中的 Functor
【发布时间】:2020-05-30 13:53:52
【问题描述】:

我刚刚开始学习Scala cats 框架。我正在阅读Functor。我了解它的功能,但我不了解它的用法。如果Functors 中已经存在map 方法,例如ListOption 等,我为什么要使用它?

举个例子

val list = List(1, 2, 3)
Functor[List].map(list)(x => x * 2)

但也可以使用

list.map(x => x * 2)

当我们在 Functor 特征中抽象方法 map 时,我们会得到什么。 有人可以说明一下,以便我了解它的用法。

【问题讨论】:

标签: scala functor scala-cats


【解决方案1】:

你可以在对象上调用.map,当你知道这个对象有这个方法,并且这个方法是这样调用的。如果您知道对象的确切类型,那么编译器可以检查是否确实如此。但是如果你不知道对象的类型怎么办?如果您不想使用运行时反射怎么办?

想象一下这样的情况:

def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
  fInt.map(_ * 2)

在这里,我们不知道 F 的类型 - 它可能是 ListOptionFutureIOEither[String, *]。然而我们可以在不使用反射的情况下通过.map 处理它——我们使用Functor[F] 来支持扩展方法。我们也可以不使用这样的扩展方法:

def doubleIntInF[F[_]: Functor](fInt: F[Int]): F[Int] =
  Functor[F].map(fInt)(_ * 2)

它会起作用(只要我们在范围内有正确的隐含):

doubleIntInF(List(1,2,3,4,5,6)) //  List(2, 4, 6, 8, 10, 12
doubleIntInF(Option(4)) // Some(2)

在我们知道 F=List、Option 等的情况下,我们没有理由使用它。但如果这个 F 是动态的,我们就有充分的理由使用它。

我们为什么要让这个 F 动态化?在库中使用它,例如可以将通过类型类提供的多个功能组合在一起。

例如如果你有F[_]G[_] 并且你有Traverse[F]Applicative[G](更强大的Functor)你可以把F[G[A]] 变成G[F[A]]

val listOption: List[Option] = List(Some(1), Some(2))
listOption.sequence // Some(List(1, 2))

val listFuture: List[Option] = List(Future(1), Future(2))
listFuture.sequence // Future(List(1, 2))

Cats 生态系统中的几乎所有库都使用这个概念(称为类型类)来实现功能,而无需假设您选择与它们相同的数据结构和 IO 组件。只要您可以提供类型类实例来证明它们可以安全地在您的类型上使用某些方法,它们就可以实现该功能(例如,Cats Effect 正在使用一些类型类和 Doobie、FS2、Http4s 等扩展 Cats,等等。这些没有假设你使用什么来运行你的计算)。

长话短说 - 在像您这样的情况下使用 Functor 没有意义,但总的来说,它们使您仍然可以在不那么简单且您没有的情况下使用 .map硬编码类型。

【讨论】:

  • 当你说 F 是动态的时,我仍然不明白。作为一个例子,你说过将 F[G[A]] 变成 G[F[A]]。我们什么时候需要这样做?
  • 例如,当您有 List[A] 时,您可以将其映射到 List[Future[B]] 以并行运行,然后返回到 Future[List[B]] - 这比并行收集更好,因为:1 . 它们是错误的 2. 通过自己做事,您可以更好地控制使用哪些线程,是否有任何重试等等。这只是类型类有用的一个示例 - 请参阅fs2.io 例如,其中可以定义 F在调用点,但整个图书馆工作不知道 F 是什么。
【解决方案2】:

这个问题类似于为什么在 OOP 中有人需要一个接口(特征),而它的实现具有相同的方法。

接口是一种抽象。抽象很有用。在编程时,我们更喜欢关注重要的事情,而忽略当前不重要的细节。此外,使用接口进行编程有助于解耦实体,从而创建更好的架构。

接口(如ComparableIterableSerializable 等)是描述 OOP 中行为的方式。类型类(如FunctorMonadShowRead 等)是在 FP 中描述行为的方式。

如果您只想在列表(或Option)上映射(或flatMap),则不需要Functor。如果你想处理所有需要Functor 的可映射的东西,如果你想处理所有flatMappable 需要的东西Monad 等等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-29
    • 2013-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-04
    相关资源
    最近更新 更多