不,你不能在多个文件之间分割一个模块,你当然不能在不同的位置定义一个函数。最接近这一点的是作为类型类一部分的函数,其实例定义在各种模块中。但这可能不是你想要的。
但是,可以编译相互递归的模块。从理论上讲,这应该可以正常工作(tm),但 GHC 需要一些跳圈才能做到这一点;有关详细信息,请参阅用户指南。如果您遇到循环模块导入错误,这应该可以让您使该版本正常工作。
没有“好”的方法来捕获不详尽的模式匹配错误并尝试其他方法。有多种不太好的方式,但你可能不想去那里。
如果您的目标是对具有大量案例的单一数据类型进行模式匹配,那么在不弄乱相互递归模块或类型类的情况下拆分事物的最直接方法是在其他模块中具有单独的函数,这些函数获取每个构造函数的内容作为直接参数,然后在导入其他模块并执行调度的模块中的所有情况下进行单个模式匹配。
假设您有一个Foo
带有 case A
、B
、 &c. 的类型,具有类似命名的模块。在“中央”模块中,您可以拥有:
doStuff (A x y) = A.doStuffA x y
doStuff (B z) = B.doStuffB z
...等等。
在某些情况下,甚至可以以类似的方式拆分整个数据类型,并为每个构造函数创建一个单独的类型,例如:data Foo = A FooA | B FooB | ...
. 当您有可能以多种方式相互递归的复杂数据类型时,这是最有用的,典型的例子是 AST。
好的,这是一种模拟你想要的东西而不做任何太粗略的方法。
首先,按照您理想的方式将您的功能拆分为不同的模块。然后进行以下更改:
从中央模块导入包含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 自动化所有这些废话,但这可能与手动操作一样麻烦。
就个人而言,我可能只是咬紧牙关,把它全部放在一个模块中。