![](/img/trans.png)
[英]Freer-Simple Freer Monads How do I Unify IO Exception Handling with Error Effect
[英]What are freer monads?
這個詞我聽過幾次,但我仍然不知道什么是所謂的“Freer Monad”。 這個名字讓我想到了 Free Monads,但我不明白它們到底有什么關系。 我在 hackage 上找到了一些庫: http ://hackage.haskell.org/package/freer ,但是那里的示例對我沒有多大幫助。
我認為我根本不理解這個想法,因此我看不到它們有任何好的用例。 我也想知道它們比免費的 monad 和經典的 mtl 堆棧有什么優勢。
我知道這是一個舊線程,但我想我還是會回答它以防萬一
什么是所謂的“Freer Monad”
根據原始論文Freer Monads, More Extensible Effects a “Freer Monad”本質上是一個 Free Monad,沒有 Free Monad 的必要 Functor 約束。
一個自由的 monad 基本上是 monadic 結構的本質; 仍然是 monad 的“最小”的東西。 在這篇文章中可以找到一個非常好的實用解釋方法。 這篇文章還表明,“正常”的自由 monad 需要一個 Functor 約束。
然而,在每個函數中添加函子約束通常很乏味(有時甚至實現起來可能很奇怪),事實證明,通過“將函子功能”移動到Impure
構造函數的參數中,實現方可以改變輸出本身的類型(所以沒有通用函子),可以擺脫這個約束。 這是通過使用GADTs
完成的GADTs
來自Freer Monads
論文的示例)
data Free f a = Pure a
| Impure (f (Free f a))
instance Functor f => Monad (Free f) where
變成
data FFree f a where
Pure :: a → FFree f a
Impure :: f x → (x → FFree f a) → FFree f a
instance Monad (FFree f) where
[...]
Impure fx k’ >>= k = Impure fx (k’ >>> k)
這基本上讓后面的實現選擇如何執行固定 [雙關語] 到適當的“輸出/包裝類型”的fmap
操作。 所以根本的區別本質上是可用性和通用性。
因為有一些混淆: FFree
是 Freer monad 並且對應於包freer-simple
中的Eff
。
對他們來說很好的用例
Freer monads 和 Free monads 一樣適合構建 DSL。
考慮例如一種類型
data Lang r where
LReturn :: Var -> Lang Int
LPrint :: IntExpr -> Lang ()
LAssign :: Var -> IntExpr -> Lang ()
LRead :: Var -> Lang Int
這告訴我在Lang
中有幾個操作要執行: return x
print x
assign xy
read y
。
我們在這里使用 GADT,以便我們還可以指定各個操作將具有的輸出。 如果我們在 DSL 中編寫函數,這會非常方便,因為它們的輸出可以被 tpecheck。
添加一些方便的函數(可以直接導出):
lReturn :: Member Lang effs
=> Var -> Eff effs Int
lReturn = send . LReturn
lPrint :: Member Lang effs
=> IntExpr -> Eff effs ()
lPrint = send . LPrint
lAssign :: Member Lang effs
=> Var -> IntExpr -> Eff effs ()
lAssign v i = send $ LAssign v i
lRead :: Member Lang effs
=> Var -> Eff effs Int
lRead = send . LRead
(這已經是使用freer
編寫的)
現在我們可以像這樣使用它們:(假設 IntExpr 包含變量和整數)
someFunctionPrintingAnInt = do
lAssign (Var "a") (IE_Int 12)
lPrint (IE_Var $ Var "a")
這些函數現在使您能夠擁有可以以不同方式解釋的 DSL。 為此只需要一個具有特定類型的解釋器,用於effs
(這是 ~~ 更自由的 monad“實例”的類型級別列表)
所以freer
將freer monads 的想法打包成一個效果系統。
這個解釋器看起來像這樣:
runLangPure :: Eff '[Lang] Int -> Either () Int -- [StateMap]
runLangPure program = fst . fst $
run (runWriter (runState empty (runError (reinterpret3 go program))))
where
go :: Lang v -> Eff '[Error (), State StateMap, Writer [String]] v
go (LReturn var) = get >>= go (Eval stmt) >>= tell . []
go (LPrint expr) = do
store <- get
value <- evalM expr
tell [show value]
go (LAssign var expr) = do
value <- evalM expr
--modify state (change var)
go (LRead var) = do
strValue <- getLine
get >>= insert var (stringToInt strValue)
run...
部分指定了 monad 的初始“狀態”。 go
部分是解釋器本身,解釋不同的可能動作。
請注意,即使它們是不同 monad 的一部分,也可以在同一個 do 塊中使用get
和tell
函數,這使我們
我也想知道它們比免費的 monad 和經典的 mtl 堆棧有什么優勢。
該實現允許在不lift
情況下使用“monad stack”的不同部分的monadic action。
關於實施:
為了理解這一點,我們從高層次的抽象來看待它:我們的 DSL 的輔助功能被send
到Eff effs
,在那里需要Member Lang effs
。
所以Member
約束只是聲明Lang
在Member Lang effs
中的類型級列表effs
中的Member Lang effs
。 (基本上是類型級別的elem
)
Eff
monad 具有“詢問”monad 類型級別列表的Member
是否可以處理當前值的功能(請記住,操作只是隨后解釋的值)。 如果是,則執行他們的解釋,如果不是,則將問題交給列表中的下一個 monad。
當花一些時間在freer-simple
代碼庫中時,這變得更加直觀和易於理解。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.