On 2015-11-12, Nick Bowler <
nbo...@draconx.ca> wrote:
> On Wed, 11 Nov 2015 16:12:32 +0000, Kaz Kylheku wrote:
>> On 2015-11-11,
martin...@yahoo.co.uk <
martin...@yahoo.co.uk> wrote:
>>> If I understand the OP correctly: No, that would not be legal.
>>>
>>> What would be legal, is call somefn at one point in the compilation
>>> unit, and then at some later point *in the same compilation unit*
>>> define somefn.
>>>
>>> That would allow people to write the external functions first,
>>> calling the local static functions which are defined lower down the
>>> file.
>>
>> Anything which encourages this awful style of arranging a program, sets
>> me dead against itself as a matter of a knee-jerk reflex. :)
>
> It is often quite natural to read and write programs in this way
> (broad strokes first, followed by details), except in C where it is
> very awkward since it requires rendundant declarations. Many other
> languages do not have this limitation.
You can put the broad strokes at the bottom, followed by details toward
the top.
I follow the low level first, higher levels later even in languages
where it is not required.
This is because
1) There *should* be an order among the functions, rather than chaos.
2) This order should proceed from their call dependencies
(i.e. it should be one of the possible topological sorts of their
dependency graph.)
So this leaves only two possible (kinds of) orders: bottom to top, or top to
bottom. It rules out scatterbrained orders where a given function definition is
embedded in the middle of its uses (except in cases of recursion).
The low to high order is better for a thorough understanding of a module
by a human reading the file in the natural order. The lower levels constitute a
language/vocabulary in terms of which the higher levels are expressed.
In this order, you do not have to leave holes in your understanding
which are filled later. You only have to go back if you forgot something.
Without the vocabulary of the lower level material, the high level
routines might not make any sense at all. "Oh, we allocate a two instances
of a splat based on the arguments, and then apply the blorch function
to them. Great, WTF does that mean?" I have to stash a meaningless piece
of syntax into my brain, in which "splat" and "blorch" are just meaningless
meta-syntactic identifiers to be filled later.
This kind of suspense is great in mystery novels, not when you're
reading code.
Here is the crux of it: bottom-to-top lets your brain the forms being defined
as immutable. Each form you see is complete and stays that way; the others
build on what was seen before without altering it.
Top-to-bottom requires ugly mutation: you form some incomplete representation
of a form in your mind, and then you have to go back and update it.
Immutability beats mutation, at least whenever both are equally viable.
Extending your understanding in a purely additive way, without having to refute
or revise prior knowledge, is ideal: constructing new objects in your "mind's
heap" which reference existing ones without alteration.
Thus when I'm writing Lisp code, or functions in a shell ~/.profile file or
what have you, I still put the lower-level function ones first. Same thing in
a C module in which I could use any order, because almost all the functions are
declared in a header.
The only problem is that the order sometimes decays when it is not enforced.
Lisp compilers suppress their "undefined function" warning diagnostic until
they process a translation unit (which can be a single source file, or
multiple, thanks to the with-translation-unit macro). It's actually good
to have such a warning which is immediately issues, and not deferred
until the end, so you can detect that your source is ill-ordered
for maintainability and readability.