2012-04-28 79 views
3

我想让用户能够首先运行程序import DryRun,然后用import Do运行程序,如果他认为一切正确。
因此,有一个模块,它做实际工作:多个模块的通用接口

doThis ∷ SomeStack() 
doThis = actuallyDoThis 
... 
doThat ∷ SomeStack() 
doThat = actuallyDoThat 

和害羞的用户模块:

doThis ∷ SomeStack() 
doThis = liftIO $ putStrLn "DoThis" 
... 
doThat ∷ SomeStack() 
doThat = liftIO $ puStrlLn "DoThat" 

问题是我不能肯定,在DoDryRun接口是相同的(编译器不能提供帮助),而且这种混乱在开发过程中很难维护。
有没有什么常见的成语来解决这类问题?

+5

是否让相同类型类型的两个实现实例成为选项? – 2012-04-28 12:06:09

+0

@GregBacon那么,monad栈的两个副本肯定是一个选项,但如果有一些不涉及复制的东西,我会使用它。 – 2012-04-28 12:17:37

+0

在两个模块上运行haddock并比较生成的文档? – 2012-04-28 13:34:19

回答

2

您可以确定有问题的模块。也就是说,一个基础模块中定义的数据类型:

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模块实现,如果我没有记错。