[Haskell-cafe] Re: Unexported functions are evil

Peter Simons simons at cryp.to
Tue May 17 06:09:35 EDT 2005


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 this point?

Peter



More information about the Haskell-Cafe mailing list