假设我们在Haskell中允许两种类型的函数:
区别将是f.x.通过声明一个点("。")作为函数名的第一个字母,将其声明为非纯函数。
我们还会制定规则:
有了这种语法糖和手头的规格 - 是否仍然需要Monads?莫纳德可以做哪些规则集不允许的事情吗?
当我开始了解Monads时 - 这正是他们的目的。只有那些非常聪明的人才能通过手边的导管理论工具,通过功能性方法实现这一目标。答案 0 :(得分:16)
Monads原则上与纯度或杂质无关。恰好IO
模型很好地修改了代码,但Monad
类可以完美地用于State
或Maybe
这样的绝对纯粹的实例。
Monads还允许以非常明确的方式表达复杂的上下文层次结构(因为我选择调用它们)。 "纯/不纯"不是您可能想要做的唯一部门。例如,考虑authorized
/ unauthorized
。可能的用途列表一直在继续...我们建议您查看其他常用的实例,例如ST
,STM
,RWS
,"限制IO"和朋友们更好地了解可能性。
很快你就会开始制作自己的monad,根据手头的问题量身定做。
答案 1 :(得分:11)
当我开始了解Monads时 - 这正是他们的目的。
Monads完全具有通用性,与纯度/杂质或命令性测序无关。所以没有,如果我理解你的问题,monad肯定不是效果封装的概念糖。
首先考虑前奏中的绝大多数单子:List
,Reader
,State
,Cont
,Either
,(->)
与效果或IO无关。假设IO是"规范"这是一个非常普遍的误解。 monad,虽然事实上它确实是一个堕落的案例。
答案 2 :(得分:4)
当我开始了解Monads时 - 这正是他们的目的。
这:http://homepages.inf.ed.ac.uk/wadler/topics/monads.html#monads是关于Haskell中monad的第一篇论文:
分类理论家在20世纪60年代发明了单子理论,以简明扼要地表达普遍代数的某些方面。
所以马上就可以看出monad与“纯”/“不纯”计算无关。最常见的monad(世界上!)是Maybe
:
data Maybe a
= Nothing
| Just a
instance Monad Maybe where
return = Just
Nothing >>= f = Nothing
Just x >>= f = f x
monad是四元组(Maybe, liftM, return, join)
,其中:
liftM :: (a -> b) -> Maybe a -> Maybe b
liftM f mb = mb >>= return . f
join :: Maybe (Maybe a) -> Maybe a
join Nothing = Nothing
join (Just mb) = mb
请注意,liftM
需要非Maybe
函数(不是纯粹的!)并将其应用于Maybe
,而join
需要Maybe
两级Just
并将其简化为单个图层(因此结果中的Just
来自两层join (Just (Just x)) = Just x
:
Nothing
虽然结果中的Nothing
可以来自任一层的join Nothing = Nothing
join (Just Nothing) = Nothing
:
Maybe
)。我们可以将这些术语翻译如下:
liftM
:可能存在或不存在的值。return
:将此函数应用于值(如果存在),否则不执行任何操作。Maybe
:获取此值并将其注入join
的额外结构。Maybe
:取一个(可能存在或不存在的值)可能存在或不存在的值,并删除“可能存在或不存在”这两个层之间的区别。现在,undef
是一种非常合适的数据类型。在脚本语言中,它只是通过使用Nothing
或等效来表达Just x
来表达,并以与x
相同的方式表示t*
。在C / C ++中,它通过使用指针类型NULL
表示,并允许指针为foreach
。在Scala中,有一个明确的容器类型:http://www.scala-lang.org/api/current/index.html#scala.Option。所以你不能说“哦,那只是异常”,因为有异常的语言仍然希望能够在不抛出异常的情况下表达“没有值”,然后在值存在的情况下应用函数(这就是Scala的Option类型的原因)有一个Maybe
方法)。但是'如果值在那里'应用此函数'恰恰是>>=
的{{1}}所做的!所以这是一个非常有用的操作。
您会注意到C和脚本语言通常不允许区分Nothing
和Just Nothing
- 值是否存在。在函数式语言中 - 就像Haskell一样 - 允许两个版本都很有意思,这就是为什么我们需要join
在我们完成它时才能消除这种区别。 (而且,从数学上讲,用>>=
和liftM
来定义join
比用其他方式更好。
顺便提一下,要清除关于Haskell和IO的常见错误概念:GHC的IO实现包装了GHC实现I / O的副作用。即使这是GHC的一个可怕的设计决定---命令式(不同于不纯的!)I / O可以在系统的任何级别上单独建模而不会产生杂质。您不需要副作用(在系统的任何一层)来进行I / O!