Python
On Code Ordering
Posted by Alyssa Riceman on
When I first learned to program, it was in Python. Python has a very simple principle for how to order your code: you put the dependencies before the things which depend on them. You can’t define a const on line 5 as the output of a function defined on line 18; the interpreter doesn’t yet know about that function, when interpreting the assignment of the const.
In the last few months, I’ve been working with a lot of makefiles. And, in makefiles, things tend to be structured opposite to how they’re structured in Python: the all
recipe, which is likely to be among the most dependency-heavy recipes in the file is the first to be defined, and subsequent file structure tends (at least idiomatically) to follow the pattern onward, placing dependencies below the recipes which depend on them.
Both of these options—dependencies before dependers, dependencies after dependers—are perfectly reasonable and sensible ways to do code-ordering. They’re opposite, but they’re both clear and non-confusing. Where things get bad is when neither of these protocols is followed.
This afternoon, I was looking at some Haskell code, in anticipation of finally making an effort to learn the language (as I’ve been meaning to do for years). In particular, at the sample code on this page (archive). And I was struck: the code changed direction, midway through. At the beginning, it was defining its grammar dependency-last, starting with the highest-level pattern (top
) and working its way down to lower-level ones on which that pattern depended. But then, suddenly, after it had defined its grammar, it went and defined a main
function dependent on that grammar. Within the grammar, the dependencies were below the dependers; but, outside of the grammar, the dependencies were above the dependers.
Shifting directions like that, midway through a file, makes for a confusing reading experience.
Sometimes, perhaps, it’s mandatory due to the restrictions of the languages you’re working with. (To anyone designing languages: please don’t make that sort of direction-changing mandatory; it makes the code more confusing to essentially no benefit.) And sometimes, perhaps, it allows you to simplify the code on other axes enough to be worth it. (Earlier today, in a Rust program, I had an earlier-defined function call a later-defined function, because the later-defined function was part of a larger block of parser functions which sorted neatly together and which, as a group, were dependent on the earlier-defined helper functions as a group; reordering the functions to follow dependency order individually, rather than as blocks, would have reduced code readability more than the direction-changing did.)
But, as a general principle, code tends to be far more readable if you pick a single direction for your dependency-ordering and stick with it. I try my best to write my code that way, when practical, and I recommend that everyone else do the same.
Python Supports Type Annotations Now!
Posted by Alyssa Riceman on
This week, I returned to Python for the first time in A While. And I discovered something wonderful and beautiful which was a continual source of delight to me throughout the week: as of Python 3.9, Python now supports type annotations!
Being Python, of course, it’s supporting them in a weird Python-y way. They’re officially called ‘type hints’, because unlike traditional type annotations, they’re only there for humans; the interpreter doesn’t care the slightest bit about them. You can throw a dict
into a function marked as taking in a str
, or return a bool
from a function marked as returning a tuple[int, int]
, or whatever else, and it’ll function just as it would with no type annotations at all.
But, even if the interpreter doesn’t care, I, as a person who needs to interact with the source code, care a lot. The type annotations make it so much faster to figure out what I’m supposed to do with unfamiliar functions or methods, as compared with reading the docs (if I’m lucky and the docs are clear on the matter) or trial-and-error testing (if I’m less lucky).
(They also make it all the more conspicuously inconvenient, when a given library happens not to have implemented type annotations in its code yet and so I am still forced to do those things.)
For that matter, even when working with familiar code and functions, the annotations still end up being useful, just for the way they put type information right at my metaphorical fingertips such that I don’t need to rely on memory so much. When I hover over a variable name in VSCode, now, the IDE will let me know what type that variable is to the best of its ability to figure out, saving me the trouble of rereading the code which produced that variable to make sure it’s the right type.
Overall, this is an excellent change, and I’m glad it’s happened.