您可以确定有问题的模块。也就是说,一个基础模块中定义的数据类型:
module Base where
data MyModule = MyModule {
doThis_ :: SomeStack(),
doThat_ :: SomeStack()
}
你需要每个模块出口(的原因下划线将很快变得明显)的东西。
然后,你可以在每个模块的定义值这个类型,例如:
module DryRun(moduleImpl) where
import Base
moduleImpl :: MyModule
moduleImpl = MyModule doThis doThat
-- doThis and doThat defined as above, with liftIO . putStrLn
module Do(moduleImpl) where
import Base
moduleImpl :: MyModule
moduleImpl = MyModule doThis doThat
-- doThis and doThat defined as above, where the real work gets done
我不使用记录语法,以确保构建MyModule
值,如果MyModule
变化,类型检查器在大多数情况下开始抱怨。
在客户端模块,你可以做
module Client where
import DryRun
-- import Do -- uncomment as needed
doThis = doThis_ moduleImpl
doThat = doThat_ moduleImpl
-- do whatever you want here
现在你知道,同样的操作由两个模块输出。这确实是单调乏味的,但是由于Haskell没有一流的模块,所以你总是需要解决模块系统的限制。好处是您只需编写一次Base和Client模块,并且您可以开始定义在MyModule
值上运行的组合器。例如。
doNothing = MyModule (return()) (return())
addTracing impl = MyModule ((liftIO $ putStrLn "DoThis") >> doThis_ impl)
((liftIO $ putStrLn "DoThat") >> doThat_ impl)
将允许您通过addTracing doNothing
更换DryRun
模块实现,如果我没有记错。
是否让相同类型类型的两个实现实例成为选项? – 2012-04-28 12:06:09
@GregBacon那么,monad栈的两个副本肯定是一个选项,但如果有一些不涉及复制的东西,我会使用它。 – 2012-04-28 12:17:37
在两个模块上运行haddock并比较生成的文档? – 2012-04-28 13:34:19