问题描述
我在上看到了一些包含<$的模块名称的软件包c $ c> .Internal 作为它们的姓氏组件(例如内部模块通常是公开内部的模块打包包封装。
以 ByteString 为例:您通常使用 ByteString s,它们用作不透明的数据类型;一个 ByteString 值是原子的,它的表示是无趣的。 Data.ByteString 中的所有函数都取值为 ByteString ,并且永远不会生成 Ptr CChar s或其他东西。
这是一件好事;这意味着 ByteString 作者设法使表示足够抽象,以使有关 ByteString 的所有细节都可以完全隐藏来自用户。这样的设计可以实现功能的封装。
Internal 模块适用于人员它希望与封装概念的内部工作,以扩大封装。
例如,您可能想要创建一个新的 BitString 数据类型,并且您希望用户能够将 ByteString 转换为 BitString 没有复制任何内存。为了做到这一点,你不能使用不透明的 ByteString s,因为这不会让你访问表示 ByteString 。您需要访问指向字节数据的原始内存指针。这就是为 ByteString s提供的 Internal 模块。
<然后你应该把你的 BitString 数据类型封装起来,从而扩大封装的范围而不会破坏封装。然后,您可以自由提供您自己的 BitString.Internal 模块,以暴露您的数据类型的内部空间,以供可能想依次检查其表示的用户使用。
如果有人不提供内部模块(或类似),则无法访问模块的内部表示,用户编写例如 BitString 被迫(ab)使用像 unsafeCoerce 之类的东西来施放内存指针,事情变得很难看。
应放入 Internal 模块中的定义是数据类型的实际数据声明:
模块Bla.Internal其中
数据Bla = Blu Int | Bli字符串
- ...
模块Bla(Bla,makeBla)其中 - 仅导出Bla类型,不是构造函数
import Bla.Internal
makeBla :: String - > Bla - 一些函数只处理不透明类型
makeBla = undefined
I've seen a couple of package on hackage which contain module names with .Internal as their last name component (e.g. Data.ByteString.Internal)
Those modules are usually not properly browsable (but they may show up nevertheless) in Haddock and should not be used by client code, but contain definitions which are either re-exported from exposed modules or just used internally.
Now my question(s) to this library organization pattern are:
- What problem(s) do those .Internal modules solve?
- Are there other preferable ways to workaround those problems?
- Which definitions should be moved to those .Internal modules?
- What's the current recommended practice with respect to organizing libraries with the help of such .Internal modules?
Internal modules are generally modules that expose the internals of a package, that break package encapsulation.
To take ByteString as an example: When you normally use ByteStrings, they are used as opaque data types; a ByteString value is atomic, and its representation is uninteresting. All of the functions in Data.ByteString take values of ByteString, and never raw Ptr CChars or something.
This is a good thing; it means that the ByteString authors managed to make the representation abstract enough that all the details about the ByteString can be hidden completely from the user. Such a design leads to encapsulation of functionality.
The Internal modules are for people that wish to work with the internals of an encapsulated concept, to widen the encapsulation.
For example, you might want to make a new BitString data type, and you want users to be able to convert a ByteString into a BitString without copying any memory. In order to do this, you can't use opaque ByteStrings, because that doesn't give you access to the memory that represents the ByteString. You need access to the raw memory pointer to the byte data. This is what the Internal module for ByteStrings provides.
You should then make your BitString data type encapsulated as well, thus widening the encapsulation without breaking it. You are then free to provide your own BitString.Internal module, exposing the innards of your data type, for users that might want to inspect its representation in turn.
If someone does not provide an Internal module (or similar), you can't gain access to the module's internal representation, and the user writing e.g. BitString is forced to (ab)use things like unsafeCoerce to cast memory pointers, and things get ugly.
The definitions that should be put in an Internal module are the actual data declarations for your data types:
module Bla.Internal where data Bla = Blu Int | Bli String -- ... module Bla (Bla, makeBla) where -- ONLY export the Bla type, not the constructors import Bla.Internal makeBla :: String -> Bla -- Some function only dealing with the opaque type makeBla = undefined
这篇关于如何,何时以及何时使用“。内部”模块模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!