Monads解决什么编程问题?[关闭]


14

我读过很多文章来解释什么是单子,如何使用unitbind工作,其中一些直接陷入类别理论,如此抽象(至少对我而言),使眼睛流血,有些人完全忽略了这些,并触及了奇怪的类比。墨西哥卷饼,盒子和什么不。

经过几周的研究和大量的油炸神经元,(我认为)我了解了Monads的工作原理。但是,还有一件事让我无法理解,实际上很少有文章涉及(IO和状态除外):

为什么?

为什么Monad重要?他们为什么如此重要?他们正在解决什么问题?那些问题只能用Monad来解决吗,或者还有其他方法吗?


3
程序又大又复杂。我们需要构建它们的方法。有很多方法。Monad是一个。箭是一个。函子是一个。对象,类,函数,方法,模块,特性,混合包,包是其他一些,还不清楚您的具体问题是什么。您是在问为什么我们需要构造程序吗?
约尔格W¯¯米塔格

1
@JörgWMittag:我不是在问为什么我们需要构造程序。显然,一个大问题被分解为多个小问题,您可以将它们解决,然后合并以获得大问题的解决方案。我在问Monads解决什么问题。是吗 构建代码?这是大惊小怪的吗?例如,在Haskell中,这就是您执行I / O的方式。在那儿,单子假装是一种假装,而实际上却不是。我的问题在标题中,我不知道如何以更清晰的方式说明。
Dummy



1
@RobertHarvey:我还找到了您添加的前两个链接,但是就像在这些帖子和其他帖子的答案中一样,响应直接指向Maybe,State和IO monad,然后迅速跳转到示例或代码,最后以“有很多内容Monads可以解决的问题”。还有哪些其他问题?我正在寻找一个更高级别的答案,而不是重复相同的示例实际上是问题的根源(无论是什么问题),并解释Monads如何解决它们以及为什么它是最佳选择,而不是使用其他东西。非常感谢您的反馈。
Dummy Me

Answers:


10

不需要 monad解决任何问题。他们只是使某些事情变得简单。很多人在解释单子时变得过于抽象和理论化。通常,monad是一种在编程中反复出现的模式。通过识别该模式,我们可以简化代码并避免重新实现某些功能。

对于Haskell而言,从具体的角度来看,monads最容易看到的就是符号。 Haskell和其他语言的列表理解也大量使用了monad。您还可以创建类似Control.Monad的库。

这些都提供了有用的简化,一旦为一个monad实现了它,就可以为所有 monad 自动获得它。这是代码重用对于函数式编程而言比其他范例容易得多的主要原因之一。


2
我实际上要说的是,haskell monads最明显的作用是使IO效果按您期望的方式实际工作的简便方法。我想任何想知道单子给我们的人都应该尝试米兰达。米兰达语是Haskell旨在替代的语言,对于具有Haskell经验的人来说,应该非常容易学习。主要区别在于,它没有Monadic IO,这使得该语言比Haskell更加难以处理任何无关紧要的项目。
Jules

是的,IO是Haskell最著名的monad,但我没有列出单个monad的示例。我正在列出它们支持的总体抽象的示例。例如,我试图弄清楚为什么第一个想到将monad用于IO的人会认为这通常是个好主意。
Karl Bielefeldt

1
@朱尔斯:或者看看哈斯克尔的历史。在Monads之前,Haskell设计师尝试使用“惰性流”和“ Continuations”作为I / O的基础,并且两者均难以使用。
约尔格W¯¯米塔格

5

如果查看特定的monad并查看它们可以解决什么问题,则更容易理解。例如,在Haskell中:

  • IO:允许IO在类型系统中表示,因此您可以将纯函数与执行IO的函数明确分开。

  • 列表:允许您进行列表理解和节点确定性计算。

  • 也许:null的更好替代方案,并支持与C#的null推算运算符相当的东西。

  • Parsec:用于编写解析器的便捷DSL。

因此,很容易(我希望)了解单个monad的原理,因为它们都很有用。但是,他们解决的问题也大不相同,从表面上看,它们似乎并没有太多共通之处,只是它们都与链接操作的某些逻辑有关。Monad很有用,因为它们使您可以构建各种工具。

上面的示例可以在没有monad的情况下实现吗?当然,它们可以以即席方式实现,但是在语言中内置了monad可以提供直接的语言支持,例如do-notation可以与所有monad一起使用。


2

让人感到困惑的一件事是“流行”功能类似bind<*>是面向实践的。但是,要了解这些概念,首先查看其他功能会更容易。还值得一提的是,与其他关联概念相比,monad表现得有些夸张。因此,我将从仿函数开始。

函子提供功能(以Haskell表示法)fmap :: (Functor f) => (a -> b) -> f a -> f b。换句话说,您具有f可以将功能提升到的上下文。您可以想象,几乎所有的东西都是函子。列表,也许,函数,I / O,元组,解析器……每个都代表一个可以在其中出现值的上下文。因此,您可以使用fmap或其内联变体来编写可在几乎任何上下文中工作的极其通用的功能<$>

您还想对上下文做些什么?您可能要结合两个上下文。所以,你可能希望得到的推广zip :: [a] -> [b] -> [(a,b)],例如像这样:pair :: (Monoidal f) => f a -> f b -> f (a,b)

但是,因为它在实践中甚至更有用,所以Haskell库改为提供Applicative,它是FunctorMonoidal,以及的组合Unit,它只是添加了您可以实际将值“放入”上下文的内部unit

您只需声明关于所使用上下文的三件事,就可以编写极其通用的函数。

Monad您可以在此之上声明另一件事。我之前没有提到的是,您已经有两种方法来组合两个上下文:您不仅可以将pair它们组合在一起,还可以将它们堆叠在一起,例如可以有一个列表列表。在I / O上下文中,一个示例是可以从文件读取其他I / O操作的I / O操作,因此您将拥有一个type FilePath -> IO (IO a)。我们如何摆脱那种堆叠以获得可执行的功能IO a?这就是Monads的join用武之地,它使我们可以合并两个相同类型的堆叠上下文。解析器,Maybe等bind也是如此。这只是一种更实用的使用方式join

因此,一元上下文只需要提供四样东西,它就可以与为I / O,解析器,故障等开发的几乎所有机器一起使用。


1

Monads使您可以表达各种非纯计算以及简化代码

  • 有状态计算(通过monad获取/设置状态)
  • I / O(日志,UI,文件或仅产生/使用X列表)
  • 同样,“非线性”控制流(例如,异常等)

而且,重要的是在不损害纯语言构造和从中获得更简洁的语言的前提下


2
没错-但这只是monad用途的一部分。列出理解或Maybe与任何外部无关。
JacquesB '16

如果投票者解释了这样做的原因,将会很有帮助。我认为这个答案没有错。
Jules

@JacquesB是有状态的。
Jules

1
不过,世界类型,唯一性类型,线性类型也可以使您做到这一点。
约尔格W¯¯米塔格

@Jules列为非确定性monad是否有状态?您能否阐明“状态”的定义是什么?
杰克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.