【问题标题】:Building two distinct modules with the same source code with Haskell使用 Haskell 构建两个具有相同源代码的不同模块
【发布时间】:2016-04-20 20:01:16
【问题描述】:

我正在用 Haskell 编写一个模块,我想创建这个模块的两个版本:

  • “基本”版本(名为 MyLib):用于速度和公开发布。
  • 一个“扩展”的(名为 MyLibExt):供专家和私人使用。

为方便起见,我希望这两个模块(具有两个不同的名称)共享相同的源文件。这两个模块将具有相同的类型和相同的功能,但存在一些差异(“扩展”版本将依赖于“基本”版本)。

我的想法是有这样的东西:

module MyLib where  -- for 'basic' version

module MyLibExt where  -- for 'extended' version

MyType = 
         TypeA            -- for 'basic' version
       | TypeB            -- for 'basic' version
       | TypeC            -- for 'basic' version

       | TypeExtendedD    -- for 'extended' version
       | TypeExtendedE    -- for 'extended' version


MyFunction TypeA         = ...  -- for 'basic' version
MyFunction TypeB         = ...  -- for 'basic' version

MyFunction TypeExtendedD = ...  -- for 'extended' version

并使用为 GHC/Cabal 提供的一些编译指令构建这两个模块。

有没有可能用 Haskell 做这样的事情?

哪些 GHC/Cabal 编译指令可用于条件构建?

【问题讨论】:

  • 我知道这很有用,但不认为有一个标准的方法来完成它。最好将MyLib 定义为代理模块,它只是重新导出MyLibExt 的一些内容。这当然是许多现有图书馆的做法。
  • 只需将所有内容放入扩展版本MyLib.Internal,然后将其导入MyLib,但仅重新导出基本内容。这是一种很常见的模式。

标签: haskell ghc cabal


【解决方案1】:

您不能将两个模块放在同一个文件中。但是你可以通过一些重新导出来获得你想要的东西。

一个文件将同时包含基本代码和扩展代码(我将其缩短了一点):

module MyLibExt where

MyType = TypeA | TypeB | TypeC | TypeExtendedD | TypeExtendedE

myFunction TypeA = ...
myFunction TypeB = ...
myFunction TypeExtendedD = ...

那么另一个文件将是基本文件:

module MyLib (MyType (TypeA, TypeB, TypeC), myFunction)

import MyLibExt

这样,如果有人只导入MyLib,他们只能访问基本构造函数,而不能访问扩展构造函数。 myFunction 仍然可以像在 MyLibExt 中一样对 TypeExtendedD 值起作用,但是由于我们无法仅使用 MyLib 创建这些值,所以没关系。

更一般地,当您定义您的模块时,您可以说出您想要导出的确切内容。以下是一些基本示例:

module Example (
  exampleFunction, -- simply export this function.

  ExampleType1 (), -- export the type, but no constructors.
  ExampleType2 (ExampleConstructor1, ExampleConstructor2), -- export the given type and its given constructors. You can't export constructors like functions, you have to do it like this).
  ExampleType3 (..), -- export given type and all its constructors.

  ExampleClass1, -- export type class, but no functions that belong to it.
  ExampleClass2 (exampleFunction2, exampleFunction3), -- export type class and the given functions belonging to it. You can also export these functions as though they were normal functions, like in the first of the examples.
  ExampleClass3 (..), -- export type class and all its functions.

  module Module1, -- re-export anything that is imported from Module1.
  ) where

您可以导出范围内的任何内容,包括从其他模块导入的任何内容。事实上,如果你想从其他模块重新导出一些东西,你需要像这样显式定义一个导出列表,默认情况下它只会导出这个模块中定义的任何东西。

【讨论】:

    【解决方案2】:

    考虑抽象。或许不值得,但有时隐藏着美丽和力量,只能用抽象来梳理。对于您的情况MyType 和MyFunction,可能是这样的(只是一个示例,根据您的详细信息,它可能看起来非常不同):

    class MyFunction a where
        myFunction :: a -> Int
    
    data MyType = TypeA | TypeB | TypeC
    
    instance MyFunction MyType where
        myFunction TypeA = 0
        myFunction TypeB = 1
        myFunction TypeC = 2
    
    data MyTypeExt = Orig MyType | TypeExtendedD | TypeExtendedE
    
    instance MyFunction MyTypeExt where
        myFunction (Orig x) = myFunction x
        myFunction TypeExtendedD = myFunction TypeC + 1
        myFunction TypeExtendedE = myFunction TypeC + 2
    

    那将是第一级。您可以更进一步,使扩展参数化:

    data MyTypeExt a = Orig a | TypeExtendedD | TypeExtendedE
    
    instance (MyFunction a) => MyFunction (MyTypeExt a) where
        ...  -- defined so that it works for any myFunction-able Orig
    

    然后很容易分成不同的文件。但抽象的优势远不止将事物分成公共和私人空间。如果您的代码有两个“有效”版本,那么这两个版本各自具有一致的含义。找出它们的共同点,看看你是否可以在不特别引用任何一个的情况下编写它们的共同点。然后仔细看——这些东西和你的两个例子还有什么共同点?你能用同样具有这些共同点的更简单的部分来构建你的两个例子吗?在 Haskell 中,为您编写代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-18
      • 2012-12-26
      • 1970-01-01
      • 2016-03-24
      • 1970-01-01
      相关资源
      最近更新 更多