簡體   English   中英

Wadler,“功能編程的Monads”,第2.8節

[英]Wadler, “Monads for Functional Programming,” Section 2.8

編輯II:啊,好吧:我不明白ab是如何綁定在eval的定義中的! 現在我做。 如果有人感興趣,這是跟蹤ab的圖表。 我非常喜歡圖表。 我發誓,繪制箭頭確實改善了我的Haskell。

評估電子圖(PDF)

有時候我覺得自己真的很濃


在Wadler的“ Monads for Functional Programming ”第2.8節中,他將狀態引入了一個簡單的評估函數。 原始(非monadic)函數使用一系列let表達式跟蹤狀態,並且很容易遵循:

data Term = Con Int | Div Term Term
 deriving (Eq, Show)

type M a = State -> (a, State)
type State = Int

eval' :: Term -> M Int
eval' (Con a) x   = (a, x)
eval' (Div t u) x = let (a, y) = eval' t x in
                    let (b, z) = eval' u y in
                    (a `div` b, z + 1)

monadic評估器的單位綁定的定義同樣簡單明了:

unit :: a -> M a
unit a = \x -> (a, x)

(>>=) :: M a -> (a -> M b) -> M b
m >>= k = \x -> let (a, y) = m x in
                let (b, z) = k a y in
                 (b, z)

這里,(>> =)接受monadic值m :: M a ,函數k :: a - > M b ,並輸出monadic值M b m的值取決於lambda表達式中替換x的值。

然后Wadler引入了函數tick

tick :: M ()
tick = \x -> ((), x + 1)

再次,直截了當。 然而, 直接的是如何將這些函數鏈接在一起以產生一個評估函數,該函數返回執行的除法運算符的數量。 具體來說,我不明白:

(1)如何實施滴答 例如,以下是有效的函數調用:

(tick >>= \() -> unit (div 4 2)) 0
 ~> (2, 1)

但是,我無法用手正確評估它(表明我誤解了一些東西)。 特別是:(a)在0處評估tick的結果是((),0),那么lambda表達式如何接受()? (b)如果a是通過在0處調用tick返回的對中的第一個元素,那么如何評估單位

(2)如何組合tickunit來跟蹤執行的除法運算符的數量。 雖然非monadic評估器沒有問題,但bind的使用讓我感到困惑。

編輯:謝謝大家。 我認為我的誤解是lambda表達式的作用,'( - ) - unit(div 4 2)'。 如果我理解正確,

(tick >>= (\() -> unit (div m n)) x

擴展到

(\x -> let (a, y) = tick x in
       let (b, z) = (\() -> unit (div m n) a y) in
       (b, z)) x

當'a'應用於'() - > unit(div mn)a y'時,不會產生'實際結果'。 通過將任何變量與lambda運算符綁定,並用值替換它,可以實現相同的效果。 在這種情況下, 綁定的多功能性是任何M a都可以傳遞給它。 如上所述,值M a表示計算,例如'eval'。 因此:

eval (Con a) = unit a

eval (Div t u) = eval t >>= (\a ->
                  eval u >>= (\b ->
                   tick >>= (\c -> unit (a `div` b))))

如果我理解正確,'eval t'代替m和表達式的其余部分,即函數

'(\a -> eval u >>= (\b -> tick >>= (\c -> unit (a `div` b))))'

代替k 評估'eval t'的結果綁定到(a,y),並且評估k的結果綁定到(b,z)。 我有辦法去,但這有點清除它。 謝謝。

1A)

在0處評估tick的結果是((),1) - 再次查看代碼,它將輸入值增加1。

lambda表達式接受()因為它是綁定操作的右側,這意味着它的類型應該是(() - > M b)。 因此它將()作為其第一個參數,然后使用“unit”作為M b項。

圖1b)

我不太清楚你在這里問的是什么。 綁定運算符被定義為將結果和狀態從第一個操作(分別是()和1)傳遞到第二個操作,因此單元最終被傳遞1作為當前狀態(結果,()被吞下lambda表達式)。 當前狀態由單位函數保持原樣,結果是4 div 2的結果,即2。

2)

大概你會想要一些類型的功能:

divCounted :: Int -> Int -> M Int

它既可以組合刻度和單位(類似於你的方式),確保勾選一次以增加計數,並使用單位來回饋結果。

您可以像這樣手動評估表達式:

(tick >>= \() -> unit (div 4 2)) 0

如果將tick\\() -> unit (div 4 2)插入到>>=的定義中,則變為:

(\x -> let (a, y) = tick x in
       let (b, z) = (\() -> unit (div 4 2)) a y in
       (b, z)) 0

如果現在通過將0替換為x應用該函數,則得到:

let (a, y) = tick 0 in
let (b, z) = (\() -> unit (div 4 2)) a y in
(b, z)

現在讓我們將tick應用於0:

let (a, y) = ((), 0 + 1) in
let (b, z) = (\() -> unit (div 4 2)) a y in
(b, z)

所以a ()y變為0+1 ,即1 所以我們有

let (b, z) = (\() -> unit (div 4 2)) () 1 in
(b, z)

如果我們將函數應用於()我們得到

let (b,z) = unit (div 4 2) 1 in
(b,z)

如果我們申請單位,我們得到

 let (b,z) = (div 4 2, 1) in
 (b,z)

div 4 2是2,結果是(2,1)

1a)在0處評估tick的結果是((),1),那么lambda表達式如何接受()?

關於狀態monad的關鍵是bind負責該對的第二個組件,即狀態。 lambda表達式只需要處理() ,該對的第一個組件,返回值。

一般來說,關於monad M的關鍵在於它抽象了整個國家的線索。 您應該將類​​型M a的值視為計算機程序,它返回類型a的值,同時還會弄亂狀態。 洞察力是兩個操作unit>>=足以編寫任何這樣的程序; 構建和解構對(a,s)的整個業務可以在這兩個函數中捕獲。

暫無
暫無

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

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