【发布时间】:2019-04-06 04:27:22
【问题描述】:
相关问题是
在第一个问题中:
Monad 有什么特别之处?
monad 是一种在(纯)函数式编程中大量使用的数学结构,基本上是 Haskell。但是,还有许多其他可用的数学结构,例如应用函子、强单子或幺半群。有些更具体,有些更通用。然而,单子更受欢迎。这是为什么呢?
回复问题的评论:
据我记得,monad 是由 Wadler 普及的,当时做 IO 而不用繁琐的 CPS 和解析而不显式传递状态的想法是巨大的卖点;这是一个非常激动人心的时刻。 A.F.A.I.R.,Haskell 没有做构造函数类,但 Gofer(Hugs 之父)做了。 Wadler 建议对 monad 重载列表推导,所以 do 表示法后来出现了。一旦 IO 成为 monadic,monad 对初学者来说就成了一件大事,将它们巩固为一个需要深入了解的主要事物。 Applicatives 会更好,Arrows 更通用,但它们来得较晚,IO 卖得很厉害。 – AndrewC 2013 年 5 月 9 日 1:34
@Conal 的回答是:
我怀疑对这一特定类型类别 (
Monad) 的过度关注主要是历史上的侥幸。人们经常将IO与Monad联系在一起,尽管两者都是独立有用的想法(as are list reversal and bananas)。因为IO很神奇(有实现但没有外延),而Monad经常与IO相关联,所以很容易陷入对Monad的神奇思维。
首先,我同意他们的观点,我认为 Monads 的用处主要来自于 Functors,我们可以在结构中嵌入许多函数,而 Monads 是对函数组合鲁棒性的一点扩展 join :@ 987654333@ 避免嵌套类型。
在第二个问题中:
我们必须使用一元函数 a -> m b 吗?
网络上的许多教程仍然坚持使用单子函数,因为那是 Kleisli 三元组和单子定律。
还有很多类似的答案
我喜欢将这样的 m 视为“计划获得”的意思,其中“计划”涉及某种超出纯粹计算的额外交互。
或
在不需要
Monad的情况下,使用Applicative、Functor或仅使用基本的纯函数通常更简单。在这些情况下,应该(并且通常)使用这些东西来代替Monad。例如:
ws <- getLine >>= return . words -- Monad
ws <- words <$> getLine -- Functor (much nicer)
要明确:如果没有 monad 是可能的,并且没有 monad 更简单且更具可读性,那么你应该在没有 monad 的情况下这样做!如果 monad 使代码比它需要的更复杂或更混乱,不要使用 monad! Haskell 拥有 monad 的唯一目的是使某些复杂的计算更简单、更易于阅读和更易于推理。如果这没有发生,你不应该使用 monad。
阅读他们的回答,我想他们对 Monad 的特殊感觉源于 Haskell 社区恰好选择 Kleisli 类别中的 Monads 来解决他们的问题(IO 等)的历史事件
所以,再一次,我认为 Monads 的用处主要来自于 Functors,我们可以在结构中嵌入许多函数,而 Monads 是join:M(M(X)) -> M(X) 对函数组合鲁棒性的一点扩展,以避免嵌套输入。
事实上,在 JavaScript 中我实现如下..
函子
console.log("Functor");
{
const unit = (val) => ({
// contextValue: () => val,
fmap: (f) => unit((() => {
//you can do pretty much anything here
const newVal = f(val);
// console.log(newVal); //IO in the functional context
return newVal;
})()),
});
const a = unit(3)
.fmap(x => x * 2) //6
.fmap(x => x + 1); //7
}
关键是我们可以在 Functor 结构中实现我们喜欢的任何东西,在这种情况下,我只是将其设置为 IO/console.log 值。
另外一点是,做这个 Monads 是绝对没有必要的。
单子
现在,基于上面的 Functor 实现,我添加了额外的 join: MMX => MX 功能以避免嵌套结构,这应该有助于复杂函数组合的稳健性。
功能与上面的 Functor 完全相同,请注意用法也与 Functor fmap 相同。这不需要bind 的“单子函数”(Kleisli 单子组合)。
console.log("Monad");
{
const unit = (val) => ({
contextValue: () => val,
bind: (f) => {
//fmap value operation
const result = (() => {
//you can do pretty much anything here
const newVal = f(val);
console.log(newVal);
return newVal;
})();
//join: MMX => MX
return (result.contextValue !== undefined)//result is MX
? result //return MX
: unit(result) //result is X, so re-wrap and return MX
}
});
//the usage is identical to the Functor fmap.
const a = unit(3)
.bind(x => x * 2) //6
.bind(x => x + 1); //7
}
单子定律
以防万一,Monad 的这种实现满足 monad 法则,而上面的 Functor 不满足。
console.log("Monad laws");
{
const unit = (val) => ({
contextValue: () => val,
bind: (f) => {
//fmap value operation
const result = (() => {
//you can do pretty much anything here
const newVal = f(val);
//console.log(newVal);
return newVal;
})();
//join: MMX => MX
return (result.contextValue !== undefined)
? result
: unit(result)
}
});
const M = unit;
const a = 1;
const f = a => (a * 2);
const g = a => (a + 1);
const log = m => console.log(m.contextValue()) && m;
log(
M(f(a))//==m , and f is not monadic
);//2
console.log("Left Identity");
log(
M(a).bind(f)
);//2
console.log("Right Identity");
log(
M(f(a))//m
.bind(M)// m.bind(M)
);//2
console.log("Associativity");
log(
M(5).bind(f).bind(g)
);//11
log(
M(5).bind(x => M(x).bind(f).bind(g))
);//11
}
所以,这是我的问题。
我可能错了。
除了通过扁平化嵌套结构来实现函数组合的健壮性之外,Functor 不能做 Monad 可以做的事情吗?
Kleisli 类别中的 Monads 有什么特别之处?似乎有可能通过一些扩展来实现 Monads,以避免 Functor 的嵌套结构,并且没有 Kleisli 类别中的实体 a -> m b 的单子函数。
谢谢。
编辑(2018-11-01)
阅读答案,我同意在应满足 Functor-laws 的 IdentityFunctor 中执行 console.log 是不合适的,因此我像 Monad 代码一样注释掉了。
所以,消除这个问题,我的问题仍然成立:
除了通过扁平化嵌套结构来实现函数组合的鲁棒性之外,Functor 不能做 Monad 可以做的事情吗?
Kleisli 类别中的 Monads 有什么特别之处?似乎有可能通过一些扩展来实现 Monads,以避免 Functor 的嵌套结构,并且没有 Kleisli 类别中的实体 a -> m b 的单子函数。
@DarthFennec 的回答是:
“避免嵌套类型”实际上不是
join的目的,它只是一个巧妙的副作用。你说的方式听起来就像join只是去掉了外部类型,但 monad 的值没有改变。
我相信“避免嵌套类型”不仅仅是一个简洁的副作用,而是范畴论中对 Monad 的“连接”的定义,
monad 的乘法自然变换 μ:T∘T⇒T 为每个对象 X 提供了一个态射 μX:T(T(X))→T(X)
monad (in computer science): Relation to monads in category theory
这正是我的代码所做的。
另一方面,
事实并非如此。
join是 monad 的核心,它让 monad 能够做事。
我知道很多人在 Haskell 中以这种方式实现 monad,但事实是,Haskell 中有 Maybe functor,没有join,或者有 Free monad join 从一开始就嵌入到定义的结构中。它们是用户定义函子以做事的对象。
因此,
您可以将函子视为基本上是一个容器。有一个任意的内部类型,围绕它的外部结构允许一些变化,一些额外的值来“装饰”你的内部值。
fmap允许您处理容器内的东西,就像您正常处理它们的方式一样。这基本上是函子所能做的极限。monad 是一个具有特殊能力的函子:
fmap允许您处理内部值,bind允许您以一致的方式组合外部值。这比一个简单的函子要强大得多。
这些观察不符合 Maybe 函子和 Free monad 存在的事实。
【问题讨论】:
-
我不明白你的问题。是的,扁平化嵌套结构正是 Monads 可以做到而 Functors 无法做到的。这就是为什么我们对两个不同的概念使用两个不同的术语。
-
result.contextValue !== undefined上的分支毫无意义。您也不应该重载bind来执行fmap。另外,我不明白你想用这个例子说明什么? -
@Bergi 我写了“除了通过扁平化嵌套结构来实现函数组合的鲁棒性之外,有没有什么反例可以证明 Functor 不能做 Monad 可以做的事情?”和其他人明白了。抱歉,您的评论没有帮助。
-
您一定是在过去一周左右在几个不同的帐户上不断提出类似问题的同一个人。您为什么不断创建新帐户?
-
我不用几个账号来问。
标签: javascript haskell functional-programming monads