不,你不能在多个文件之间分割一个模块,而且你当然不能在不同的位置定义一个函数。最接近这个的是一个函数,它是类型类的一部分,实例在各个模块中定义。但这可能不是你想要的。
但是,它是可能编译相互递归模块。理论上这应该是Just Work(tm),但是GHC需要一些箍跳才能做到;详情请参阅the User's Guide。如果您正在获取循环模块导入错误,则应该让您可以使用该版本。
有没有“好”的方式来捕捉一个无尽的模式匹配错误,并尝试别的。有很多不太好的方法,但你可能不想去那里。
如果你的目标是对具有大量情况的单一数据类型进行模式匹配,那么最直接的方式就是将相互分离的模块或类型分解出来,而不是混淆相互递归的模块或类型类,将每个构造函数的内容作为直接参数,然后在导入其他模块的所有情况下进行单个模式匹配并执行分派。
假设您有一个类型Foo
与案件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自动化所有这些垃圾,但这可能与手动操作一样麻烦。
就我个人而言,我可能只是咬紧牙关,把它留在一个模块中。
我加了标签haskell,但我不确定问题属于那里,因为问题是GHC特定的。如有必要,请删除。 – 2013-02-14 15:54:47
关于第一点的一个显而易见的问题:你确定你无法避免循环进口吗?你能以某种方式拆分'A'以消除循环进口的需要吗? – gspr 2013-02-14 16:01:33
对于大多数实际用途,Haskell一般也可能是GHC特定的。不要担心。它是遥遥领先的最广泛使用的编译器,大多数时候人们说“Haskell”时,他们真正的意思是“GHC对报告的解释以及一些未指定的GHC扩展”。 – 2013-02-14 16:04:05