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

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

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

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


当前回答

你回答了不少不错的问题。我将加入:

函子,在数学意义上,是代数上一种特殊的函数。它是将一个代数映射到另一个代数的最小函数。“极简性”用函子定律来表示。

有两种方式来看待这个问题。例如,列表是某些类型的函子。也就是说,给定类型为“a”的代数,您可以生成包含类型为“a”的列表的兼容代数。(例如:将一个元素带到包含它的单元素列表的映射:f(a) = [a])同样,兼容性的概念是由函子定律表示的。

另一方面,鉴于函子f / a型,(也就是说,f是应用函子的结果f的代数a型),从g和功能:- > b,我们可以计算一个新的函子f = (fmap g)映射f a到f b。简而言之,fmap是f的一部分映射“函子零件”“函子零件”,和g函数的一部分,“代数”映射到“代数部分”。它接受一个函数,一个函子,一旦完成,它也是一个函子。

看起来不同的语言使用不同的函子概念,但事实并非如此。它们只是在不同的代数上使用函子。OCamls有一个模块代数,这个代数上的函子允许您以一种“兼容”的方式将新声明附加到模块。

Haskell函子不是类型类。它是一个具有满足类型类的自由变量的数据类型。如果您愿意深入挖掘数据类型的精髓(没有自由变量),您可以通过底层代数将数据类型重新解释为函子。例如:

数据F = F Int

是整型类的同构。F,作为一个值构造函数,是一个将Int映射到F Int的函数,一个等价的代数。它是一个函子。另一方面,这里的fmap不是免费的。这就是模式匹配的作用。

函子很适合以一种代数相容的方式将事物“附加”到代数元素上。

其他回答

简单地说,函子或函数对象是可以像函数一样调用的类对象。

在c++中:

这就是写函数的方法

void foo()
{
    cout << "Hello, world! I'm a function!";
}

这就是写函子的方法

class FunctorClass
{
    public:
    void operator ()
    {
        cout << "Hello, world! I'm a functor!";
    }
};

现在你可以这样做:

foo(); //result: Hello, World! I'm a function!

FunctorClass bar;
bar(); //result: Hello, World! I'm a functor!

这样做的好处是可以将state保存在类中——想象一下,如果您想询问一个函数被调用了多少次。没有办法以简洁、封装的方式做到这一点。对于函数对象,它就像任何其他类一样:你有一些实例变量,你在operator()中增加它,还有一些方法来检查这个变量,一切都很整齐。

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

在投票最多的答案下,网友Wei Hu问道:

我理解ml -函子和haskell -函子,但缺乏 将它们联系在一起的洞察力。这两者之间是什么关系 二,在分类理论的意义上?

注:本人不懂ML,如有错误请见谅。

让我们首先假设我们都熟悉“范畴”和“函子”的定义。

一个紧凑的答案是,“haskell -函子”是(endo-)函子F: Hask -> Hask,而“ML-函子”是函子G: ML- > ML'。

这里,Hask是由Haskell类型和它们之间的函数组成的类别,类似地,ML和ML'是由ML结构定义的类别。

注意:将Hask作为一个类别存在一些技术问题,但有一些方法可以绕过它们。

从范畴论的角度来看,这意味着hask -函子是Haskell类型的映射F:

data F a = ...

伴随着Haskell函数的map fmap:

instance Functor F where
    fmap f = ...

ML是差不多的,尽管我不知道有一个规范的fmap抽象,所以让我们定义一个:

signature FUNCTOR = sig
  type 'a f
  val fmap: 'a -> 'b -> 'a f -> 'b f
end

f映射ml -类型fmap映射ml -函数

functor StructB (StructA : SigA) :> FUNCTOR =
struct
  fmap g = ...
  ...
end

是一个函子F: StructA -> StructB。

不是为了与前面的理论或数学答案相矛盾,但Functor也是一个对象(在面向对象编程语言中),它只有一个方法,并且可以有效地用作函数。

Java中的Runnable接口就是一个例子,它只有一个方法:run。

考虑这个例子,首先在Javascript中,它有一级函数:

[1, 2, 5, 10].map(function(x) { return x*x; });

输出: [1,4,25,100]

map方法接受一个函数并返回一个新数组,其中每个元素都是将该函数应用于原始数组中相同位置的值的结果。

要在Java中使用Functor做同样的事情,你首先需要定义一个接口,比如:

public interface IntMapFunction {
  public int f(int x);
}

然后,如果你添加一个集合类,它有一个映射函数,你可以这样做:

myCollection.map(new IntMapFunction() { public int f(int x) { return x * x; } });

这使用了IntMapFunction的一个内嵌子类来创建一个Functor,它是前面JavaScript示例中的函数的OO等效。

使用函子可以在OO语言中应用函数式技术。当然,一些OO语言也直接支持函数,所以这不是必需的。

参考:http://en.wikipedia.org/wiki/Function_object

这是一篇关于函子的编程文章,后面更具体地介绍了它们是如何在编程语言中出现的。

函子的实际使用是在单子中,如果你想找的话,你可以找到很多关于单子的教程。