Haskell 多个模块的通用接口
我想让用户能够首先使用Haskell 多个模块的通用接口,haskell,Haskell,我想让用户能够首先使用import-DryRun运行程序,然后使用import-Do运行程序,如果他认为一切都是正确的。 因此,有一个模块进行实际工作: doThis ∷ SomeStack () doThis = actuallyDoThis ... doThat ∷ SomeStack () doThat = actuallyDoThat 和一个供害羞用户使用的模块: doThis ∷ SomeStack () doThis = liftIO $ putStrLn "DoThis" ...
import-DryRun
运行程序,然后使用import-Do
运行程序,如果他认为一切都是正确的。因此,有一个模块进行实际工作:
doThis ∷ SomeStack ()
doThis = actuallyDoThis
...
doThat ∷ SomeStack ()
doThat = actuallyDoThat
和一个供害羞用户使用的模块:
doThis ∷ SomeStack ()
doThis = liftIO $ putStrLn "DoThis"
...
doThat ∷ SomeStack ()
doThat = liftIO $ puStrlLn "DoThat"
问题是我无法确定Do
和DryRun
中的接口是否相同(编译器无法帮助),并且在开发过程中很难维护这种混乱状态。有没有解决这类问题的常用习惯用法?您可以具体化有问题的模块。也就是说,在基本模块中定义一个数据类型:
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没有一流的模块,因此您必须始终克服模块系统的限制。好的是,只需编写一次基本模块和客户机模块,就可以开始定义对MyModule
值进行操作的组合器。例如
doNothing = MyModule (return ()) (return ())
addTracing impl = MyModule ((liftIO $ putStrLn "DoThis") >> doThis_ impl)
((liftIO $ putStrLn "DoThat") >> doThat_ impl)
如果我没有弄错的话,将允许您用添加跟踪操作来替换
DryRun
模块实现。是否将同一类型类的两个实现实例作为选项?@GregBacon,两个monad堆栈的副本当然是一个选项,但如果有不涉及复制的内容,我会使用它。在两个模块上运行haddock并比较生成的文档。SomeStack
是否也会因是否包含Do
或DryRun
模块而有所不同,或者这两个模块的类型相同?