(Updated Wed 2021-12-01)

Functional Components

In the process of re-writing my old Perl accounting software in Fexl, I am stumbling onto a neat technique for writing software "integrated circuits," aka "functional components," aka "parts."

The interesting bit that's allowing me to progress rapidly is to write isolated functions which do interesting things, but not bother too much with naming them. I actually roll a random number and call the function something like "d8214". Not a very illuminating name I know, but a block comment above it explains roughly what it does. The name itself is like a standard "part number" of a hardware IC.

That sounds weird, but it liberates me from the "tyranny of naming."

As I go, I relentlessly refactor functions to minimize the number of such components. At an upper level, I might end up with such gems as:

# Return formatted data for the "Change in Net Asset Value" section.
\d2997=(d2479 d1006)

# Return formatted data for the "Balance" section.
\d1622=(d2479 d8214) 

Yeah that's right: the d2997 component is just the result of plugging a d1006 into a d2479.

What could be clearer? ;) OK, I could rename "d2479" as "format_numeric_data_as_monetary_values" and "d1006" as "get_numeric_data_for_the_change_in_net_asset_value_section", but why?

Sometimes when developing a new capability I am tempted to build that into a part that's already stable and minimal. But now I can say no, I'll just make a new part that provides exactly the new thing I need without disturbing the old part. If I find that the old and new parts have a lot of structure in common, I can abstract the similarities into a new common part — passing in paramters which can include data or even entire parts (functions) themselves. I don't always have to do that, because sometimes it is possible to extend the old part without disturbing anything. But I like making relentless forward progress and never having to look back. Plus, I don't degrade the efficiency and independence of the old part.

Also, by using caching ("memoization") inside a part, I can avoid having to deal with annoying preconditional sequencing issues to optimize performance by computing things that might be needed up front. The part can cache in memory using the remember function, and/or it can cache to a disk as well. If I happen to activate the part, then fine, it gives me the same output for the same inputs every time, more quickly on subsequent calls. Otherwise it just sits there and does nothing.

Names versus Topology

It's not so much the names that matter, but the topology of the interconnected functions. The whole thing could be laid out using a sort of computer-aided circuit design tool -- instantiating components, drawing traces, etc. Physically, I don't have to worry about voltage and capacitance, but the functions do have propagation delays and memory footprints, so it does involve actual physics.

I don't carry this approach all the way down of course: there are still functions with names such as "map" and "append" and "format_money". But the neat thing is that when I discover a common functional pattern between two otherwise distinct functions, I can abstract that pattern instantly into a new function with a "part number" without having to obsess over giving it a long descriptive name. Plus half the time I'm not even sure that will turn out to be the best design, so why waste time with the name? I just proceed and see if I like it.

I can imagine a CAD tool popping up a block comment when I hover over a component in the functional circuit, and providing a search function for locating components based on these comments.