为什么我可以在不提供monad的情况下调用monadic函数?

时间:2014-04-03 15:15:00

标签: haskell monads state-monad

我认为我对Haskell Monads有很好的处理,直到我意识到这段非常简单的代码对我来说没有意义(这来自haskell wiki about the State monad):

playGame :: String -> State GameState GameValue
playGame []     = do
  (_, score) <- get
  return score

令我困惑的是,为什么允许代码调用&#34; get&#34;,当提供的唯一参数是字符串时?看起来它几乎就像是从空气中汲取价值。

我提出问题的一个更好的方法可能是,如何使用>>=和lambda&#39;而不是记号来重写此函数?我自己无法弄清楚。

2 个答案:

答案 0 :(得分:8)

将其伪装成do符号看起来像

 playGame [] =
   get >>= \ (_, score) ->
   return score

我们也可以用fmap

写这个
 playGame [] = fmap (\(_, score) -> score) get
 playGame [] = fmap snd get

现在的诀窍是要意识到get是一个类似

的值的任何其他值
 State s s

在我们将计算提供给get或我们为州提供明确起始值的类似情况之前,确定runState将返回的内容不会被确定。

如果我们进一步简化这一点并摆脱状态monad,我们就

playGame :: String -> (GameState -> (GameState, GameValue))
playGame [] = \gamestate -> (gamestate, snd gamestate)

状态monad正在绕过GameState的所有手动传递,但您可以将get视为访问我们的&#34;函数&#34;过了。

答案 1 :(得分:0)

monad是一个&#34;&#34;这需要一个上下文(我们称之为m)以及哪些&#34;产生&#34;一个价值,同时仍然尊重monad法律。我们可以从内心&#34;内部&#34;和&#34;外面&#34;单子。 monad法则告诉我们如何处理&#34;往返&#34; - 走到外面然后回到里面。特别是,法律告诉我们m(m a)与(m a)的类型基本相同。

关键是monad是这种往返事物的概括。将南瓜(m(m a))加入(m a)&,并且(&gt;&gt; =)从monad中拉出一个值并将一个函数应用到monad中。换句话说,它将一个函数(f :: a - &gt; mb)应用于a(ma) - 产生一个(m(mb)),然后通过join压缩得到我们的(mb)。

那么这与&#39; get&#39;有什么关系呢?和对象?

好吧,用符号设置我们,以便计算的结果在我们的monad中。并且(&lt ;-)让我们拉出一个monad的值,这样我们就可以将它绑定到一个函数,同时仍然名义上在monad中。所以,例如:

doStuff = do
   a <- get
   b <- get
   return $ (a + b)

请注意a和b是纯粹的。他们在外面&#34;得到的,因为我们实际上偷看了它。但是现在我们在monad之外有一个值,我们需要用它做一些事情(+)然后把它放回monad中。

这只是一些暗示性的符号,但如果我们可以这样做可能会很好:

doStuff = do
  a       <- get
  b       <- get
  (a + b) -> (\x -> return x) 

要真正强调它的来回。完成monad操作后,必须位于该表中的右侧列中,因为当操作完成后,&#39;加入&#39;将被调用以压平图层。 (至少在概念上)

哦,对,对象。嗯,显然,OO语言基本上存在并且在某种IO monad中呼吸。但我们实际上可以将其分解。当你按照以下方式运行时:

 x = foo.bar.baz.bin()

你基本上运行一个monad变换器堆栈,它接受一个I​​O上下文,它产生一个foo上下文,它产生一个bar上下文,它产生一个baz上下文,它产生一个bin上下文。然后运行时系统&#34;调用&#34;根据需要多次加入此事。请注意这个想法与#34;调用堆栈&#34;的结合程度。事实上,这就是为什么它被称为&#34; monad变压器堆栈&#34;在哈斯克尔方面。它是一堆monadic上下文。