Haskell 使用相同的源代码构建两个不同的模块
Building two distinct modules with the same source code with Haskell
我正在用 Haskell 编写一个模块,我想创建这个模块的两个版本:
- 一个 'basic' 一个(名为 MyLib):为了速度和 public 发布。
- 一个 'extended' 一个(名为 MyLibExt):供专家和私人使用。
为方便起见,我希望这两个模块(具有两个不同的名称)共享相同的源文件。这两个模块将具有相同的类型和相同的功能,但有一些差异('extended' 版本将依赖于 'basic' 版本)。
我的想法是这样的:
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编译指令可用于进行条件构建?
不能将两个模块放在同一个文件中。但是你可以通过一些重新导出来获得你想要的东西。
一个文件既有基本代码也有扩展代码(我把它缩短了一点):
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
您可以导出范围内的任何内容,包括您从其他模块导入的任何内容。事实上,如果你想从其他模块重新导出一些东西,你需要像这样明确定义一个导出列表,默认情况下它只会导出这个模块中定义的任何内容。
考虑抽象化。可能不值得,但有时隐藏着美丽和力量,只能用抽象来梳理。对于你的情况 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
然后很容易分离出到不同的文件中。但抽象化的优势远不止将事物分割成 public 和私人空间。如果您的代码有两个 "valid" 版本,那么这两个版本的含义是一致的。找到它们的共同点,看看您是否可以在不特别引用任何一个的情况下编写它们的共同点。然后仔细看——这些东西与你的两个例子还有什么共同点?你能用同样具有这些共同点的更简单的部分构建你的两个示例吗?在 Haskell 中,代码为您编程。
我正在用 Haskell 编写一个模块,我想创建这个模块的两个版本:
- 一个 'basic' 一个(名为 MyLib):为了速度和 public 发布。
- 一个 'extended' 一个(名为 MyLibExt):供专家和私人使用。
为方便起见,我希望这两个模块(具有两个不同的名称)共享相同的源文件。这两个模块将具有相同的类型和相同的功能,但有一些差异('extended' 版本将依赖于 'basic' 版本)。
我的想法是这样的:
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编译指令可用于进行条件构建?
不能将两个模块放在同一个文件中。但是你可以通过一些重新导出来获得你想要的东西。
一个文件既有基本代码也有扩展代码(我把它缩短了一点):
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
您可以导出范围内的任何内容,包括您从其他模块导入的任何内容。事实上,如果你想从其他模块重新导出一些东西,你需要像这样明确定义一个导出列表,默认情况下它只会导出这个模块中定义的任何内容。
考虑抽象化。可能不值得,但有时隐藏着美丽和力量,只能用抽象来梳理。对于你的情况 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
然后很容易分离出到不同的文件中。但抽象化的优势远不止将事物分割成 public 和私人空间。如果您的代码有两个 "valid" 版本,那么这两个版本的含义是一致的。找到它们的共同点,看看您是否可以在不特别引用任何一个的情况下编写它们的共同点。然后仔细看——这些东西与你的两个例子还有什么共同点?你能用同样具有这些共同点的更简单的部分构建你的两个示例吗?在 Haskell 中,代码为您编程。