To Each Module Its Own

Posted on Jun 14, 2021

Long time no post. That’s bad. Well, it’s not that I’ve got any kind of schedule here, but I do like keeping a steady pace of sorts. I like writing, I love tech, I super love writing about tech! And I feel somewhat empty when Life™ prevents me to dedicate proper time to this my little place on the web.

Earlier this week I released scalc Horrible release cycle, seriously. Barely had time to really devote myself to the code and memory registers were madness to implement.

That’s precisely what I wanted to bring your attention to. OK, not the feature itself of memory registers in scalc, which are just trivial… but sharing a small reflection on why I made it so hard onto myself to implement a very simple feature.

Memory registers in scalc are inspired on your run of the mill scientific calculator that provides more than just M as a place where to store values. scalc has 10 of them, labeled A-J. A very simple session log follows:

$ scalc
> 34
> :sav E
> E
> 2 E *

OK, this looks quite easy, isn’t? Just look whether you “see” any token within the A-J range, convert that information into some kind of memory location where values are stored, save data there or read data from there, and bingo! Yeah, that’s essentially what it is.

For reasons that I won’t get here right now,2 scalc uses two parsers. One for commands (all of them start with ‘:’, e.g. :sav) and a parser to deal with maths expressions. Problem? Memory registers are the only thing in all scalc that can be used by both parsers… as :sav takes a register label as its argument (and stores the top of the stack to that register)… and you want labels to be accessible within math expressions, as if they were actual numbers.

Long story short, it was a mess. Poor design always shows to be poor in the worst possible times. The main culprit was how the command parser didn’t initially allow for commands to have any arguments… and was awful in general. See here for a first major improvement and here for the addition of :sav (although it was called :mem back then) and adding support for arguments.

The math parser was easier to deal with though: if a token isn’t a number, then we check whether it’s a label, or else an operation. Very straightforward actually. I’m sparing you links to the repos, because that part went way smoother…

I can’t stress it out though, how important it is to divide and conquer your code. Yep, mem.c is just two exportable subroutines and another one that is static. It might seem it doesn’t “deserve” to be a module on its own, but now think… what was the alternative? Cramming it into the main module, which is way too crowded already?

A very, very wise coder once told me: To each module, its own. Modules should be seen as independent semantic blocks related to some specific “thing.” I know, not too precise, but it translates my feelings quite well… Are these subroutines semantically close? Are they meant to be used in the same context? Do they make use of the same data types…? These are some of the leading questions I ask myself to decide whether a chunk of code is going to live in its own place or not.

I also follow the static by default style of coding. What that means is that nothing is exported out from a module unless there’s a proven need or benefit to. This makes debugging way easier than you think and even helps the compiler a bit in optimizing your code… for free! I know, this doesn’t bring any sorts of runtime protection, but that’s not the goal here… The goal is to make it easier for us coders to understand how our components cooperate with each other.

One thing is for sure: this ain’t a science, but an art… and probably everyone will have their opinion on how to split stuff in modules their own way. That’s totally fine! Don’t send me lengthy rants to my email, though /jk As long as you get used to the idea that sometimes it is worth having things in their own places, even if they’re just small… It’s always about the meaning, not the LOC!

  1. scalc 0.2.1 is very, very likely to be released any time soon. It includes a nice improvement that makes packaging scalc much easier for downstream mantainers! ↩︎

  2. And this is very probably going to change radically over the course of the 0.x development series. ↩︎