haskell - 什么是更自由的单子(monad)?

标签 haskell functional-programming monads

这个词我听过几次,但我仍然不知道究竟什么是所谓的“Freer Monad”。这个名字让我想到了 Free Monads,但我看不出它们实际上是如何相关的。我在 hackage 上找到了一些库:http://hackage.haskell.org/package/freer ,但那里的例子对我没有多大帮助。
我根本不理解这个想法,因此我看不到它们有什么好的用例。我还想知道它们比免费的 monad 和经典的 mtl 堆栈有什么优势。

最佳答案

我知道这是一个旧线程,但我想我还是会回答它以防万一

what [...] is a so-called "Freer Monad"


根据原论文Freer Monads, More Extensible Effects “Freer Monad”本质上是一个 Free Monad,没有 Free Monad 的必要 Functor 约束。
自由单子(monad)基本上是单子(monad)结构的本质; “最小”的东西仍然是一个单子(monad)。一个很好的实用解释方法can be found in this article .这篇文章还展示了“正常”的自由 monad 需要一个 Functor 约束。
然而,在每个函数中添加仿函数约束通常是相当乏味的(有时实现起来可能很奇怪),事实证明,通过“移动仿函数功能”到 Impure 的参数。构造函数,以便实现方可以更改输出本身的类型(因此没有通用仿函数),可以摆脱这个约束。这是通过使用 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,对应于 Eff包装内freer-simple .

good usecases for them


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 x y read y .
我们在这里使用 GADT,以便我们还可以指定各个操作将具有的输出。如果我们在 DSL 中编写函数,这会非常方便,因为可以检查它们的输出。
添加一些便利功能(可以实际派生):
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采用更自由的单子(monad)的想法并将其打包到效果系统中。
这个解释器可能看起来像这样:
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部分是解释器本身,解释不同的可能 Action 。
请注意,可以使用函数 gettell在同一个 do block 中,即使它们是不同 monad 的一部分,这使我们

I also wonder what advantages do they provide over free monads and classic mtl stacks.


该实现允许在不使用 lift 的情况下使用“monad stack”不同部分的单子(monad)操作ing。
关于实现:
为了理解这一点,我们从高度抽象的角度来看待它:
我们DSL的辅助功能是sendEff effs需要Member Lang effs .
所以Member约束只是声明 Lang 的一种方式在类型级别列表中 effsMember Lang effs . (基本上类型级别 elem )Eff monad 具有“询问”Member 的功能。单子(monad)的类型级别列表的 s 是否可以处理当前值(请记住,操作只是随后解释的值)。如果是,则执行他们的解释,如果不是,则将问题移交给列表中的下一个 monad。
freer-simple 中花费一些时间后,这将变得更加直观和易于理解。代码库。

关于haskell - 什么是更自由的单子(monad)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57758951/

相关文章:

programming-languages - 响应式(Reactive)编程和函数式响应式(Reactive)编程之间的区别

haskell - 这个片段如何翻译成 Haskell?

haskell - GHC 8.6.5 中是否删除了在 do block 中使用 let 语句的功能?

date - 在本地时区获取 Haskell 中的日期

haskell - Setter 保留索引是什么意思?

haskell - 为什么尾递归模数可以优化?

haskell - 如何重构才不会卡在IO monad里?

shell - #/bin/sh 一行

haskell - Haskell 中的半素数测试

functional-programming - writer monad 和 list writer monad 有什么区别