簡體   English   中英

什么是更自由的 monad?

[英]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 塊中使用gettell函數,這使我們

我也想知道它們比免費的 monad 和經典的 mtl 堆棧有什么優勢。

該實現允許在不lift情況下使用“monad stack”的不同部分的monadic action。

關於實施:

為了理解這一點,我們從高層次的抽象來看待它:我們的 DSL 的輔助功能被sendEff effs ,在那里需要Member Lang effs

所以Member約束只是聲明LangMember Lang effs中的類型級列表effs中的Member Lang effs (基本上是類型級別的elem

Eff monad 具有“詢問”monad 類型級別列表的Member是否可以處理當前值的功能(請記住,操作只是隨后解釋的值)。 如果是,則執行他們的解釋,如果不是,則將問題交給列表中的下一個 monad。

當花一些時間在freer-simple代碼庫中時,這變得更加直觀和易於理解。

暫無
暫無

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

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