在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
当前回答
http://mikehadlow.blogspot.com/2011/02/monads-in-c-8-video-of-my-ddd9-monad.html
这是你要找的视频。
用C#演示组合和对齐类型的问题,然后用C#正确实现它们。最后,他展示了F#和Haskell中相同的C#代码的外观。
其他回答
实际上,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解析器组合器库时。(也就是说,根据我的经验)
不要挂断电话:-分类理论-可能是月-莫纳德定律-哈斯克尔- !!!!
在Coursera“反应式编程原理”培训中,Erik Meier将其描述为:
"Monads are return types that guide you through the happy path." -Erik Meijer
遵循您简短、简洁、实用的指示:
理解monad最简单的方法是在上下文中应用/组合函数。假设你有两个计算,它们都可以看作是两个数学函数f和g。
f取一个String并生成另一个String(取前两个字母)g获取一个String并生成另一个String(大写转换)
因此,在任何语言中,“取前两个字母并将其转换为大写”的转换都会写成g(f(“某个字符串”))。因此,在纯完美函数的世界中,合成只是:先做一件事,然后再做另一件事。
但假设我们生活在一个功能可能失败的世界中。例如:输入字符串可能有一个字符长,因此f将失败。所以在这种情况下
f获取一个String并生成一个String或Nothing。g仅在f未失败时生成字符串。否则,将不生成任何内容
所以现在,g(f(“somestring”))需要一些额外的检查:“计算f,如果它失败,那么g应该返回Nothing,否则计算g”
此思想可应用于任何参数化类型,如下所示:
让Context[Sometype]是Context中Sometype的计算。考虑功能
f: :AnyType->上下文[Sometype]g: :某些类型->上下文[AnyOtherType]
合成g(f())应该读作“compute f。在这个上下文中,做一些额外的计算,然后计算g,如果它在上下文中有意义”
monad是一种将共享共同上下文的计算组合在一起的方法。这就像建立一个管道网络。当构建网络时,没有数据流过它。但是当我用“bind”和“return”将所有位拼接在一起后,我调用类似runMyMonad monad数据的东西,数据流过管道。
对于来自命令式背景(c#)的人,
考虑以下代码
bool ReturnTrueorFalse(SomeObject input)
{
if(input.Property1 is invalid)
{
return false;
}
if(input.Property2 is invalid)
{
return false;
}
DoSomething();
return true;
}
您会看到很多这样的代码,甚至不会看到早期返回,但所有检查都是嵌套完成的。现在,Monad是一种模式,它可以像下面一样被压平
Monad<bool> ReturnTrueorFalse(SomeObject input) =>
from isProperty1Valid in input.Property1
from isProperty2Valid in input.Property2
select Monad.Create(isProperty1Valid && isProperty2Valid);
这里有几点需要注意。首先,更改函数的返回值。其次,输入的两个财产都必须是Monad。接下来,Monad应该实现SelectMany(LINQ的展平运算符)。由于SelectMany是为该类型实现的,因此可以使用查询语法编写语句
那幺,什么是莫纳德?它是一种以可组合方式对返回相同类型的表达式进行扁平化的结构。这在函数式编程中特别有用,因为大多数函数式应用程序倾向于将状态和IO保持在应用程序的边缘层(例如:控制器),并在整个调用堆栈中返回基于Monad的返回值,直到需要解包该值。当我第一次看到这张照片时,我最大的优点是它很容易在眼睛上看到,也很有陈腔滥调。
每个c#(现在几乎每个人)开发人员都能立即识别的Monad的最佳示例是async/await。在.Net4.5之前,我们必须使用ContinueWith编写基于任务的语句来处理回调,在async/await之后,我们开始使用同步语法来处理异步语法。这是可能的,因为Task是一个“monad”。
关于OOP开发人员的详细说明,请参阅本文,这是一个简单的实现和语言文本,其中包含许多很棒的Monad和大量关于函数式编程的信息