簡體   English   中英

Monad 中的 do 符號在 Haskell 中是什么意思

[英]How does the do notation in Monad mean in Haskell

考慮以下代碼段

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 
    a <- pop 
    pop

正如我們所知,Monad 中的do notation>>=運算符相同。 我們可以將此段重寫為:

push 3 >>= (\_ ->
pop >>= (\a ->
pop))

這個表達式的最終值與push 3和第一個pop無關,無論輸入什么,它都只返回pop ,所以不會先把值3入棧再pop,為什么會這樣?


感謝您的回復。 我在上面添加了缺少的代碼( Stackpushpop ),我想我已經弄清楚它是如何工作的。 理解這段代碼的關鍵是理解State s monad的實現:

instance Monad (State s) where 
    return x = State $ \s -> (x, s) 
    (State h) >>= f = State $ \s -> let (a, newState) = h s 
                                        (State g) = f a
                                    in g newState

>>= do 的整個實現是將狀態傳遞給函數h ,計算其新狀態並將新狀態傳遞給f 中隱含的函數g ,從而獲得更新的狀態。 所以實際上狀態已經隱式改變了。

由於 Monad 法則,您的代碼相當於

stackManip :: State Stack Int
stackManip = do 
    push 3 
    a <- pop    -- a == 3
    r <- pop
    return r

所以你壓入 3,彈出它,忽略彈出的 3,彈出另一個值並返回它。

Haskell 只是另一種編程語言。 你是程序員。 編譯器是否跳過無關緊要的指令取決於它,並且無論如何都是不可觀察的(除非通過檢查編譯器生成的代碼,或在執行代碼時測量 CPU 的熱量,但在服務器中可能有點困難)極圈以外的農場)。

Haskell 中的 Monad 有時被稱為“可編程分號”。 總的來說,我覺得這不是一個特別有用的短語,但它確實捕捉到了用 Haskell 的do表示法編寫的表達式具有命令式程序的味道的方式。 特別是do塊中的“語句”組合方式取決於所使用的特定 monad。 因此,“可編程分號”——連續的“語句”(在許多命令式語言中用分號分隔)組合在一起的方式可以通過使用不同的 monad 來改變(“編程”)。

由於do表示法實際上只是使用>>=運算符從其他人構建表達式的語法糖,因此每個 monad 的>>=實現決定了它的“特殊行為”是什么。

例如, Monad例如Maybe允許一個,作為一個粗略的說明中,與工作Maybe值,如同它們是實際值的基礎類型的,同時確保,如果一個非值(即, Nothing )發生在任何點,計算短路和Nothing將是整體結果。

對於列表 monad,每一行實際上都被“執行”多次(或沒有)——對列表中的每個元素執行一次。

對於State s monad 的值,這些本質上是s -> (a, s)類型s -> (a, s) “狀態操作函數” - 它們采用初始狀態,並從中計算出新狀態以及某種類型的輸出值a >>=實現 - “分號” - 在這里*所做的只是確保,當一個函數f :: s -> (a, s)后跟另一個g :: s -> (b, s) ,結果函數將f應用於初始狀態,然后將g應用於從f計算出的狀態。 它基本上只是函數組合,稍作修改,以便還允許我們訪問類型不一定與狀態相關的“輸出值”。 這允許人們在do塊中一個接一個地列出各種狀態操作函數,並知道每個階段的狀態正是由前幾行放在一起計算的狀態。 這反過來又允許一種非常自然的編程風格,您可以在其中給出連續的“命令”來操作狀態,但實際上不會進行破壞性更新,或者以其他方式脫離純函數和不可變數據的世界。

*嚴格來說,這不是>>=而是>> ,一個從>>=派生的操作,但忽略了輸出值。 您可能已經注意到,在示例中,我給出了f輸出的a值被完全忽略了 - 但是>>=允許檢查該值並確定下一步要執行的計算。 do表示法中,這意味着先寫a <- f然后再使用a 這實際上是將 Monad 與它們功能較弱但仍然很重要的表親(特別是 Applicative functor)區分開來的關鍵。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM