2013-04-10 21 views
3

我有一些麻煩搞清楚/如何/綁定操作實際上綁定在一起以下國家的單子:哈斯克爾 - 單子約束力的評估順序

pop :: State [Int] Int 
pop = do 
     (x:xs) <- get 
     put xs 
     return x 

push :: Int -> State [Int]() 
push x = do 
      xs <- get 
      put (x:xs) 

doStuff :: State [Int]() 
doStuff = do 
      pop 
      x <- pop 
      push 5 
      push x 

採取doStuff,可脫到以下幾點:

pop >>= (\_ -> pop >>= (\x -> push 5 >>= (\_ -> push x))) 

當這條線被評估時,綁定實際發生的順序是什麼?因爲要實際綁定,Haskell需要從>>=運算符右邊的函數中獲得狀態monad(即,需要首先充分評估函數右操作數),但我會認爲會發生以下情況:

  1. S1 = push 5 >>= (\_ -> push x)
  2. S2 = pop >>= (\x -> s1)
  3. S3 = pop >>= (\_ -> s2)

這是考慮它的正確方法?我覺得我很好地理解單子,但是我最大的問題在於真正想象「幕後」發生了什麼,以及數據如何流動,可以這麼說。 do表示法給出了我正在處理一系列順序操作的錯覺,實際上,有大量的嵌套和閉包。

我感覺有點像我在這裏過度思考的事情,並進一步混淆了我自己。

+1

您應該從狀態monad擴展'>> ='的定義,以瞭解發生了什麼。沒有什麼特別的,只是普通的評估。 – augustss 2013-04-10 13:09:59

+0

評估策略在這裏並不重要,所以沒有正確的方法來考慮它。通過嘗試不同的評估方式,您可以輕鬆看到這一點。結果將始終如一。 – 2013-04-10 18:47:20

回答

8

pop >>= (\_ -> pop >>= (\x -> push 5 >>= (\_ -> push x))) 

的一些功能可以被內聯開始(對所發生的事情更好)。我將從(>>=)開始,假設State未被定義爲變壓器或新類型,以使事情簡單化。

type State s a = s -> (a, s) 
m >>= k = \ s -> let (a, s') = m s in k a s' 

\ s -> let (a, s') = pop s in 
(\ _ -> pop >>= (\ x -> push 5 >>= (\ _ -> push x))) a s' 

\ s -> let (_, s') = pop s in 
(pop >>= (\ x -> push 5 >>= (\ _ -> push x))) s' 

\ s -> let (_, s') = pop s in 
let (a, s'') = pop s' in 
(\ x -> push 5 >>= (\ _ -> push x)) a s'' 

\ s -> let (_, s') = pop s in 
let (a, s'') = pop s' in 
(push 5 >>= (\ _ -> push a)) s'' 

\ s -> let (_, s') = pop s in 
let (a, s'') = pop s' in 
let (b, s''') = push 5 s'' in 
(\ _ -> push a)) b s''' 


\ s -> let (_, s') = pop s in 
let (a, s'') = pop s' in 
let (_, s''') = push 5 s'' in 
push a s''' 
3

這是正確的思考方式嗎?

首先:而這顯然是正確的想在IO單子「首先出現這種情況,比的話,我們評估這種鍵盤輸入......」,這」不是個所有單子都是如此。例如,在monad列表中,這沒有任何意義。一般來說,根本不可能在Haskell中計算特定的順序,它不是定義的行爲。

然而,它始終是可能,很多時候有幫助,想計算的一個單子的秩序,這個順序實際上是在一個由do符號提示。所以,大多數情況下,思考脫俗的表情並不真切。但是,如果你想使這一步,這裏就是我會做它:

  1. pop >>= \_ -> THUNK1

  2. THUNK1≡>pop >>= \x -> THUNK2

  3. {Closure{x}} THUNK2≡>push 5 >>= \_ -> THUNK3

  4. {Closure{x}} THUNK3≡>push x

這當然令人難以置信的更醜陋,但表示幾乎與加糖do表達式一樣。

2

當這條線被評估時,綁定實際發生的順序是什麼?

這裏沒有什麼特別的「綁定」。 desugared表達式的評估方式與其他表達式完全相同(懶惰),具體取決於您正在使用的特定monad的實現(>>=)

如果我們談論的是使用類似runState,給予類似的foo >>= (\x -> bar)的表達,最外面的表達式是(>>=)應用程序,但我們正在努力解開一個newtype,然後應用內部的功能,所以(>>=)被強制,功能也一樣。

如果我們考慮改爲列表monad,(>>=)concatMap。給定像[foo1, foo2] >>= (\x -> [bar1, bar2, x] >>= (\y -> [baz, y]))這樣的表達式,對結果使用take 5顯然不會完全計算所有綁定。


也就是說,這裏存在一個重要的規則:到任何程度評估x >>= f強制的x評價,在一個大的表現像一個脫do塊將出現明顯的「順序」爲了強迫,爲與「順序錯覺」是可能的相同的原因。