在阅读各种关于函数式编程的文章时,我遇到过几次“Functor”这个术语,但作者通常认为读者已经理解了这个术语。在网络上你可以看到一些过于技术性的描述(参见维基百科的文章),也可以看到一些非常模糊的描述(参见ocaml-tutorial网站上关于函数函数的部分)。

有没有人可以定义这个术语,解释它的用法,或者提供一个如何创建和使用函子的例子?

编辑:虽然我对这个术语背后的理论很感兴趣,但我对这个概念的实现和实际应用更感兴趣,而不是理论。

编辑2:看起来好像有一些交叉术语:我特别指的是函数式编程的函子,而不是c++的函数对象。


当前回答

在OCaml中,它是一个参数化模块。

如果您了解c++,可以将OCaml函子视为模板。c++只有类模板,函数子在模块规模上工作。

函数子的一个例子是Map.Make;module StringMap =映射。使(字符串);;构建一个使用字符串键映射的映射模块。

你不能通过多态性实现StringMap这样的东西;你需要对这些键做一些假设。String模块包含对完全有序字符串类型的操作(比较等),函子将链接到String模块包含的操作。你可以用面向对象编程做一些类似的事情,但是你会有方法间接开销。

其他回答

在Inria网站上的O'Reilly OCaml书中有一个很好的例子(不幸的是,在写这篇文章时,它被删除了)。我在加州理工学院使用的这本书中找到了一个非常相似的例子:OCaml介绍(pdf链接)。相关的部分是关于函子的章节(书中139页,PDF中149页)。

在书中,他们有一个名为MakeSet的函子,它创建了一个由列表组成的数据结构,以及添加元素、确定元素是否在列表中以及查找元素的函数。用于确定它是否在集合中的比较函数已被参数化(这是使MakeSet成为函子而不是模块的原因)。

它们还有一个实现比较函数的模块,这样就可以进行不区分大小写的字符串比较。

使用函子函数和实现比较的模块,它们可以在一行中创建一个新模块:

module SSet = MakeSet(StringCaseEqual);;

这将为使用不区分大小写比较的一组数据结构创建一个模块。如果您想创建一个使用区分大小写比较的集合,那么您只需要实现一个新的比较模块,而不是一个新的数据结构模块。

Tobu将函子与c++中的模板进行了比较,我认为这是非常恰当的。

函子是对象和态射的映射,它保留了一个类别的组成和身份。

让我们定义什么是类别?

是一堆东西! 在a内部画几个点(现在是两个点,一个是“a”,另一个是“b”) 圆圈,并命名为圆圈A(类别)。

这个类别包含什么?

对象之间的组合和每个对象的恒等函数。

因此,在应用Functor之后,我们必须映射对象并保存组合。

让我们想象‘A’是我们的范畴,它有对象[' A', 'b'],并且存在一个态射A -> b

现在,我们必须定义一个函子,它可以将这些对象和态射映射到另一个类别“B”。

假设这个函子叫做Maybe

data Maybe a = Nothing | Just a

B类是这样的。

请再画一个圆,但这次用“也许a”和“也许b”代替“a”和“b”。

一切看起来都很好,所有的对象都被映射了

“a”变成了“也许a”,“b”变成了“也许b”。

但问题是我们也要把态射从a映射到b。

这意味着' a'中的态态a -> b应该映射到'可能a' -> '可能b'

来自a -> b的形态称为f,然后来自'Maybe a' -> 'Maybe b'的形态称为'fmap f'

现在让我们看看函数f在A中做了什么,看看我们能否在B中复制它

A中f的函数定义:

f :: a -> b

F取a并返回b

f在B中的函数定义:

f :: Maybe a -> Maybe b

f取也许a,返回也许b

让我们看看如何使用fmap将函数'f'从'A'映射到'B'中的函数'fmap f'

fmap的定义

fmap :: (a -> b) -> (Maybe a -> Maybe b)
fmap f Nothing = Nothing
fmap f (Just x) = Just(f x)

那么,我们在这里做什么?

我们将函数“f”应用于类型为“a”的“x”。“Nothing”的特殊模式匹配来自于Functor Maybe的定义。

因此,我们将对象[a, b]和形态[f]从类别' a '映射到类别' b '。

那是富克托!

函数子是一个具有映射方法的对象。

JavaScript中的数组实现了map,因此是函子。承诺、流和树通常在函数式语言中实现map,当它们这样做时,它们被认为是函子。函子的map方法获取它自己的内容,并使用传递给map的转换回调对它们进行转换,并返回一个新的函子,该函子包含作为第一个函子的结构,但带有转换后的值。

src: https://www.youtube.com/watch?v=DisD9ftUyCk&feature=youtu.be&t=76

这里的其他答案是完整的,但我将尝试另一种解释FP使用函子。我们来做个类比:

函子是类型为A的容器,当它受到从A→b映射的函数的作用时,将产生类型为b的容器。

与c++中使用的抽象函数指针不同,这里函子不是函数;相反,它是一种在受到某个函数影响时表现一致的东西。

Functor与函数式编程没有特别的关系。它只是一个指向函数或某种对象的“指针”,可以像调用函数一样调用它。