Haskell -- 双重人格的IO / ST单子?

18

我有一些使用ST单子进行评估的代码。我喜欢不在每个地方都放置IO,因为runST方法生成纯结果,并表明这种结果是安全的(与unsafePerformIO相比)。但是,随着我的一些代码变得越来越长,我确实想要添加调试打印语句。

是否有任何类提供双重性格的单子 [或类型类机制],可以根据其类型或“isDebug”标志而变成 ST 或 IO?我记得 SPJ 在他的“Fun with Type Functions”论文中介绍了一个“Mutation”类,它使用关联类型将 IO 与 IORef 相关联并将 ST 与 STRef 相关联。是否存在某个包中?

编辑/解决方案

非常感谢[C.A.McCann],使用该解决方案,我能够引入一个额外的类,用于支持pdebug函数的单子。ST单子将忽略这些调用,而IO将运行putStrLn。

class DebugMonad m where
    pdebug :: String -> m ()

instance DebugMonad (ST s) where
    pdebug _ = return ()

instance DebugMonad IO where
    pdebug = putStrLn

test initV = do
    v <- newRef initV
    modifyRef v (+1)
    pdebug "debug"
    readRef v
testR v = runST $ test v

这在ghci中有一个非常幸运的后果。由于默认情况下,它期望表达式是IO类型,因此运行类似“test 3”的东西将导致运行IO单子,因此您可以轻松调试它,然后在实际想要运行它时使用类似“testR”的东西来调用它。

2个回答

17
如果你想要一个统一的接口来处理 IORefSTRef,那么你是否考虑过使用 stateref?它提供了用于“可变数据引用”的类型类,包括可读、可写等不同的类别,并支持 IORefSTRef 的实例化,同时还支持 TVarMVarForeignPtr 等。

10

1
我目前正在使用它。但是,它也有其弱点,例如跟踪结果只会打印一次:在变量具有值(例如ST计算的结果)之后,它不会重新计算。例如:我在ghci中键入seq (somecomp somedata) ();第一次它会打印调试信息和错误消息,第二次只有错误消息。当然,:r可以解决问题,但我更希望它有更好的语义(即Debug.Trace有点像一个hack)。 - gatoatigrado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接