【函数式编程】函子及JavaScript实现
函子
函子是范畴论的一个概念,理解函子,先要理解范畴。基本的概念我们从wiki获得,然后再加上我(非数学专业/计算机专业毕业)的理解。
范畴论
范畴论(英语:Category theory)是数学的一门学科,以抽象的方法处理数学概念,将这些概念形式化成一组组的“对象”及“态射”。数学中许多重要的领域可以形式化为范畴。使用范畴论可以令这些领域中许多难理解、难捉摸的数学结论更容易叙述证明。
范畴最容易理解的一个例子为集合范畴,其对象为集合,态射为集合间的函数。但需注意,范畴的对象不一定要是集合,态射也不一定要是函数;一个数学概念若可以找到一种方法,以符合对象及态射的定义,则可形成一个有效的范畴,且所有在范畴论中导出的结论都可应用在这个数学概念之上。
注意几点:
-
范畴论基本等于对象 + 态射。 -
态射大部分为映射函数,但态射不等于函数。 -
可以用形象化的例子来解释范畴:集合为范畴对象,集合间映射为范畴态射。
函子 Functor
在范畴论中,函子是范畴间的一类映射。函子也可以解释为小范畴范畴内的态射。
函子首先现身于代数拓扑学,其中拓扑空间的连续映射给出相应的代数对象(如基本群、同调群或上同调群)的代数同态。在当代数学中,函子被用来描述各种范畴间的关系。“函子”(英文:Functor)一词借自哲学家鲁道夫·卡尔纳普的用语[1]。卡尔纳普使用“函子”这一词和函数之间的相关来类比谓词和性质之间的相关[2]。对卡尔纳普而言,不同于当代范畴论的用法,函子是个语言学的词汇。对范畴论者来说,函子则是个特别类型的函数。
注意几点
-
函子是范畴间的一类映射 -
函子可以理解为一个特殊的函数
我们再来看一下数学定义:
明确几点数学定义
-
函子把数据从C范畴映射到D范畴 -
C范畴内的数据态射(函数)f可以被映射到D范畴上 -
被映射的态射满足条件:id函数自等;组合函数结合律 -
id函数满足后意为自函子 -
组合函数:g . f = x => g(f(x))
再看一下haskell
中的定义
class Functor f where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
haskell
的函子是一个类,并带有两个方法
-
fmap
:得到一个映射关系a -> b
,和一个函子f a
,输出一个函子f b
-
<$
:这是haskell
的中缀表达式,用前缀表达式表示就是id
函数,表示函子值为a
(f a
),并接受一个函子f b
,返回f a
也就是说,如果我们把代码放到范畴下解决,所有的数据都统一为函子的话,函子和函子之间可以使用fmap
和id
函数通信。当然,我们需要给函子扩展更多的函数才能满足程序中奇奇怪怪的需求,但这不在本文讨论中。我们只关注函子。
函子的定律haskell
实现
id :: a -> a
fmap id = id
fmap (f . g) = fmap f . fmap g
JavaScript实现
直接上代码
// Just为我们的一个“函子”
function Just(value) {
return {
value, // 保存值
// 值为基本数据,支持映射到范畴(Just)内其他元素
// addOne = x => x + 1
// 比如 Just(1).map(addOne) -> Just(2)
map: function map(fn) {
return Just(fn(value))
},
// 值为基本数据,支持映射到自身范畴
// 比如 Just(1).flatMap(addOne) -> 2
flatMap: function flatMap(fn) {
return fn(value);
},
// 值为函数(映射),支持映射到范畴(Just)内的其他元素
// 比如Just((x) => x + 1).fmap(Just(2)) -> Just(3)
fmap: function fmap(just) { // 函子 Just(compose(b, a)) = composeJust(Just(b), Just(a));
return just.map(value)
}
}
}
实现了fmap
之后,我们要实现复合函数compose
// compound 复合函数 属于Number范畴
function compose(g, f) {
return x => g(f(x))
}
// compound 复合函数 属于Just范畴
function composeJust(Justg, Justf) {
return jx => Justg.fmap(Just(Justf.fmap(jx)))
}
实现了复合函数,则我们就满足了结合律。即Number
范畴复合后再态射与函数复合前分别态射再在Just
范畴上复合,得到的结果是一样的。
课后作业:
-
实现自函子定律 -
理解 compose
函数的意义