Thanks for your opinions everybody!
Ketil Malde writes:
I guess you could sometimes have name clashes as well?
I was afraid about those for the longest time too, but in
practice name clashes curiously enough hardly ever occur --
in my experience. The problem only arises when you actually
_use_ a function that's ambiguous, just importing two
modules with a function called 'foo' in them is no problem.
And then you still have the option of using 'hide' or
'qualified' when importing the modules.
On those occasions, however, why not put the function
into a module, say Foo.Bar.Private and import it into
Foo.Bar from there?
So you don't want to automatically re-export imports, I
take it? :-)
No. ;-) Although I would like to have a shortcut for saying
(re-)export everything.
David Roundy writes:
[...] why not put the function into a module, say
Foo.Bar.Private and import it into Foo.Bar from
there?
Because a separate module is more work.
It sure is, but I don't think that it's more work or it's
less work is a good principle by which to make software
design decisions. If you follow this idea through, then you
could also argue that splitting a program into separate
functions is more work than writing one big, big 'main'
function for the task. And it sure is. Still enlightened
programmers do just that, because it often turns out that
doing so is _less work_ in the long run. I believe the same
applies to cleanly grouping functions into separate modules,
and a separation between public API and internal
implementation doesn't sound so unreasonable, IMHO.
Almost every module I write has private functions, and
I'd really not want to write twice as many modules.
Why do these functions need to be private?
In darcs, if someone needs to play with fire, he can do
it right there in the module itself, and export a new
interface function.
Not really, unless you decide to include the patches into
the main distribution. If you don't, someone who wants to
access the internals essentially has to create a fork of
your software, and that's something you really want to avoid
if you want to encourage re-use.
In the case of darcs, I'd say that the whole point of
using modules (besides making things faster to compile)
is to place these barriers, so that one can modify an
individual module without worrying about the rest of the
code, as long as one keeps the interface fixed.
I'm not sure whether I understand that. When modifying a
function 'foo', why do you have to worry _less_ about 'bar'
if it is in a separate module than you'd have to if it would
be in the same module?
There's also the ease of bug-writing issue. I think that
exported interfaces should be the sorts of functions
where the meaning of the function can be guessed from its
name and type.
Shouldn't _any_ function have those properties if at all
possible?
ajb writes:
Is there any reason why you would have a function in one
of your modules and _not_ export it?
Because that function is nobody else's business.
I'm sorry, but that's not really a convincing technical
argument, that's essentially because I want it so.
So while I think you've identified a real problem (the
modules that you want to use expose insufficient APIs), I
think your solution is wrong. The right solution is to
complain to the module writer, and ask them to export a
functionally complete API.
So my solution is wrong and your solution is right. ;-)
Having that out of the way, what are your reasons for this
opinion? (Other than that the art of programming says it
ought to be this way.)
The only reason I could think of is that a function is
considered to be internal [...]
Right. And I agree with David: This is reason enough.
How is an internal function any _more_ internal if you don't
export it? How is it less internal if you _do_ export it?
Why doesn't the approach
-- | /Attention:/ this function is internal and may change
-- at random without even so much as shrug.
foo = ...
suffice?
With my business hat on: Every time you expose something
for use, you must at the very least document it.
I'd recommend documenting _all_ functions I write, not just
the exported ones.
Taking this to an illogcial extreme, why don't we allow
pointer arithmetic in Haskell, but require people to
import Prelude.C first, so that people who enjoy
playing with fire can do it?
You mean Foreign.Ptr? Curiously enough, if Haskell
wouldn't support pointer arithmetic, the language would be
completely useless to me, so I for one don't think that's
taking things to the illogical extreme.
Iavor Diatchki writes:
[...] in practice this is likely to often lead to
recursive modules [...]
Why is that? My intuition would say that the exact opposite
is true: a more fine-grained set of modules is _less_ likely
to require recursive modules. But that's just intuition. Do
you have an concrete example which illustrates