【发布时间】:2018-12-23 19:57:28
【问题描述】:
我已经被函子(Endofunctor)的结合律弄糊涂了好几个星期了。
我知道每个 endofunctor 形成组合/关联特征。
组合是关联的。基本上这意味着当你组合多个函数时(如果你喜欢的话,可以使用态射),你不需要括号:
h∘(g∘f) = (h∘g)∘f = h∘g∘f
让我们再看一下 JavaScript 中的组合法则:
给定一个函子,F:
const F = [1, 2, 3];
以下是等价的:
F.map(x => f(g(x)));
// is equivalent to...
F.map(g).map(f);
但是,如下代码所示,尤其是后面的部分:
console.log("===================");
const take1 = a => a //any
.map(g)
.map(f)
.map(trace);
这会导致类型错误:
// const r3 = v.map(take1); //TypeError: a.map is not a function
,当然:
const take2 = a => Identity(a) //Identity(any)
.map(trace)
.map(g)
.map(trace)
.map(f)
.map(trace);
const r4 = v.map(take2);
有效。
我觉得有问题的是在take2函数中,将参数类型:a转换为Identity(a)的要求并不是真正的组合函数。
我也明白 Monads 是为了避免这个组合问题,我想知道这个问题是否仅仅是由于缺少“Monads 的左右同一律”而仍然满足函子结合律,或者可能有不同层关联律,在函子中,显然满足了某一层关联律,但如上例所示,另一层关联律被破坏。
你能澄清一下吗?
示例代码当然是用 JavaScript 编写的,但我仍然标记 Haskell,因为社区在这个主题上很强大,所以请原谅。
谢谢。
const trace = x => {
console.log(x);
return x;
};
const Identity = value => ({
map: fn => Identity(fn(value)),
valueOf: () => value,
});
const u = Identity(2);
const f = n => n + 1;
const g = n => n * 2;
// Composition law
const r1 = u
.map(x => f(g(x)));
const r2 = u
.map(g)
.map(f);
r1.map(trace); // 5
r2.map(trace); // 5
console.log("===================");
const take1 = a => a //any
.map(g)
.map(f)
.map(trace);
const v = Identity(100);
// const r3 = v.map(take1); //TypeError: a.map is not a function
const take2 = a => Identity(a) //Identity(any)
.map(trace)
.map(g)
.map(trace)
.map(f)
.map(trace);
const r4 = v.map(take2);
PS/编辑:
问这个问题的另一个原因是,如果我们简单地将函数序列 f/g/h 视为数据序列并像字符串一样剪切和粘贴,则结构变为:
h∘(g∘f) != (h∘g)∘f != h∘g∘f
没有刻意展平程序。这破坏了关联性,如果只有 Monoids/Monads 的左/右恒等律的扁平化过程使事物具有关联性,那么这些恒等律和关联律是否不相互隔离??
【问题讨论】:
-
你问的问题真的与结合律无关。
-
还有一个建议:要完全进入函数式思维,最好不要使用
trace,而只显示最终值。 (并不是说 Haskellers 不使用trace进行调试,或者更确切地说是traceShowId,而只是作为找出一些价值差异细节的黑客。首先总是类型检查阶段。)
标签: javascript haskell monads functor associative