[英]Monads and do notation
如果我們有以下代碼:
import Control.Monad.State
type Stack = [Int]
pop :: State Stack Int
pop = state $ \(x:xs) -> (x,xs)
push :: Int -> State Stack ()
push a = state $ \xs -> ((),a:xs)
stackManip :: State Stack Int
stackManip = do
push 3
x <- pop
pop
與我們一起運行:
command: runState stackManip [1]
result: (1,[])
“ Haskell”如何知道將“ x”的值賦予3? 那么,他怎么知道從州單子中取出3個而不是州(在這種情況下為堆棧)?
Maybe monad的相同問題:
do
x <- Just 5
這里只有一個值,但是Haskell如何知道將'5'取為'x'?
這歸結為>>=
的定義。 你應該注意到, do
---這里使用的功能---只是薄語法糖調用>>=
:
do
x <- pop
pop
= pop >>= \ x -> pop
並且(忽略新類型包裝器) >>=
為State
定義為:
a >>= f = \ s -> case a s of
(x, s') -> f x s'
因此, f
將結果作為第一個參數(名為x
參數)並將狀態作為其(靜默)第二個參數( pop
定義內的參數)獲取結果。
請注意,您無法以其他方式定義>>=
:
a >>= f = \ s -> case a s of
(x, s') -> f s' x
因為那樣它就會有類型
State s a -> (s -> State a b) -> State a b
這通過類型分配了錯誤的類型變量。 (這就是Haskell偏愛單字母類型變量的原因:相同的類型變量比其實際含義要重要得多)。
x <- pop
大致從pop
獲取x
。 pop
操作返回3
,而不是整個堆棧。 您還可以從以下類型中看到:
pop :: State Stack Int -- returns Int, keeps Stack as state
相比之下, get
具有以下類型:
get :: State Stack Stack -- returns Stack, keeps Stack as state
因此, x <- get
將獲得整個狀態堆棧。
如果您需要更詳細的說明,建議您查看如何為State Stack
monad定義(>>=)
。 然后,將其刪除后,將其應用於您的代碼
push 3 >>= ( \ _ ->
pop >>= ( \ x ->
pop ))
經過許多簡化之后,您應該達到類似以下的目的:
State (\stack0 -> let (_, stack1) = unState (push 3) stack0
(x, stack2) = unState pop stack1
(_, stack3) = unState pop stack2
in stack3 )
然后,您可以根據需要內聯push
和pop
並繼續進行簡化。 但是您已經在上面看到了x
的來源。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.