【问题标题】:How are Functors useful?函子有什么用处?
【发布时间】:2016-06-20 18:10:00
【问题描述】:

我们知道任何泛型类型F[_]map 方法,符合某些laws,是一个函子。例如,List[_]Option[_]F[A] = Env => A 是函子。我只是想知道这个函子抽象是否有意义。

我如何利用它们是函子这一事实?您能否展示一个使用map 并且实际有用的非平凡计算示例?

【问题讨论】:

  • 还有eed3si9n.com/learning-scalaz/Functor.html你认为非平凡的计算是什么?
  • 我认为有些人误解了迈克尔的问题。我是这样读的:他理解函子对各种不同数据类型的map() 方法进行抽象,但他很难理解如何利用这一事实。很容易想到作为函子的特定类型会出现的上下文,以及为什么你会在这些上下文中那些类型'map()。很难想到您会使用限制为Functor 但没有其他约束的类型变量 的上下文!
  • @VictorMoroz 好的。你可以展示你选择的任何计算,我会告诉你它是否微不足道:)

标签: scala haskell functional-programming functor


【解决方案1】:

函数等概念的最大好处之一是泛型构造允许您从更简单的函子构建更复杂的类型,并保证这些复杂类型具有某些属性。当您像以前那样孤立地考虑函子时,函子似乎毫无意义是可以理解的,但您学习和掌握的此类结构越多,它们就会变得越来越有用。

一个更简单的例子是几种组合函子的方法也产生一个函子。例如,如果 List[A]Option[A] 是函子,那么:

  • 函子的组成: List[Option[A]]Option[List[A]]
  • 函子的乘积: (List[A], Option[A])
  • 函子之和: Either[List[A], Option[A]]

我没有足够的知识在 Scala 中写出来,但是在 Haskell 中,这些事实可以转化为这些示例的通用代码:

-- A generic type to represent the composition of any two functors
-- `f` and `g`.
newtype Compose f g a = Compose { getCompose :: f (g a) }

-- If `f` and `g` are functors, so is `Compose f g`.
instance (Functor f, Functor g) => Functor (Compose f g) where
  fmap f (Compose fga) = Compose (fmap (fmap f) fga)

这是一个非常简单的例子,但是:

  • 它至少作为一种分析工具已经很有用了。 很多人们在实践中编写的数据类型,当您通过本示例的镜头查看它们时,结果是更简单的函子的乘积、总和或组合。因此,一旦您了解了这些结构,您就可以在编写复杂类型时自动“感知”它是函子,以及如何编写其map() 操作。
  • 更详细的示例具有相同的风格:
    • 我们有一个通用构造,当使用实现Functor 的类型实例化时,它可以保证某些合约;
    • 当我们将 Functor 实现添加到任何 a 类型时,我们就能够在该构造中使用该类型。

一个更详细的例子是free monads(链接有一个扩展的Scala例子),一个通用的解释器结构,它依赖于用户提供的Functors来定义语言的“指令”。其他链接(这些链接大多直接来自 Google 搜索):

【讨论】:

  • 谢谢你,路易斯。确实非常有趣。您能否举一个仅使用Functor 的计算示例(除了免费的单子)?
【解决方案2】:

我不了解 Scala,但在 Haskell 中,Functor 类对于定义 Van Laarhoven-style lenses 至关重要:

type Lens' s a = forall f . Functor f => (a -> f a) -> s -> f s

这些镜头通常是为特定相关的类型 sa 定义的,但它们的实用性必须与任意函子一起使用。

Functor 作为ApplicativeTraversable 的超类也很重要。在使用这些更强大的抽象时,使用fmap 方法通常非常有用。

【讨论】:

    【解决方案3】:

    好吧,一旦您知道某物是 Functor,您不仅会得到 map,还会得到所有可以用它派生的函数

    例如,可以以适用于任何函子的方式派生函数lift

    对于某些 Functor F[_],Lift 会将函数从 A => B“提升”到 F[A] => F[B],并定义为

    def lift[A, B](f: A => B): F[A] => F[B] = map(_)(f)
    

    如果您使用像 cats 或 scalaz 这样的库,那么您可以免费获得这些功能。 cats documentation 有一些您可能感兴趣的其他示例

    【讨论】:

    • 我不反对,但我不确定这是否真的回答了这个问题。以我的经验,在应用程序代码中只有一个 Functor 约束本身是非常罕见的——如果你只知道 F 是一个函子,那么你几乎必须有一个像 F[A] => F[B] 这样的签名,并且在那时您应该只定义一个A => B 并在调用站点进行映射,其中F 通常是具体的或有其他已知的事实。
    猜你喜欢
    • 1970-01-01
    • 2010-10-22
    • 2019-10-12
    • 2012-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-12
    相关资源
    最近更新 更多