2

evalExpression :: Exp -> Value在一个模块A中有一个函数,它严重依赖于参数的模式匹配Exp

该文件已变得足够大,需要更多的组织。我想将模块拆分A为 modules A.GHC.Num,A.GHC.Types等。

  • 有没有办法在 GHC 中将一个模块内联到另一个模块中?我试过这样做,但我收到错误“模块导入形成一个循环”

或者

  • 有没有办法在两个不同的文件中编写相同的模块?

或者

  • 我可以定义一个函数A.evalExp来尝试(在 try 和 catch 的意义上)返回的值,A.GHC.Num.evalExp如果它缓存一个错误(非详尽的模式匹配),尝试返回 A.GHC.Types.evalExp,等等?

更新

我尝试解决循环依赖,但 GHC 不相信,它说“模棱两可的发生”。

4

1 回答 1

5

不,你不能在多个文件之间分割一个模块,你当然不能在不同的位置定义一个函数。最接近这一点的是作为类型类一部分的函数,其实例定义在各种模块中。但这可能不是你想要的。

但是,可以编译相互递归的模块。从理论上讲,这应该可以正常工作(tm),但 GHC 需要一些跳圈才能做到这一点;有关详细信息,请参阅用户指南。如果您遇到循环模块导入错误,这应该可以让您使该版本正常工作。

没有“好”的方法来捕获不详尽的模式匹配错误并尝试其他方法。有多种不太好的方式,但你可能不想去那里。

如果您的目标是对具有大量案例的单一数据类型进行模式匹配,那么在不弄乱相互递归模块或类型类的情况下拆分事物的最直接方法是在其他模块中具有单独的函数,这些函数获取每个构造函数的内容作为直接参数,然后在导入其他模块并执行调度的模块中的所有情况下进行单个模式匹配。

假设您有一个Foo带有 case AB、 &c. 的类型,具有类似命名的模块。在“中央”模块中,您可以拥有:

doStuff (A x y) = A.doStuffA x y
doStuff (B z) = B.doStuffB z

...等等。

在某些情况下,甚至可以以类似的方式拆分整个数据类型,并为每个构造函数创建一个单独的类型,例如:data Foo = A FooA | B FooB | .... 当您有可能以多种方式相互递归的复杂数据类型时,这是最有用的,典型的例子是 AST。


好的,这是一种模拟你想要的东西而不做任何太粗略的方法。

首先,按照您理想的方式将您的功能拆分为不同的模块。然后进行以下更改:

  • 将结果类型更改为 use Maybe,将结果包装在Just其中并添加一个产生Nothing.

  • 添加一个额外的参数r,并将任何递归调用替换evalExpr

从中央模块导入包含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 自动化所有这些废话,但这可能与手动操作一样麻烦。

就个人而言,我可能只是咬紧牙关,把它全部放在一个模块中。

于 2013-02-14T16:02:08.513 回答