在阅读各种关于函数式编程的文章时,我遇到过几次“Functor”这个术语,但作者通常认为读者已经理解了这个术语。在网络上你可以看到一些过于技术性的描述(参见维基百科的文章),也可以看到一些非常模糊的描述(参见ocaml-tutorial网站上关于函数函数的部分)。
有没有人可以定义这个术语,解释它的用法,或者提供一个如何创建和使用函子的例子?
编辑:虽然我对这个术语背后的理论很感兴趣,但我对这个概念的实现和实际应用更感兴趣,而不是理论。
编辑2:看起来好像有一些交叉术语:我特别指的是函数式编程的函子,而不是c++的函数对象。
考虑到其他的答案和我现在要发布的内容,我想说这是一个相当沉重的重载词,但无论如何……
关于Haskell中'functor'这个词的含义,可以问GHCi:
Prelude> :info Functor
class Functor f where
fmap :: forall a b. (a -> b) -> f a -> f b
(GHC.Base.<$) :: forall a b. a -> f b -> f a
-- Defined in GHC.Base
instance Functor Maybe -- Defined in Data.Maybe
instance Functor [] -- Defined in GHC.Base
instance Functor IO -- Defined in GHC.Base
基本上,Haskell中的函子是可以被映射的。另一种说法是,函子是可以被视为容器的东西,它可以被要求使用给定的函数来转换它所包含的值;因此,对于列表,fmap与map重合,对于Maybe, fmap f (Just x) = Just (f x), fmap f Nothing = Nothing等。
函子类型类小节和《Learn You a Haskell for Great Good》的函子、应用函子和Monoids小节给出了一些例子,说明这个特定概念在哪里有用。(总结一下:很多地方!: -))
请注意,任何单子都可以被视为函子,事实上,正如Craig Stuntz所指出的,最常用的函子往往是单子……对了,有时使一个类型成为Functor类型类的实例是很方便的,而不需要麻烦地使它成为一个单子。(例如,在Control中的ZipList的情况下。适用,在前面提到的页面之一。)
有三种不同的意思,没有太大的联系!
In Ocaml it is a parametrized module. See manual. I think the best way to grok them is by example: (written quickly, might be buggy)
module type Order = sig
type t
val compare: t -> t -> bool
end;;
module Integers = struct
type t = int
let compare x y = x > y
end;;
module ReverseOrder = functor (X: Order) -> struct
type t = X.t
let compare x y = X.compare y x
end;;
(* We can order reversely *)
module K = ReverseOrder (Integers);;
Integers.compare 3 4;; (* this is false *)
K.compare 3 4;; (* this is true *)
module LexicographicOrder = functor (X: Order) ->
functor (Y: Order) -> struct
type t = X.t * Y.t
let compare (a,b) (c,d) = if X.compare a c then true
else if X.compare c a then false
else Y.compare b d
end;;
(* compare lexicographically *)
module X = LexicographicOrder (Integers) (Integers);;
X.compare (2,3) (4,5);;
module LinearSearch = functor (X: Order) -> struct
type t = X.t array
let find x k = 0 (* some boring code *)
end;;
module BinarySearch = functor (X: Order) -> struct
type t = X.t array
let find x k = 0 (* some boring code *)
end;;
(* linear search over arrays of integers *)
module LS = LinearSearch (Integers);;
LS.find [|1;2;3] 2;;
(* binary search over arrays of pairs of integers,
sorted lexicographically *)
module BS = BinarySearch (LexicographicOrder (Integers) (Integers));;
BS.find [|(2,3);(4,5)|] (2,3);;
您现在可以快速添加许多可能的顺序,形成新顺序的方法,轻松地对它们进行二进制或线性搜索。泛型编程。
In functional programming languages like Haskell, it means some type constructors (parametrized types like lists, sets) that can be "mapped". To be precise, a functor f is equipped with (a -> b) -> (f a -> f b). This has origins in category theory. The Wikipedia article you linked to is this usage.
class Functor f where
fmap :: (a -> b) -> (f a -> f b)
instance Functor [] where -- lists are a functor
fmap = map
instance Functor Maybe where -- Maybe is option in Haskell
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
fmap (+1) [2,3,4] -- this is [3,4,5]
fmap (+1) (Just 5) -- this is Just 6
fmap (+1) Nothing -- this is Nothing
因此,这是一种特殊的类型构造函数,与Ocaml中的函子关系不大!
在命令式语言中,它是指向函数的指针。
在投票最多的答案下,网友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。
函子是对象和态射的映射,它保留了一个类别的组成和身份。
让我们定义什么是类别?
是一堆东西!
在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 '。
那是富克托!