To clarify, an explicit export:
module X (…) where …
An explicit import:
import X (…)
I would like to question the desirability of explicit exports and local imports in a production application.
I want to make two cases:
- That explicit exports are never necessary. Moreover, that explicit exports are harmful as they make checking of non-exported definitions impossible.
- That explicit imports are not necessary when importing a module from the same package.
By default, I make an easy claim that none are desirable since they incur mindless typing effort and thereby unfairly tax the programmer. Let us then consider the justifications for their use despite that.
Explicit exports.
When there are no explicit exports, everything is exported from a module. Recall a case when this is disadvantageous: abstract types. The values of abstract types must only be obtained via smart constructors. So their definitions should not be made available. Explicit exports may provide that.
However, there is another practice: putting dangerous definitions of the supposedly abstract type X
into a module X.Internal
, then importing them to module X
and defining smart constructors there. By default, a module only exports the definitions defined in itself — there are no re-exports. So, a user importing X
cannot invoke unsafe constructors from X.Internal
, and they know better than to import the dangerous module directly.
In the former case above, the internal definitions remain out of reach for a test suite. In the latter case, they may be freely checked as needed.
Explicit imports.
Recall the scenario in which explicit imports are useful.
- A module
"p" X
from packagep
imports a module"q" Y
from packageq v0.0.0
. - The package
q
bumps the third version number and exports a definitionY.d
which name coincides with an already defined definitionX.d
.
What happens?
-
If imports are explicit, nothing happens. The maintainers of
p
are safe if they set a restriction as weak asq ^>= 0.0
. -
If imports are implicit, there would be a clash of names if
p
is built againstq v0.0.1
, but the build would pass if it is built against a less recent versionq v0.0.0.1
.The maintainers of
p
might not even notice that the package does not build in some cases. When they do, they would have to set a restrictionq ^>= 0.0.0
But surely there is only one version to build against if the two modules reside in the same package. So the overlapping names will necessarily result in an error that would immediately be rectified. It is no different from any other compile time error.