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
.Internalmodules solve? - Are there other preferable ways to workaround those problems?
- Which definitions should be moved to those
.Internalmodules? - What’s the current recommended practice with respect to organizing libraries with the help of such
.Internalmodules?
Internalmodules are generally modules that expose the internals of a package, that break package encapsulation.To take
ByteStringas an example: When you normally useByteStrings, they are used as opaque data types; aByteStringvalue is atomic, and its representation is uninteresting. All of the functions inData.ByteStringtake values ofByteString, and never rawPtr CChars or something.This is a good thing; it means that the
ByteStringauthors managed to make the representation abstract enough that all the details about theByteStringcan be hidden completely from the user. Such a design leads to encapsulation of functionality.The
Internalmodules 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
BitStringdata type, and you want users to be able to convert aByteStringinto aBitStringwithout copying any memory. In order to do this, you can’t use opaqueByteStrings, because that doesn’t give you access to the memory that represents theByteString. You need access to the raw memory pointer to the byte data. This is what theInternalmodule forByteStrings provides.You should then make your
BitStringdata type encapsulated as well, thus widening the encapsulation without breaking it. You are then free to provide your ownBitString.Internalmodule, exposing the innards of your data type, for users that might want to inspect its representation in turn.If someone does not provide an
Internalmodule (or similar), you can’t gain access to the module’s internal representation, and the user writing e.g.BitStringis forced to (ab)use things likeunsafeCoerceto cast memory pointers, and things get ugly.The definitions that should be put in an
Internalmodule are the actual data declarations for your data types: