我认为我对Haskell Monads有很好的处理,直到我意识到这段非常简单的代码对我来说没有意义(这来自haskell wiki about the State monad):
playGame :: String -> State GameState GameValue
playGame [] = do
(_, score) <- get
return score
令我困惑的是,为什么允许代码调用&#34; get&#34;,当提供的唯一参数是字符串时?看起来它几乎就像是从空气中汲取价值。
我提出问题的一个更好的方法可能是,如何使用>>=
和lambda&#39;而不是记号来重写此函数?我自己无法弄清楚。
答案 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变换器堆栈,它接受一个IO上下文,它产生一个foo上下文,它产生一个bar上下文,它产生一个baz上下文,它产生一个bin上下文。然后运行时系统&#34;调用&#34;根据需要多次加入此事。请注意这个想法与#34;调用堆栈&#34;的结合程度。事实上,这就是为什么它被称为&#34; monad变压器堆栈&#34;在哈斯克尔方面。它是一堆monadic上下文。