Haskell 如何将CheckingFuelMonad与Hoop中的状态monad结合起来?
我正在使用库,并希望在重写时携带一些状态。重写函数对于使用的单子是多态的,但是我不知道如何将Haskell 如何将CheckingFuelMonad与Hoop中的状态monad结合起来?,haskell,monads,state-monad,hoopl,Haskell,Monads,State Monad,Hoopl,我正在使用库,并希望在重写时携带一些状态。重写函数对于使用的单子是多态的,但是我不知道如何将状态单子与库的燃料单子组合 下面是一个简单的例子MyMonad是Hoop的CheckingFuelMonad和带有标志的状态的monad的同义词Stmt只是我的中间语言的一个占位符,并不重要 {-# LANGUAGE GADTs, RankNTypes #-} import Compiler.Hoopl import Control.Monad.State type MyMonad = Checkin
状态
单子与库的燃料
单子组合
下面是一个简单的例子MyMonad
是Hoop的CheckingFuelMonad
和带有标志的状态的monad的同义词Stmt
只是我的中间语言的一个占位符,并不重要
{-# LANGUAGE GADTs, RankNTypes #-}
import Compiler.Hoopl
import Control.Monad.State
type MyMonad = CheckingFuelMonad (State Bool)
data Stmt e x where
Bind :: () -> Stmt O O
rewriter :: forall e x. Stmt e x -> Fact x () -> MyMonad (Maybe (Graph Stmt e x))
rewriter (Bind ()) () = return $ do
f <- get
if f
then return $ Just emptyGraph
else return Nothing
我想做的事可能吗?如何正确编写重写函数?浏览hoopl代码可以发现CheckingFuelMonad不是MonadTrans的实例,您不能将其作为MonadTrans的实例,因为它的构造函数没有导出。但是,您可以在CheckingFuelMonad周围包装一个StateT,如下所示:
{-# LANGUAGE GADTs, RankNTypes #-}
import Compiler.Hoopl
import Control.Monad.State
type MyMonad = StateT Bool SimpleFuelMonad
data Stmt e x where
Bind :: () -> Stmt O O
rewriter :: forall e x. Stmt e x -> Fact x () -> MyMonad (Maybe (Graph Stmt e x))
rewriter (Bind ()) () = do
f <- get
if f
then return $ Just emptyGraph
else return Nothing
{-#语言GADTs,RankNTypes}
导入编译器.hoop
进口控制单体状态
类型MyMonad=StateT Bool SimpleFuelMonad
数据Stmt e x,其中
绑定::()->stmto
重写器::对于所有e x。stmtex->Fact x()->MyMonad(可能(图stmtex))
重写器(Bind())()=do
好吧,你当前错误的直接原因很简单。如果f
为真,那么最后的表达式是什么?如果我们这样做:
rewriter :: forall e x. Stmt e x -> Fact x () -> MyMonad (Maybe (Graph Stmt e x))
rewriter (Bind ()) () = return $ do
f <- get
if f
then return $ Just emptyGraph
else return Nothing
…简化为:
rewriter :: forall e x. Stmt e x -> Fact x () -> MyMonad (Maybe (Graph Stmt e x))
rewriter (Bind ()) () = return $ return $ Just emptyGraph
return$return$Just emptyGraph
的类型是什么
(Monad m1, Monad m2, GraphRep g) => m1 (m2 (Maybe (g n O O)))
换句话说,您在那里有一个额外的返回(Monad m)=>CheckingFuelMonad m
本身就是一个Monad
,尽管CheckingFuelMonad
似乎没有被定义为Monad转换器,所以你只有一个Monad层可以返回。有趣的是,如果你看看CheckingFuelMonad
的实现,这只是StateT燃料本身。这有助于我走得更远一点!不幸的是,MyMonad
必须是FuelMonad
的一个实例,而且我认为我不能将其作为一个实例,因为类成员没有导出。但是,我可以通过mkBRewrite(\s f->evalStateT(rewriter s f)False)
逃脱。不幸的是,我认为这意味着状态将不会在单个语句中保留-我们拭目以待@贾斯汀·贝利:从FuelMonad
看,所有东西都像是出口给我的。另外,我非常确定顺序与相邻的StateT
层无关,因此sclv
的版本应该相当于将State
放在SimpleFuelMonad
中。啊,没关系,我现在明白了——只有FuelMonad
类从编译器重新导出。哦,好吧。我认为这样做的原因是为了防止客户端代码以无效方式弄脏剩余的燃油,但我不明白为什么lift getFuel
和lift setFuel
会有问题。我想我可以侵入图书馆让你去做。Meh.@sclv:嗯,我想Hoopl的全部目的是限制你能做的事情,只有那些可以有效优化的东西才能被写出来,所以它是有意义的。我认为@Justin Bailey试图做的事情是完全安全的,只是没有在图书馆中说明,但是。。。不是100%确定,因为我不太熟悉Hoopl的内部结构。@Justin Bailey:所以我说“直接原因”。在do
块之外删除return
只会将其简化为实际问题,这正是sclv
所讨论的。我非常不相信这种重写是正确的。这是一件非常狡猾的事情。
rewriter :: forall e x. Stmt e x -> Fact x () -> MyMonad (Maybe (Graph Stmt e x))
rewriter (Bind ()) () = return $ return $ Just emptyGraph
(Monad m1, Monad m2, GraphRep g) => m1 (m2 (Maybe (g n O O)))