-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【函数式编程】函子 #39
Comments
证明现在,证明我们 首先,从现在开始,你要认为Just(x)是一种数据结构了,它代表着一类数据(一种范畴)。 我们先把id函数补充进去 // 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);
},
id: function id(anything) {
return Just(value);
}
}
} 然后我们看一下 fmap id = id
fmap (f . g) = fmap f . fmap g 这是 var id = (x) => x;
// 在Number范畴中,id函数自等,即x = id(x)
var a = 1;
console.log(a === id(a)); // true
// 在Just范畴中
var ja = Just(1);
function eq(justa, justb) { // 实现一个Just范畴的判等函数
return justa.flatMap(id) === justb.flatMap(id);
}
console.log(eq(ja, ja.id(ja))); // true 等于实现Just范畴中的 === 操作
Just(id).fmap(Just(999)); // Just(999) 等于实现Just范畴中的 fmap id = id
// 需要实现操作: fmap (g . f) = (fmap g) . (fmap f)
var a = 3;
var square = x => x * x;
var addOne = x => x + 1;
// Number范畴中
var compose = (g, f) => x => g(f(x))
var compound = compose(square, addOne);
console.log(compound(a)); // (3 + 1) ^2 = 16
Just(compound)
.fmap(Just(a)) // Just(16) 等于实现了 fmap compound =
Just(square)
.fmap(
Just(addOne).fmap(Just(a))
) // Just(16) 等于实现了= fmap f . fmap g 风格现在,你必须接受Just就是一种通用类型了,就像你能接受Number类型、String类型一样。Just类型看起来有这些功能
这种写法风格,等价为给数据值封装了一系列操作,以后你使用值和进行运算,都必须在Just上进行。这就是范畴论,一种研究数据「对象」和「态射」的方法。你可以理解类比为三角形有函数表示、几何表示、代数表示、复数表示等方式,范畴态射把同一个事物映射到不同的研究空间里面去,本质上,你还是在做类似的事情。 以上的几个方法只是monad世界的冰山一角,后面我们要理解为什么要做这种抽象,也会讲更多的操作,用以构建我们的程序世界。 compose函数的意义组合函数令我们的程序,能够以更方便的方式进行组合,并且让我们的基础函数可以得到复用。比如加减乘除四个操作只需要对应四个函数,程序中没有必要创建这种函数 function addThree(a, b, c) {
return a + b + c;
} 而可以通过组合的方式实现 var add = a => b => a + b;
var addThree = compose(add(c), add(b), add(a))(0) 数组中的 课后习题
|
函子
函子是范畴论的一个概念,理解函子,先要理解范畴。基本的概念我们从wiki获得,然后再加上我(非数学专业/计算机专业毕业)的理解。
范畴论
注意几点:
函子 Functor
注意几点
我们再来看一下数学定义:
明确几点数学定义
再看一下
haskell
中的定义haskell
的函子是一个类,并带有两个方法fmap
:得到一个映射关系a -> b
,和一个函子f a
,输出一个函子f b
<$
:这是haskell
的中缀表达式,用前缀表达式表示就是id
函数,表示函子值为a
(f a
),并接受一个函子f b
,返回f a
也就是说,如果我们把代码放到范畴下解决,所有的数据都统一为函子的话,函子和函子之间可以使用
fmap
和id
函数通信。当然,我们需要给函子扩展更多的函数才能满足程序中奇奇怪怪的需求,但这不在本文讨论中。我们只关注函子。函子的定律
haskell
实现JavaScript实现
直接上代码
实现了
fmap
之后,我们要实现复合函数compose
实现了复合函数,则我们就满足了结合律。即
Number
范畴复合后再态射与函数复合前分别态射再在Just
范畴上复合,得到的结果是一样的。课后作业:
compose
函数的意义The text was updated successfully, but these errors were encountered: