这个词我听过几次,但我仍然不知道究竟什么是所谓的“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 。请注意,可以使用函数
get
和 tell
在同一个 do block 中,即使它们是不同 monad 的一部分,这使我们I also wonder what advantages do they provide over free monads and classic mtl stacks.
该实现允许在不使用
lift
的情况下使用“monad stack”不同部分的单子(monad)操作ing。关于实现:
为了理解这一点,我们从高度抽象的角度来看待它:
我们DSL的辅助功能是
send
至Eff effs
需要Member Lang effs
.所以
Member
约束只是声明 Lang
的一种方式在类型级别列表中 effs
在 Member Lang effs
. (基本上类型级别 elem
)Eff
monad 具有“询问”Member
的功能。单子(monad)的类型级别列表的 s 是否可以处理当前值(请记住,操作只是随后解释的值)。如果是,则执行他们的解释,如果不是,则将问题移交给列表中的下一个 monad。在
freer-simple
中花费一些时间后,这将变得更加直观和易于理解。代码库。
关于haskell - 什么是更自由的单子(monad)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57758951/