我在模块A
中有一个函数evalExpression::Exp -> Value
,它很大程度上依赖于参数Exp
的模式匹配.
文件已经变得足够大,需要更多的组织。我想将模块 A
拆分为模块 A.GHC.Num
、A.GHC.Types
等。
- 有没有办法在 GHC 中将一个模块内联到另一个模块中?我尝试这样做,但收到错误“模块导入形成一个循环”
或
- 有没有办法在两个不同的文件中编写相同的模块?
或
- 我可以定义一个函数
A.evalExp
来尝试(在 try 和 catch 的意义上)返回A.GHC.Num.evalExp
的值,并且如果它缓存一个错误(非详尽的模式匹配),尝试返回A.GHC.Types.evalExp
等等?
更新:
我尝试解决循环依赖性,但 GHC 不相信,它说“出现不明确”。
最佳答案
不,您不能在多个文件之间拆分模块,当然也不能在不同位置定义函数。与此最接近的是作为类型类一部分的函数,其实例在各个模块中定义。但这可能不是您想要的。
但是,编译相互递归模块是是可能的。理论上这应该可行(tm),但 GHC 需要一些跳圈才能做到这一点;请参阅the User's Guide了解详情。如果您遇到循环模块导入错误,这应该可以让您使该版本正常工作。
没有“好的”方法来捕获不详尽的模式匹配错误并尝试其他方法。有多种不太好的方法,但您可能不想去那里。
如果您的目标是在多种情况下对单一数据类型进行模式匹配,那么在不干扰相互递归模块或类型类的情况下进行拆分的最直接方法是在其他模块中使用单独的函数来获取内容每个构造函数作为直接参数,然后对模块中的所有情况进行单一模式匹配,导入其他构造函数并进行分派(dispatch)。
假设您有一个类型 Foo
,其中包含 A
、B
等情况,以及类似命名的模块。在“中央”模块中,您可以:
doStuff (A x y) = A.doStuffA x y
doStuff (B z) = B.doStuffB z
...等等。
在某些情况下,以类似的方式拆分整个数据类型并为每个构造函数创建单独的类型甚至是有意义的,例如:data Foo = A FooA | B FooB | ...
。当您拥有可能以多种方式相互递归的复杂数据类型时,这是最有用的,典型的例子是 AST。
好的,这是一种模拟您想要的内容的方法,而无需做任何过于粗略的事情。
首先,按照您理想的方式将您的功能分成不同的模块。然后进行以下更改:
将结果类型更改为使用
Maybe
,将结果包装在Just
中,并添加一个生成Nothing
的包罗万象的默认情况.添加额外参数
r
,并将对evalExp
的任何递归调用替换为r
。
从中央模块导入包含 evalExp
案例的每个模块。如有必要,请使用合格的导入以避免歧义。定义每个 eval 函数的列表(它们应该具有相同的类型),然后将“真实”evalExp
定义为如下所示:
expCases = [A.GHC.Num.evalExp, A.GHC.Types.evalExpr {- etc... -} ]
evalExpCases exp = mapMaybe (\eval -> eval evalExp exp) expCases
evalExp exp = case evalExpCases exp of
(r:_) -> -- use the first result
[] -> -- no cases matched
本质上,这是使用Maybe
显式地指示不详尽的模式,并用fix
样式的构造替换直接递归,其中组合的递归函数被传递到每个(单独的非递归)案例集。
这很尴尬,但我不确定是否真的有更好的方法。可能有一种方法可以使用 Template Haskell 来自动化所有这些废话,但这可能和手动操作一样麻烦。
就我个人而言,我可能会咬紧牙关,将所有内容都留在一个模块中。
关于haskell - 在两个不同的模块中定义函数或解决方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14878737/