[英]Monad Stack Penetration Classes with Free/Operational Monad Transformers?
[英]Is operational really isomorphic to a free monad?
在這篇博文中,Tekmo指出我們可以證明ExitSuccess
退出是因為(我認為)它就像構造函數的Const
函子(它不帶x
因此fmap
行為類似於const
)。
通過操作包,Tekmo的TeletypeF
可能會被翻譯成如下:
data TeletypeI a where
PutStrLn :: String -> TeletypeI ()
GetLine :: TeletypeI String
ExitSuccess :: TeletypeI ()
我已經讀過,操作與一個免費monad是同構的,但我們可以在這里證明ExitSuccess
退出嗎? 在我看來它遇到了與exitSuccess :: IO ()
完全相同的問題,特別是如果我們要為它編寫一個解釋器,我們需要把它寫成就像它沒有退出一樣:
eval (ExitSuccess :>>= _) = exitSuccess
與不涉及任何模式通配符的免費monad版本相比:
run (Free ExitSuccess) = exitSuccess
在操作Monad教程中, apfelmus提到了一個缺點:
狀態monad表示為s - >(a,s)可以應對一些無限的程序,如
evalState (sequence . repeat . state $ \\s -> (s,s+1)) 0
而指令列表方法沒有希望處理它,因為只有最后一個Return指令才能返回值。
對於免費的monad也是如此嗎?
(請允許我通過大膽結合以前的答案來獲得大獎。;-))
關鍵的觀察是:證明究竟是什么? 根據Free TeletypeF
的表述,我們可以證明以下內容:
Free TeletypeF a
類型程序的每個解釋器在遇到ExitSuccess
指令時必須退出。 換句話說,我們自動獲得代數定律interpret (exitSuccess >>= k) = interpret exitSuccess
因此, Free
monad實際上允許您將某些代數定律烘焙到該類型中。
相反,操作方法不限制ExitSuccess
的語義,沒有與每個解釋器相關的相關代數法。 可以編寫在遇到此指令時退出的解釋器,但也可以編寫不能解釋的解釋器。
當然,您可以通過檢查證明任何特定的解釋器滿足法律,例如因為它使用通配符模式匹配。 Sjoerd Visscher觀察到您還可以通過將ExitSuccess
的返回類型ExitSuccess
為Void
來在類型系統中強制執行此操作。 然而,這不適用於可以融入自由單子的其他法律,例如mplus
指令的分配法則。
因此,在一個令人困惑的事件轉變中,如果你將“自由”解釋為“最少量的代數定律”,那么操作方法比免費monad更自由。
這也意味着這些數據類型不是同構的。 但是,它們是等價的:每個用Free
編寫的解釋器都可以轉換成用Program
編寫的解釋器,反之亦然。
就個人而言,我喜歡將我的所有法律都放入翻譯中,因為有許多法律無法融入免費的monad中,我喜歡將它們全部放在一個地方。
答案是肯定的,但僅當您使用TeletypeF
的其他翻譯時:
data TeletypeI a where
PutStrLn :: String -> TeletypeI ()
GetLine :: TeletypeI String
ExitSuccess :: TeletypeI Void
TeletypeI
的論點是操作將/必須提供給程序的其余部分。 它是繼續k
in的參數的類型
eval (ExitSuccess :>>= k) = ...
由於沒有Void
類型的值,我們可以確定永遠不會調用k
。 (一如既往,我們必須忽略undefined
。)
等效類型是:
data TeletypeI a where
PutStrLn :: String -> TeletypeI ()
GetLine :: TeletypeI String
ExitSuccess :: TeletypeI a
現在我們必須為k
匹配任何類型提供一個值,我們也不能這樣做。 這可能更實用,因為singleton ExitSuccess
現在具有靈活類型Program TeletypeI a
。
類似地, exitSuccess
可以通過給它類型IO Void
或IO a
來修復。
答案是否定的,你無法證明操作者忽略了exitSuccess
上的其余程序。 將TeletypeI
與TeletypeF
對比以了解原因。 為了便於比較,我將用GADT表示法重寫TeletypeF
data TeletypeF x where | data TeletypeI x where
PutStrLn :: String -> x -> TeletypeF x | PutStrLn :: String -> TeletypeI ()
GetLine :: (String -> x) -> TeletypeF x | GetLine :: TeletypeI String
ExitSuccess :: TeletypeF x | ExitSuccess :: TeletypeI ()
使用TeletypeF
,我們可以立即構建實際程序:
GetLine (\str -> PutStrLn (map toUpper str) ExitSuccess)
TeletypeI
不拿出一個辦法來指代“節目的其余部分”,因為同樣的方式TeletypeF
一樣。
-- TeletypeF:
GetLine (\str -> "rest of program" goes here)
-- or
PutStrLn someString ("rest of program" goes here)
-- or
ExitSuccess -- there is no "rest of program" slot provided
由於TeletypeI
缺少此“程序的其余部分”信息,因此當您遇到ExitSuccess
時,您將無法獲得任何知識。
-- TeletypeI
PutStrLn someString -- no information about "rest of program"
-- or
GetLine -- no information about "rest of program"
-- or
ExitSuccess -- no information about "rest of program"
允許來的是“程序的其余部分”完全取決於Program
類型,它對應用它的指令集一無所知。 它只允許您將指令綁定在一起,並通過Return
終止。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.