Haskell:计算“在monad中” - 意思是什么?

时间:2013-02-22 03:30:22

标签: haskell monads computation

在阅读monads时,我一直看到像“Xyz monad中的计算”这样的短语。计算“在某个monad中”是什么意思?

我认为我对monad的含义有一个公平的把握:允许计算产生通常是某种预期类型的​​输出,但可以替代地或另外地传达一些其他信息,例如错误状态,记录信息,状态等等。 on,并允许这样的计算被链接。

但是我不知道如何计算出一个单独的计算机。这只是引用产生monadic结果的函数吗?

示例:(搜索“计算中”)

5 个答案:

答案 0 :(得分:11)

  

这只是引用产生monadic结果的函数吗?

是的,简而言之。


很长一段时间,因为Monad允许您将值注入(通过return),但一旦进入Monad,它们就会被卡住。您必须使用evalWriterrunCont这样的功能,这些功能严格比Monad更具体,才能让值“退出”。

更重要的是,Monad(实际上,它的合作伙伴,Applicative)是拥有“容器”并允许计算在其中发生的本质。这就是(>>=)给你的东西,能够在Monad内部进行有趣的计算。

因此,像Monad m => m a -> (a -> m b) -> m b这样的函数可让您在Monad内和周围进行计算。像Monad m => a -> m a这样的函数可以注入Monad。像m a -> a这样的函数可以让你“逃避”Monad,除非它们一般不存在(仅限于特定的)。因此,为了对话,我们喜欢讨论像Monad m => m a这样的结果类型为“在monad中”的函数。

答案 1 :(得分:11)

通常,“monad中的计算”不仅意味着返回monadic结果的函数,而且意味着在do块内使用的函数,或者作为(>>=)的第二个参数的一部分,或其他任何与之相当的东西。区别与您在评论中所说的内容相关:

  

“计算”发生在func f中,在从输入monad中提取val之后,并且在结果被包装为monad之前。我不知道计算本身是如何“在”monad中的;它似乎显然“超出”了monad。

这不是糟糕的方式来考虑它 - 实际上,do符号鼓励它,因为它是查看事物的便捷方式 - 但它确实导致一种略带误导的直觉。没有任何东西可以从monad中“提取出来”。要了解原因,请忘记(>>=) - 这是一个支持do表示法的复合操作。 monad的更基本定义是三个正交函数:

fmap :: (a -> b) -> (m a -> m b)
return :: a -> m a
join :: m (m a) -> m a

...其中m是monad。

现在考虑如何使用以下方法实现(>>=):从类型m aa -> m b的参数开始,您唯一的选择是使用fmap来获取类型{ {1}}之后,您可以使用m (m b)展平嵌套的“图层”以获得join

换句话说,没有任何东西被从“monad”中“取出” - 而是将计算视为更深进入monad,连续的步骤被折叠成monad的单层单子。

请注意,从这个角度来看,monad定律也更简单 - 基本上,他们说当应用m b时,只要保留嵌套顺序(一种关联形式)并且join引入的monadic图层不执行任何操作(return的标识值)。

答案 2 :(得分:3)

通常以“类似集合”的monad为例,monad东西更易于掌握。想象一下,你计算两点的距离:

data Point = Point Double Double

distance :: Point -> Point -> Double
distance p1 p2 = undefined

现在你可能有一定的背景。例如。其中一个点可能是“非法的”,因为它超出了某些界限(例如在屏幕上)。因此,您将现有计算包装在Maybe monad中:

distance :: Maybe Point -> Maybe Point -> Maybe Double
distance p1 p2 = undefined

您具有完全相同的计算,但附加功能可能存在“无结果”(编码为Nothing)。

或者你有一组两个“可能”的点,并且需要它们的相互距离(例如,以后使用最短的连接)。然后列表monad就是你的“上下文”:

distance :: [Point] -> [Point] -> [Double]
distance p1 p2 = undefined

或者用户输入点数,这使得计算“不确定”(在某种意义上,你依赖于外部世界的事物,这可能会改变),那么IO monad就是你的朋友:

distance :: IO Point -> IO Point -> IO Double
distance p1 p2 = undefined

计算总是保持不变,但碰巧发生在某个“上下文”中,这增加了一些有用的方面(失败,多值,非确定性)。您甚至可以组合这些上下文(monad变换器)。

您可以编写一个统一上述定义的定义,并适用于任何 monad:

 distance :: Monad m => m Point -> m Point -> m Double
 distance p1 p2 = do
     Point x1 y1 <- p1
     Point x2 y2 <- p2
     return $ sqrt ((x1-x2)^2 + (y1-y2)^2)  

这证明我们的计算实际上与实际的monad相比非常独立,这导致了“x在(-side)y monad中计算”。

答案 3 :(得分:1)

查看您提供的链接,似乎&#34;计算的常见用法是&#34;关于单一的monadic值。摘录:

温和的介绍 - 这里我们在SM monad中运行计算,但计算是monadic值:

-- run a computation in the SM monad
runSM                   :: S -> SM a -> (a,S)

所有关于monads - 以前的计算是指序列中的monadic值:

  

&gt;&gt; function是一个便捷运算符,用于绑定不需要序列中先前计算输入的monadic计算

了解monad - 这里的第一个计算可以参考例如getLine,monadic值:

  

(binding)给出了在另一个计算中使用计算结果的内在想法,而不需要运行计算的概念。

作为类比,如果我说i = 4 + 2,那么i就是值6,但它同样是一个计算,即计算4 + 2。似乎链接的页面在这个意义上使用计算 - 计算作为monadic值 - 至少在某些时候,在这种情况下使用表达式&#34;&#34中的计算是有意义的;给定的monad。

答案 4 :(得分:0)

考虑IO monad。类型IO a的值是对大量(通常是无限)行为的描述,其中行为是IO事件序列(读取,写入等)。这样的值称为“计算”;在这种情况下,它是IO monad中的计算。