![](/img/trans.png)
[英]Data type with type constraint in Wadler - Essence of functional programming paper,
[英]Wadler, “Monads for Functional Programming,” Section 2.8
編輯II:啊,好吧:我不明白a和b是如何綁定在eval的定義中的! 現在我做。 如果有人感興趣,這是跟蹤a和b的圖表。 我非常喜歡圖表。 我發誓,繪制箭頭確實改善了我的Haskell。
有時候我覺得自己真的很濃
在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)如何組合tick和unit來跟蹤執行的除法運算符的數量。 雖然非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.