几个模块的通用接口

时间:2012-04-28 12:00:36

标签: haskell

我想让用户首先使用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中的接口是否相同(编译器无法帮助),并且在开发过程中很难保持这种混乱。
是否有任何常用的习惯用来解决这类问题?

1 个答案:

答案 0 :(得分: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)
如果我没有弄错的话,

将允许您将DryRun模块实现替换为addTracing doNothing