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)))