Because there is no function “hoisting” in Clojure, I imagine in most cases the most important functions are in the bottom of the file (I assume no one will do
declare for everything). So my questions for you is that, when you open a Clojure code file, do you normally read from the bottom up? And is this bothering you?
If it’s bothering you, what do you do to mitigate this?
I think it’s bothering me a little bit because lately I have been frequently switching programming languages. I don’t have solution so I kinda just try to switch habit whenever I do Clojure things.
I’ve worked in a lot of languages and several required definition before use so, no, Clojure’s order does not bother me at all.
To be honest, I find it a bit weird to read code that refers to things that are defined later in the file!
I think most of the time I am arriving at code that needs to be read via
ag (that is, teleporting into the middle of something unfamiliar as the result of a go-to-definition like operation, or a search/grep). In that case I start reading where I land and then I expect additional context to be readily available either directly above where I’m reading, or accessible from another
M-. This also builds a stack that I can pop off of with
M-, so I do not need to remember from where I came.
If I am reading an entire namespace, and I’m not that familiar with it, I do think reading the file bottom up makes sense. But this is relatively rare, and often quickly leads to another sequence of
M-. anyway if anything interesting is happening.
Make sure you’re using a sufficiently powerful editor to have go-to-definition and find-usages. Reading code without those is pretty unpleasant (in any language).
In a namespace that is familiar then the single pass nature of Clojure is barely noticeable to myself. In unfamiliar code base I would read the development guide in the readme.md and start with the functions in the main namespace, usually jumping to the bottom if there is no other documentation.
Once a repl workflow is started, then it provides much greater flexibility with evaluation, so I can more readily navigate and try code in the Clojure parser (which is more efficient than the Clojure parser in my head)
The main namespace should help guide a human reader through the code within that namespace.
I use a header comment to introduce each namespace so the overall purpose is readily available.
I also use comments to divide a namespace into logical sections, guiding human readers through the namespace quickly.
Clojure editor’s can show and jump to function definitions and function call references. So once the initial start point is discerned further navigation a few key clicks away.
Without using the tools and techniques provided by a language the interaction will always be more challenging.
I would always treat each language with respect to its specific tools and techniques to get the most of that specific language.
Yes and no. Some narratives go bottom up (which then means to read from the start in the file), while some go top down (which then means to read from the end of the file). I often find the Common Lisp approach of whole-file compilation more flexible in structuring the code at top level.
Of course, if you just jump around between individual toplevel forms, it doesn’t matter at all. However, I believe that the idea of reading code as a whole is precious. Too often, we lose sight of the whole, and then optimize or abstract things that shouldn’t exist in the first place.
Try ClojureDart. It does not care about the order.
Otherwise, yep, I prefer to understand things top down, so the natural order is to look at the top level function, and if I feel I need to drill down into a subroutine I can scroll down. If I am using CL.
That said, I usually let the IDE go find a subroutine, so it does not matter much. And I have gotten used to starting on a CLJ source file by first scrolling to the end.
Not bothering me at all. As with others here, I often arrive in a namespace at the point of interest from some other namespace. And it feels natural that things below build on things above. Like with prose.
That’s one of my favorite features of Clojure. It’s so confusing in other languages where is what, because the order isn’t enforced, it tends to be all over the place.
Yes, I do read bottom up, well, when I care to look at the entry point first.
It’s a pretty simple flow, open file, go to the end. I’m not bothered by it at all, in fact, I love it, wish all languages were like that.
Yes, one learns to read all code bottom-up because one wants to have some idea of “what” and “why” before getting lost in the weeds of “how.”
Try reading code with a debugger, stepping through the test corpus. IMO, this is the best way to read code because you see what the code does in actual practice with actual run-time values for parameters and other inputs.
Check out “Literate Programming,” in which an author may present code in a narrative order, explaining first what code is doing and why, and later how code works. Literate Programming has never become mainstream because only obscure tools like org-babel support it. But Literate Programming is based on sound ideas.
The codebases that trip me up are the ones written in such a functional style that it’s hard to track down where anything actually happens (hint: it’s somewhere else )