在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
当前回答
如果我理解正确的话,IEnumerable是从monad派生出来的。我想知道,对于我们这些来自C#世界的人来说,这可能是一个有趣的视角吗?
值得一提的是,这里有一些帮助我的教程链接(不,我还不知道单子是什么)。
http://osteele.com/archives/2007/12/overloading-semicolonhttp://spbhug.folding-maps.org/wiki/MonadsEnhttp://www.loria.fr/~kow/monads/
其他回答
实际上,monad基本上允许回调嵌套(具有相互递归的线程状态(请忽略连字符))(以可组合(或可分解)的方式)(具有类型安全性(有时(取决于语言))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
例如,这不是单子:
//JavaScript is 'Practical'
var getAllThree =
bind(getFirst, function(first){
return bind(getSecond,function(second){
return bind(getThird, function(third){
var fancyResult = // And now make do fancy
// with first, second,
// and third
return RETURN(fancyResult);
});});});
但是monad启用了这样的代码。monad实际上是一组类型:{bind,RETURN,也许其他我不认识的人…}。这本质上是无关紧要的,实际上是不切实际的。
所以现在我可以使用它:
var fancyResultReferenceOutsideOfMonad =
getAllThree(someKindOfInputAcceptableToOurGetFunctionsButProbablyAString);
//Ignore this please, throwing away types, yay JavaScript:
// RETURN = K
// bind = \getterFn,cb ->
// \in -> let(result,newState) = getterFn(in) in cb(result)(newState)
或将其分解:
var getFirstTwo =
bind(getFirst, function(first){
return bind(getSecond,function(second){
var fancyResult2 = // And now make do fancy
// with first and second
return RETURN(fancyResult2);
});})
, getAllThree =
bind(getFirstTwo, function(fancyResult2){
return bind(getThird, function(third){
var fancyResult3 = // And now make do fancy
// with fancyResult2,
// and third
return RETURN(fancyResult3);
});});
或者忽略某些结果:
var getFirstTwo =
bind(getFirst, function(first){
return bind(getSecond,function(second){
var fancyResult2 = // And now make do fancy
// with first and second
return RETURN(fancyResult2);
});})
, getAllThree =
bind(getFirstTwo, function(____dontCare____NotGonnaUse____){
return bind(getThird, function(three){
var fancyResult3 = // And now make do fancy
// with `three` only!
return RETURN(fancyResult3);
});});
或者从以下内容简化一个小案例:
var getFirstTwo =
bind(getFirst, function(first){
return bind(getSecond,function(second){
var fancyResult2 = // And now make do fancy
// with first and second
return RETURN(fancyResult2);
});})
, getAllThree =
bind(getFirstTwo, function(_){
return bind(getThird, function(three){
return RETURN(three);
});});
收件人(使用“正确身份”):
var getFirstTwo =
bind(getFirst, function(first){
return bind(getSecond,function(second){
var fancyResult2 = // And now make do fancy
// with first and second
return RETURN(fancyResult2);
});})
, getAllThree =
bind(getFirstTwo, function(_){
return getThird;
});
或者把它们挤在一起:
var getAllThree =
bind(getFirst, function(first_dontCareNow){
return bind(getSecond,function(second_dontCareNow){
return getThird;
});});
这些能力的实用性并没有真正显现出来,或者变得清晰,直到你试图解决真正的棘手问题例如解析或模块/ajax/资源加载。
你能想象成千上万行indexOf/subString逻辑吗?如果频繁的解析步骤包含在小函数中呢?像字符、空格、大写字符或数字这样的函数?如果这些函数在回调中给出了结果,而不必与Regex集团和争论发生冲突?如果它们的组成/分解被很好地理解了呢?这样你就可以从下往上构建大型解析器了吗?
因此,管理嵌套回调范围的能力非常实用,尤其是在使用monadic解析器组合器库时。(也就是说,根据我的经验)
不要挂断电话:-分类理论-可能是月-莫纳德定律-哈斯克尔- !!!!
我将尝试在Haskell的背景下解释Monad。
在函数式编程中,函数组合很重要。它允许我们的程序由小的、易于阅读的函数组成。
假设我们有两个函数:g::Int->String和f::String->Bool。
我们可以做(f.g)x,这与f(gx)相同,其中x是Int值。
当进行合成/将一个函数的结果应用到另一个函数时,使类型匹配是很重要的。在上述情况下,g返回的结果类型必须与f接受的类型相同。
但有时值是在上下文中的,这使得排列类型有点不容易。(在上下文中设置值非常有用。例如,Maybe Int类型表示可能不存在的Int值,IO String类型表示由于执行某些副作用而存在的String值。)
假设我们现在有g1::Int->Maybe String和f1::String->Maybe Bool。g1和f1分别与g和f非常相似。
我们不能做(f1.g1)x或f1(g1 x),其中x是Int值。g1返回的结果类型不是f1期望的类型。
我们可以用。运算符,但现在我们不能用..组合f1和g1。。问题是我们不能直接将上下文中的值传递给期望值不在上下文中的函数。
如果我们引入一个运算符来组合g1和f1,这样我们就可以写出(f1 operator g1)x,这不是很好吗?g1返回上下文中的值。该值将脱离上下文并应用于f1。是的,我们有这样一个操作员。它是<=<。
我们还有一个>>=运算符,它为我们做了完全相同的事情,尽管语法略有不同。
我们写:g1 x>>=f1。g1 x是Maybe Int值。>>=运算符帮助将Int值从“可能不存在”上下文中取出,并将其应用于f1。f1的结果是Maybe Bool,它将是整个>>=操作的结果。
最后,为什么Monad有用?因为Monad是定义>>=运算符的类型类,与定义==和/=运算符的Eq类型类非常相似。
总之,Monad类型类定义了>>=运算符,该运算符允许我们将上下文中的值(我们称为这些monadic值)传递给不需要上下文中值的函数。将考虑上下文。
如果这里需要记住一点,那就是Monads允许在上下文中包含值的函数组合。
实际上,monad是函数组合运算符的一种自定义实现,它考虑了副作用以及不兼容的输入和返回值(用于链接)。
但是,你本可以发明蒙纳斯!
sigfpe说:但所有这些都将单子介绍为需要解释的深奥的东西。但我想说的是,它们一点都不深奥。事实上,面对函数式编程中的各种问题,你会不可避免地被引向某些解决方案,所有这些都是单子的例子。事实上,如果你还没有发明,我希望你现在就发明它们。这是注意到所有这些解决方案实际上都是变相的相同解决方案的一小步。读完这篇文章后,你可能会更好地理解单子上的其他文档,因为你会发现你所看到的一切都是你已经发明的。monads试图解决的许多问题都与副作用有关。因此,我们将从它们开始。(请注意,monad让您做的不仅仅是处理副作用,特别是许多类型的容器对象都可以被视为monad。monad的一些介绍发现,很难协调monad的这两种不同用法,并且只关注其中一种。)在命令式编程语言(如C++)中,函数的行为与数学函数完全不同。例如,假设我们有一个C++函数,它接受一个浮点参数并返回一个浮点结果。从表面上看,它可能有点像一个将实数映射到实数的数学函数,但C++函数可以做的不仅仅是返回一个依赖于其参数的数字。它可以读取和写入全局变量的值,也可以将输出写入屏幕并接收用户的输入。然而,在纯函数语言中,函数只能读取在其参数中提供给它的内容,而它对世界产生影响的唯一方式是通过它返回的值。
解释monad似乎就像解释控制流语句一样。想象一下,一个非程序员要求你解释它们?
你可以给他们一个涉及理论的解释——布尔逻辑、寄存器值、指针、堆栈和框架。但那太疯狂了。
你可以用语法来解释它们。基本上,C中的所有控制流语句都有大括号,您可以通过它们相对于括号的位置来区分条件和条件代码。这可能更疯狂。
或者,您也可以解释循环、if语句、例程、子例程以及可能的协例程。
Monad可以取代相当多的编程技术。语言中有一种特定的语法支持它们,还有一些关于它们的理论。
它们也是函数式程序员使用命令式代码而不承认它的一种方式,但这并不是他们唯一的用途。