2022-12-25

A New One By Me

Our daughter is intensely interested in upcomming changes in her life. In particular she's aware that she is currently attending the most senior program as her pre-school (a transitional class the state will accept in lieu of kintergarten) and wants to know about where she will go to "homework school".1

The phrase is descriptive and its meaning is clear, so it's a logical neologism, but I'd never heard it before. Is that a term of art among the pre-school set?


1 A very good question about which we are currently having a slow motion panic. Much research is being conducted and many experts are being consulted.

2022-12-18

API history

So I made some progress today on the Emacs configuration improvements I mentioned in my last post.1 That work was performed using the partially completed configuration on my Raspberry Pi 400 (i.e using Emacs 27.1).

Then I pushed and opened a machine at work to try it out in a legacy environment (using Emacs 25.2.2). No go: one of the elisp functions I used in solving a problem apparently got more robust between 25 and 27.2 Check the documentation to no avail. Not surprising when I think about it: no reason the on-line3 documentation for Emacs 27 should contain any history.

But having a central source with the history is hugely useful. I regularly make heavy use of cppreference.com where APIs are exhibited annotated with the version(s) to which they apply and historical versions are listed along side their more recent counterparts. The availability of the history makes it easy to see why you can't use that StackExchange solution as is, and why the other one seems clunkier than need be.

So far, I haven't found anything equivalent for Emacs.


1 I also ran into that "supporting multiple versions is hard" thing even programming on a single machine. I used a function defined in the subr-x package meaning that until I re-implement that I'm limited to version 24.4 and newer. Sigh. We'll see about that re-implementation thing. I don't think I currently need to go that far back in time. If I still had to support that 2010 legacy platform it would be another issue.

2 So now I'm building versions 24.4, 25.3, and 26.3 on my Pi so I can test a variety of version in one place. Might want to do a 28 and a 29, too, but let's start with what we've got, OK? Anyway, I remain impressed with the Pi, but trying to build Emacs in the background while doing other work does expose its limitations.

3 That is live-in-the-program rather than available over the network. How the language has changed, eh?

2022-12-17

Compatability history

Have you ever noticed how hard it can be to find out which versions of software (a) were compatible with which versions of software (b)?

Documentation for software that is meant to interoperate with other software generally says something about which version(s) of the counterpart it works with, but no one has any particular reason to keep a list of the history of that relationship.

What brought this on, you ask?

I'm trying to modernize my Emacs configuration file (for which there are many luxurious options) any have reasonably uniform behavior across the several systems I work on and home and work. Alas, customer requirements at work mean I need to deal with systems dating from 2014 (and we're happy they've finally gotten serious about migrating off that platform from 2010).

All those, aforementioned, luxurious ways to configure a modern Emacs system (Spacemacs, Doom Emacs, and other "distributions") require you to be using, well, a modern version of Emacs. By which they mean v25 is definitely out and v26,fi supported at all, will soon by unsupported.

So I'm trying to do it for myself. My strategy depend on several things:

  • add-package to centralize the configuration
  • straight as a way to get packages
  • the identification semantic categories of behavior I'd like my emacs to have and the understanding that those behaviors could be provided by different packages for different Emacs versions
  • a system for selecting implementations of those categories by Emacs version number

I expect to make the project publicly available pretty soon.

But let's say my category is language server services. Popular suppliers seem to be eglot and lsp-mode. If I want to use lsp-mode on a up-to-date Emacs I'll just let straight grab the newest release of the package. But the newest version relies on variable that are not in Emacs 26, so I need an older version, but how do I learn which tag straight should get for those system?

And the problem repeats for every behavior which isn't handled internally.

Which, I suppose, is why the big distributions tell you "Use a modern Emacs, already!", albeit usually in more diplomatic terms.

2022-11-25

But ... I knew that. I just didn't understand the importance of what I knew.

Still more Randall Monroe greatness.

What with one thing and another I learned about the TEA project recently. Ambitious stuff, and I'm completely unprepared to talk about the project as a whole, but being a ex-academic, I wasted no time downloading the white-paper, and brought along a dead-trees version for flight as we took our spawn off to visit one set of Grandparents.1 The paper concerns itself with a distributed crytographic ledger to be used for archiving and authenticating the open-source ecosystem but also to provide a framework for distributing donations across that ecosystem.

The point is that much of the open-source ecosystem is un-sexy tooling and infrastructure that only excites the people who use it to write other open-source software, but donation come from either users (who tend to direct them to sexy top-of-the-stack end-user stuff) and companies (who direct them to things they are specifically trying to improve). Very little money flows to underlying stuff that "just works" even if it requires on-going maintenance to preserve that state of operability. There is a reason Randall uses the word "thanklessly" in the comic. Anyway, the paper observes that to perform such a distribution you would need to know how much each project contributes to the ecosystem, and then that a package manager (such as apt, pip, npm, or homebrew) is exactly an encoded version of that knowledge.

I'm pretty sure my jaw hung slack for the several seconds it took for my mind to re-orient on my freshly remade understanding of the open-source ecosystem. Of course, the package managers already know that stuff. At least collectively.2 And maybe missing the dependency links implicit in rarely used build options. But for the most part it's there.


1 Between the pandemic and our commitments taking care of honorary Grandma we'd been able to avoid this for some time. Now we will, no doubt, have to do the other Grandparents soon as well.

2 It is worth noticing that there are multiple classes of package managers and build dependency systems out there, handling independent (well, largely independent) and distinct (again, largely) ecosystems. To perform as envisioned the proposed tea protocol will need to be flexible enough to express relationships within and between all such groupings. Not easy stuff.

2022-11-20

Not actually that bad (AKA git submodules part 2)

In my last post, I vented some frustration related to a work project. At this point, enough progress has been made to walk back the most wild speculations. Unsurprisingly, part of the problem was me, though that leaves the tool to take some of the blame.

At the end of the last episode we had explored the technical reason you can't simply chain a series of clones of a repository using submodules. Depending on the way submodules are identified, you may be able to work around the limitation (what I've done)1 and you may be able have all clones use the same (possibly thrid-party) master repository for some submodules. Depending on your use case these two options may be sufficient, and in the case of third party modules the latter may the Right Thing (tm).

At that point I actually had my local server copies in place but it wasn't working right. I'd started my investigation and gotten far enough to write

Only now there is the matter of branches and tags.
without having quite solved it. The symptom I had noticed is that there was a branch, call it develop, on the central server that I couldn't checkout from the local server. If I drilled down on the hosting website to find hashes for the version of the files I wanted to look at I found that those hashes were present on the local server. But the branch wasn't.

So, what is a branch, where do they come from, and how do I make sure that the local server has the ones that are on the central server?

A branch (and indeed a tag) is just the associate of a textual name with a particular commit. This class of objects are called "refs".2 And while cloning copies all the contents (commits, trees, and blobs) of the cloned repository it performs some bookkeeping on refs. Moreover exactly what bookkeeping is performed depends on how you run your clone. The gory details are available thanks to stackoverflow user Cascabel and editors, but the long and short of it is I had created the repositories on the local server using git clone --bare when I should have used git clone --mirror.

Sigh.

Thankfully, stackoverflow user onionjake knows the incantation to fix it up in place.


1 In the work-around the non-terminal repositories are in no way unified. You check out the top-level without recursing into the sub-modules and then check out each submodule separately and carefully locate them on your file-system relative the top-level in the way that the terminal repositories are going to expect. The downside of this is that there is no tooling for keeping them in-sync. I suppose I'll write a script. In python, perhaps, because I'm trying to get away from using unix-specific tools for things that could be cross-platform.

Verbing weirds language.
Calvin
2 The main difference between branches and tags is how the associated behaves when you git commit. Tags simply don't care, once you set them up they are fixed and always point to the same commit, but branches can move. Git keeps track of what branch you are "on" and when you perform a commit action, it moves that branch to point to the newly created commit object. Of course, using the same word for the noun and verb is not in the least confusing.

2022-11-16

Is git submodules really this bad? (part 1)

As I mentioned, I'm "getting" to use git submodules, and my frustration level is making a bid for a new personal best. My sense is it's a little clunky even when you use it exactly as envisioned and breaks completely as soon as you stress it. I hope I'm exagerating or outright wrong because I have no choice but to work wih it.

Here's the problem: we're working on one small piece of a larger project (a plugin, as it happens), and the project management is security conscious enough to:1

  • Use submodules as part of a system to selectively limit access to the repository: I can see all main API headers and only those implementation details I will be working directly with. I don't "need" the rest because I can test my plugin against a binary disribuion of the core program.
  • Make it quite a gaunlet to get individual credentials for direct access to the central repository.

To avoid sending each member of my team through the gaunlet as they join I thought "Oh, git is a distributed system,2 right? I'll just create a local working repository for my team and we can push back upstream when we're happy."3 Which is, evidently, not something the designers of submodules anticipated.

The core issue is submoules are found by follwing a either a path or a url which has implications for how a clone of a clone works in projects that use submodules. Look at the contents of a .gitmodules file: for each module there will be a url tag. That tag may be formatted as a filesystem path telling git where to look for the sub-repository on the filesystem where it found the super-repository, or as a url telling git where to find the repository on the wider network.

Image that there exists a project on a central.server: central.server:/repos$ ls compoundproject.git includedproject.git utilityproject.git central.server:/repos$ cat compoundproject.git/.gitmodules [submodule "IncludeProject"] path = IncludeProject url = ../includeproject.git [submodule "UtilityProject"] path = UtilityProject url = http://central.server/repos/utilityproject.git and note that I've rigged the two submoules to use different logic about finding their related repos, but that they will both find the one on the central server.

Now I create the repository for my team (there are slight differences if we make this a bare repository): local.server:/home/git$ git clone --recurse-submoules http://central.server/repos/compoudproject.git [...various git output that looks good...] local.server:/home/git$ ls compoundproject local.server:/home/git$ ls -A compoundproject .git .gitmodules IncludeProject UtilityProject [...some top-level contents...] and if we peak in the sub-project directories we'll see the expected contents.

Next a member of my team tries to set up a working repository developerworkstation:/home/developer/Projects$ git clone --recurse-submoules http://local.server/home/git/repos/compoudproject but this is going to fail when it tries to get IncludeProejct because it is going to look for it at http://local.server/home/git/includeproject.git instead of at http://local.server/home/git/compoundproject/IncludeProject, and if we assume that the developer does not have credentials for central.server then it would also fail when trying to get UtilityProject because it gets that from the central source.

Now, I can solve the first problem by (bare) cloning includeproject.git to local.server beside compoundproject.get. The second problem can only be overcome by getting the developers credentials for central.server.

Okay, backup and replace the subjunctive above with my actuall situation. In effect utilityproject.git is actaully reached by relative reference just like includeproject.git. Consequently I have made bare clones of all three projects on local.server and my developers can do a git clone --recurse-submoules http://local.server/home/git/repos/compoudproject.git and get all three. Yeah! Go me!

Only now there is the matter of branches and tags. I'm not sure I understand this, so the saga will have to continue another day...


1 Coming in heavily on the side of security in the security-versus-getting-things-done trade-off is par for the course in my industry. I've been sighing a lot about this but I'm not at all surprised.

2 Big selling point, right? Every repository is equivalent and you can move updates from any repository to any other repositorye. Of course, the way people actually use DVCS there is a (or are a few) repositories that are central to the workflow even if they are not special to the underlying software. For that matter in git those ones are usually configured as bare repositories so there's feature support in the tool for the distinction. But it is still better than using SVN.

3 Bear in mind that this plan would work just fine for a plain boring project that used git without any sub-whatevers.

2022-11-08

Mystery of the Month Club

Git.

The git parable does a pretty good job of explaining why git makes sense as an abstract tool, and of preparing you to understand other articles on why you should or should not git in various ways.1

But git will still surprise you. I think I sussed out the bit that confused me today. Maybe. It's all to do with submodules. We've opted not to use them locally because we were convinced we didn't understand all the implications. I think we were right but I've gotten involved in a project to write a plugin for a third party tool which does use submodules, so I'm getting to learn.

Of course, submodules is just one option in the mix-n-match-repositories marketplace (along with subrepos and subtrees as well as various wrappers). Probably because the use case was not part of the original design and all the patch-it-up-afer-the-fact schemes have realy drawbacks.


1 Did you notice that the cherry picking article comes in 10 (!) parts. That's because, elegant though it is, git's underlying graph theoretical model requires you to keep track, not just of a DAG, but of an evolving DAG. And also because the series actually explored multiple issues. The first four or five articles (if I recall correcly) treat the main subject and the rest explore extentions to the basic idea.

2022-11-07

Not an advertised feature

The child has a tablet and likes to stream stuff on it in the car, which means tethering it to one of our phones (yes, we're wondering about making the next one cellular enabled). You can imagine the various minor inconveniences that implies, but it also means that when my wife set off on the pre-school delivery run leaving her phone in the upstairs bathroom (where the tablet had no trouble connecting to it), she rapidly learned that something was wrong.

Back they came and (using her smartwatch) she called me from the driveway to tell me she didn't have her phone. Cue double take.

New use for the table: phone abandonment detector.

2022-11-01

Merge tool supporting on-the-fly revision history?

There is this internal library where we've been working on four branches. Call them devel, feature-a, feature-b, and feature-c. All three feature branches are significant and long running, but they have at least had the incremental improvements and bug fixes from devel regularly merged back in.

Recently feature-a and feature-b were merged to devel (by someone else, yeah!). Which means that devel now has large changes relative feature-c. Now, I'm one of the folks working on feature-c and I spent a little while today trying a "what-if" merge of devel into that branch. It wasn't as bad as I feared (because much of this branch is working on different parts of the code than the others), but it was bad.

While I was staring at the merge tool showing another set of conflicting changes without (a) recognising either change as one I had done or (b) understanding the intent behind either occured to me that it would be really nice to be able to ...

Frob1 a highlighted (i.e. changed) section of text to obtain a quick peak at the relevant change logs.

In the simplest form you would see the change summaries from the current commit back to the common anscenstor for each branch; better would be filtering only those commits that touched the current file, and best would be filtering those that involve the lines in question. Being able to drill down on interesting summaries is a bonus feature.

At that point—assuming your team writes reasonable commit messages—you have a fighting chance of sussing out the intent of the changes and thereby a better chance of choosing the right merge action. Of course, I can (and do) check those logs in a couple of terminals kept handy, but UI sugar can improve the experience.

This seemed like something someone might already have implemented, but my Google-fu wasn't up to finding it (or reliably eleminating the possibility). Anyone know?


1 Hover over, center click on, or something. Ask a UI specialist to help.

2022-10-22

This is not a job for the algorithms header, after all.

As Mark points out below, the whole premise of this post is based on having misread the documentation. My initial expectations were more or less correct, and my effort to be a careful documentation-reading programmer back fired. Well, $#!+! There is a probably a lesson there.

Watch this spot for further posts to push my shame down past the point where most poeple keep scrolling. Or something.


I was stymied today.

You see the code I was working on feaures a type, call it KeyThing, that includes a double weight member intended to be used in forming averages over some information accessible using the rest of KeyThing.

Because the absolute scale of the data accessed wih the key matters, averaging is proceeded by a normalizing step: the sum of the weights must add up to unity. Which means you have to, well, accumulate the weights. Right?

So, being a good little programmer, I reach for the algorithm header and start by writing const auto sum = std::accumulate(collecion.begin(), collection.end(), 0.0, [](){}); only to pause briefly because I need to give the empty lambda the right signature. I'm thinking that it must take one double and one iterator that I can use to get access to the weight, and I just need to look up the order they come in.

C++ Reference quickly disabused me of that notion. The binary operation in std::accumulate expects to recieve two values that are of (or are implicily converable to) the type of the initializing argument (here double).

I can't use a lambda like [](const container::const_iterator it, double sum) { return sum + it->value; } wih either order for the arguments. Perhaps the symmeric-arguments version is more in keeping with the principle of least astonishment and generates clearer code in the easy majoriy of cases, but this is also a lost opportunity for more generaltiy. Now, obviousness and clarity are significant virtues in an API, so I won't claim it represents a bug, but it sure tripped me up this time.

So I wrote double sum = 0.0; for (const auto & key : container ) { sum += key.weight; } and moved on. Because while I could write nsc::accumulate which uses my version of the signaure that would be less clear.

2022-10-13

On being "that guy"

I contacted a support desk with an issue that I should have had instructions for but didn't. So far so good.

However, I followed up by asking a question about their response that I'd have answered for myself if only I'd scrolled beyond the page break. Not a good look.

In my defense there was a huge blob of whitespace at the botom of the page and I thought I'd reached the end of the document.1

Alas, after they poliely didn't tell me what an idiot I was being, I commited the "didn't read far enough" error again. Sigh.

Sorry guys. I did frontline support for a while and I feel for 'ya.


1 Because Word is paricularly crappy at layout even by word processor standards. One of the things I miss about academia is LaTeX: the typesetting and layout might be boring but they will avoid the easy misakes by default.

2022-09-19

Sworn enemies?

Even the pious Scots, locked throughout history in a long-drawn-out battle with their arch-enemies the Scots, managed a few burnings to while away the long winter evenings.
Terry Prattchet

The last couple of weeks have seen me drawn into another skirmish in the defining contest of the information-age workplace: schedule and cost estimation for development projects.

Managers of course need to know what things will cost and when they will be done. Developers know from hard won experience that it is not possible to know either of those things before you start the work. The closest things to reliable soluions that have been developed seem to fall into two rough categories:

  • Process-heavy mechanisms where you front-load the detailed design and get a moderately reliable estimate of the total cost and time when you're 15-20% into the effort, but entailing some risk of a complete failure
  • Agile methods which get to something early and evolve ever closer to the desired product over time and might be alleged to generate a reasonable approximation to the spec given the schedule and budget allow

Now, our client has allowed us to pursure the latter strategy while my project was an exploration of possibiliies (and just as well, it's what I know how to do), but now that it is a product in actual use, they are asking for or even insisting on firm commitments on both time and money. Sigh. Best guess and a generous pad it is.

2022-09-09

Algorithms of adjacency

Today at work I wanted to test the relationships between the contents of a vector in a sequential manner: cell i+1 should have this relationship to cell i kind of thing.

Question:
Does the algorithm header support that?
Answer:
Yes, two ways.
Question:
Can you tell just by reading a list of the calls?
Answer:
Maybe. How much attention are you paying to the signatures?

The key word here is "adjacent". Two of the algorithm header routines have that word in their name: adjacent_find and adjacent_differernce, and both of them have an overload where you supply a binary operator.1

Of course, the issue of difficult naming rears its ugly head here: adjacent_difference can perform any computation it wants to create the output values written (i.e. it is a version of the pairwise overloads of transform specialized for adjacent cells). Kate Gregory's story about partial_sort_copy is relevant here.

So that's cool: what I wanted was right there.

Only I started thinking2 about the other things I might want to do this way, and I noticed that the library doesn't give me a built in way to count pairs that meet some predicate. Of course I can build it out of adjacent_find fairly easily,3 but it's not baked in.

My problem at work? Well, that was test code, and the predicate was a lot clearer if you knew where you were in the container you were, so in the end I wrote for (size_t i=1; i<container.size(); ++i) { const auto thing1 = container[i-1]; const auto thing2 = container[i]; // ... } The library routines are great, but knowing when to not use them is great, too.


1 You can also acheive same two operations by using mismatch and transform, though I feel the "adjacent" named routines make the intent clearer. I takes a moment to suss out the meaning of something like const auto [i2, i1] = std::mismatch(++container.begin(), container.end(), container.begin(), my_comparator); aferall, while adjacent_find is pretty plain (though you'll have to parse the behavior of the supplied comparator in either case).

2 I know. I know. That was my mistake.

3 If I had more spare time I'd poke ino some of he standard library implementations to see if people build count_if from find.

2022-08-11

Loving and Hating the Same Book

I bought a book in an airport not so long ago. As usual with airport news-stands the pickings were somewhat slim and I opted for something I might not have chosen from a broader menu. This is a rather long book that is claimed by the self-designated and self-concious literary establishment, though it is arguably science fiction as well.

It kept me harmlessly engaged for the rest of that rather long travel day, but after we returned home I got distracted by other reading. I hate to leave stories unfinished, however, so I've returned to it, and I'm experiencing the same mixed emotions I had on the plane.

Considered in the small, I'm blown away. Sentence after sentence, paragraph after paragraph the writing rolls on carrying me along wih it. Each element is put together with skill and craftmanship. Mere middling examples plucked from the text as good as the best I've ever written. Word selecion is precise, cadence is smooth, mood and ambiance permiate the language. Details dropped in like grace notes place the action in time and place or evoke he personallity of characters. Damn it's good.

But in the large, I'm somehow ... bored. Every time I turn a page I am disappointed that I haven't reached the end of the chapter. I struggle to hang on to any semblance of plot arc.1 I despair over how little of the book is behind me. I long for a couple paragraphs of clarity instead of literary stylings.

The book in question is Thomas Pynchon's Gravity's Rainbow. It's widely considered a masterpiece despite being a little dense. If I do manage to slog through to the end I may have some more thoughts, but for now I just note my paradoxical love and hate for the writing.


1 Well, to judge from descriptions of the work online, this is a particular offender in this regard; having more characters, more seprate threads, and just more events than is typical even for "literary" fiction.

2022-07-30

Why didn't I think of that description?

For the last couple of weeks I've been making my way through A Brief History of the Earth (subtitle "Four Billion Years in Eight Chapters") by Andrew H. Knoll. Brief shout-out to the author for including the (anti-)neutrino among the products associated with beta decay in his discussion of radiometric dating.

Nice book so far (I've reached the last chapter). I had a rough appreciation for much of the subject matter coming in, but it is clear that there has been much progress since the last time I read up on the discipline. Some open questions have been answered outright, and many details have come into focus where rough outlines existed before. All very cool.

One detail of the writing, however, is worthy of special mention. The bibliography for each chapter is divided into two sections: "Approachable Readings" and "More Technical References". I sorely wish I'd thought to use "approachable" as a description of pop-sci material more often in the past.

2022-07-11

Little surprises #8: using the preprocessor to create latent bugs

Aside: As opposed to using the build system or the template processor which are totaly different except in the ways they are completely the same.


Languages with C-derived syntax and exceptions generally have a try-catch construct and the ones I'm familiar with have a little trap for the inattentive programmer: the catch block is a different scope from the try blocks. Which means when you merrily write try { ErrorInfo e; someRiskyOperation(e) } catch (...) { print("Risky operation failed with error %s\n",e.explanation()); } because the argument to someRiskyOperation is an out-param1 and you hope to capture the error data that was generated before the exception was thrown, you actually get a compilation error because e isn't in scope when you try to issue the message.

Not a big problem, of course, at least two work-around are obvious.

Except that I tried to send a message to our logger, so the code looked like try { ErrorInfo e; someRiskyOperation(e) } catch (...) { LOG_WARN("Risky operation failed with error " + e.explanation()); } where LOG_WARN is a macro whose purpose is to capture the file and line where the error occured.2 And that's a problem if this code occurs in a library that can be compiled with logging support turned off by simply defining the logging macros as nulls: #define LOG_WARN(explanation) , because in that case the compiler never sees the offending e.explanation. It was rewritten as nothing even before lexing. So you can test the code with logging turned off to your hearts content and never find the bug. Sigh.


1 Yes, I agree: out-params are generally evil. It's a legacy API. Perhaps we'll get around to replacing it eventually.

2 When it actually does something it looks something like #define LOG_WARN(explanation) do { \ logger:Message logmsg(__FILE__, __LINE__, explanation, logger::Warning); \ logger::getLogHandler().post(logmsg); \ while(false) and it can't be a function because __FILE__ and __LINE__ are macros and have to be evaluated in place. Unless and until the language provides operators for capturing source position data like that this is the best we can do.

2022-07-10

The Rip-off from Cleveland Depths.

Title in reference to Fritz Leiber's 1962 novella "The Creature from Cleveland Depths".1

The complex of product options that go by "activity tracker", "health monitor", or "wellness monitor" (and smart watches that include that suite of functionality) aren't exactly the "tickler"s in the story, but they share a lot of DNA with them. They are, after all, automated systems we wear explicitly so they can nudge us toward better versions of ourselves. You can get other parts of the tickler package as apps. Either for your phone or, if you choose the right smart watch for the wrist mounted device itself.2

One of the psychologically interesting things is that the prime statistics these things recorded when they first took off was ... steps. They were glorified, electronic pedomenters.3 Pedometers have been around for ages and never made such a big splash before. I suppose gamification is the key to that. Anyway, today's rantlet is about the way a wrist mounted pedometer fails us: you don't get credit for pushing a shopping cart. I can trek up and down every aisle of the local, big-box, warehouse club and the fool machine registers a few hundred steps because pushing the fool cart more or less immobilizes my wrist. I only get credit for putting things in the cart. The old waist-mounted mechanical jobs managed that kind of use better.

Okay, so it's seriously a first-world problem. You're right. And it's of no actual consequence. You're right about that, too. It still annoys me.


1 Available from Project Gutenberg or in audio form from Librivox.

2 I wear a Withing ScanWatch so I can't get a bunch of apps on my wrist, but I do get reasonably comprehensive health tracking, weeks of battery life (I only every charge it while I shower), and the aesthetic appeals to me.

3 Indeed, the cheapest ones still are, but many do a lot more these days.

2022-07-03

Who shaves the barber?

Or how should a logging framework appraise the caller of unexpected situations caught and handled, anyway?

2022-07-02

The ABCs of aspect oriented C++ (part1)

OK, so the title is a little ambitious, but I am going to show you how to prove a limited level of aspect orientations for a few cases.

What the heck is "aspect oriented programming", anyway?

Honestly, I'm not strong on the subject never have worked in that format myself, but the short version is that it allows you to add behavior to program abstraction without editing the associated code. Common examples are attaching entry and exit loggers or call count tracker to functions without editing the function. C++ has no built in support for it.

Anyway, I'm going to start by showing you a very limited and specific trick that has the same flavor, then generalize it.

Example: interface with non-trivial behavior

We have a hand-rolled serialization mechanism we use on a work project. It supports selected fundamental and standard-library type and provides an interface (AKA Abstract Base Class) for building support into classes we write for ourselves1

class SerialInterface { public: SerialInterface1() = default; ~SerialInterface1() = default; virtual bool serialize(std::ostream & out) const = 0; virtual bool deserialize(std::istream & in) = 0; };

Class we want to (de)serialize derive from SerialInterface and implement the virtual methods using some utility code to maintain uniform behavior.2 The return values tell you if the process was successful or not.

We have classes whose sole purposes is to be serialized and deserialized to support interprocess data transfer, which makes it very convenient to accept the input stream in a constructor

class Client: public SerialInterface1 { public: Client(std::istream & serialData) : Client() { deserialize(serialData); } };

But what happened to the status? It's lost, which is a problem.

Now, we could throw in the the event deserialize returns failure, but who like exceptions? An alternative would be to cache the success value and offer an interface to query the value. But then you have to duplicate the code in every client class. What we would like is to give the base class the status caching behavior without needing any duplicated code in the client classes. We achieve this by splitting the implementation of serialize and deserialize into a wrapper defined in the base class and a code implementation defined by the clients.

class SerialInterface2 { public: SerialInterface2() = default; ~SerialInterface2() = default; bool status() const { return _serializeImpl; } bool serialize(std::ostream & out) const { _serializationStatus = serializeImpl(out); return status(); } bool deserialize(std::istream & in) { _serializationStatus = deserializeImpl(out); return status(); } private: virtual bool serializeImpl(std::ostream & out) const = 0; virtual bool deserializeImpl(std::istream & in) = 0; bool _serializationStatus = true; };

Now all client class support status inquires without needing any code to support it. We've provided a status aspect to all the clients.

More general aspects for callables

It is very common for the examples I see in explanations of AOP to focus on providing aspects to callable subprograms (functions, procedures, whatever you want to call them). And that is interesting because modern C++ already has a highly general idea of "a thing you can call like a function", which means that we can try to use the approach from SerialInterface2 to provide aspects to callables.

To generalize the behavior we provide virtual implementations for code to be run both before and after the the callable.

class FunctionAspect { public: using function_t = bool(int, double&, const std::string &); FunctionAspect() = delete; FunctionAspect(std::function & func): _callable(func) {} bool operator()(int i, double & d, const std::string & s); private: virtual void prologue() = 0; virtual void epilogue() = 0; std::function _callable; }; bool FunctionAspect::operator()(int i, double & d, const std::string & s) { prologue(); bool result = _callable.operator()(i,d,s); epilogue(); return result; }

Concrete derivative classes can then provide arbitrary code wrappers around any std::function with the appropriate signature. That restriction is, of course, a major drawback, but we're on the road to general purpose aspects.


1 Come to think of it, we could have avoided exactly this complexity if we had access to a general support for AOP in C++ in the first place.

2 Yeah, we should probably have used JSON, but it works.

2022-03-13

Recycling categories

My wife and I try to take advantage of whatever recycling options are available to us (and we compost, too), and because I've dragged her around the country following my jobs for the past couple of decade we've lived in places with varied levels of availability. Travel has brought us in to contact with still more way to organize the process.1

Now, I'm far from an expert on this industry, but I know it can be a challenging domain to work in. The work of sorting mixed waste into coherent categories is non-trivial and costs real money if done in bulk after the material is turned in or depends on ordinary people's willingness and ability to get the sorting right. And sorting errors are significant:2 batches of material are often diverted to landfill due to "contamination"; a word which apparently covers a lot of sins from including food-waste (pizza boxes in your cardboard recycling, anyone), to mixing different categories of material together, or carelessly tossing plain garbage in a recycling bin.

Our local arrangements require individuals wishing to recycle to bring recyclables to a central facility3 and load them into dumpsters and big bins in six different categories (paper, cardboard, glass, plastic, aluminum, and ferrous metals4). Which leads us to some questions:

  • Just what plastics are allowed?

    At least these are generally marked with the numbered recycling trefoil, but we're encountered places with an added requirement specifying only bottles with necks. Huh? And in most places I've lived plastic bags can't be recycled in the municipal system. You have to take those to the various stores that accept them.

  • What is the difference between paper and cardboard?

    How do I decide? Does it matter if the paper is glossy or not?

  • What aluminum items are accepted?

    In some places it's just cans; in others you can bring foil and disposable cookware (if clean). What about substantial pieces of structural meta?

  • Glass?

    Does is need to be sorted by color? What if it's broken?

  • About that food waste thing...

    Does it rule out greasy paper? What about bottles and cans with beer or soda remnants?5

  • Batteries?

    These are a significant source of hazardous waste in landfill streams, but in most places they are hard to recycle. It's generally the case that you have to sort by chemistry and that cracked and leaking batteries are an extra pain.6

And so on and so forth. It's not trivial to know what is expected. You have to find, read, and indeed pay attention to the documentation supplied by your locality. Uhg.

Then there is the matter of what to do if you're not sure (or if, say, you and your spouse disagree about what the rules are). Should you submit things which you are unsure of at the possible cost of "contaminating" a load or contributing to a contractor terminating a contract? Or should you knowingly send things to landfill to insure that other stuff gets recycled?

I haven't traveled widely enough recently to have a good notion of how they do these things outside of the USA, but when I was going to Japan regularly in the noughties they seem to have a much more unified system (same bins and iconography in Tokyo and Osaka airports, JR station in several places, and in the small villages in Toyama prefecture. Europe was more varied than Japan but more consistent than the US.


1 When we attended a wedding on the big island of Hawaii just after the turn of the millennium the resort provided us with a laminated copy of the island's waste disposal guidelines card, listing approximately thirty different categories of trash, recyclables, and hazardous waste and detailing how/where each was to be disposed of (there were three categories of used batteries).

2 In one place we lived the city recycling unit made yearly reports on actual the levels of waste in various categories and the target's their upstream contractors would accept. And you can learn something about the process just be examining those targets: they would accept a much higher rate of ferrous metals in the aluminum then other types of contamination, so you have to figure that magnets were employed to divert the unwanted metals to the correct processing chain.

3 At least the lot in question is open 24/7. In one place the lot was open about seven hours a days (except on Sunday) with some days of the week including morning hours and others evening hours.

4 They mean iron and steel. I've checked because, the bin is labeled "tin cans" even though no one has used tin for cans in many decades. Linguistic habits can linger long after they are no longer strictly applicaable.

5 Not rinsing is a pet-peeve of mine. Not so much because of the smell, but because the aluminum bins at our local places are often swarming with bees. But I've met people who argue (not without a point) that we live in a desert currently experiencing a drought and they don't want to waste water on the effort.

6 I've had at-work arrangements for batteries when I worked on-site at national labs, and in my last job where we had an environmental health professor who knew all the right people and maintained a drop-off site in has departmental office. Which gives you some idea of how arcane that process can be.

2022-02-08

Covid update (3)

It came through the house again. Several of Grandma's caregivers got it and I got it. But Grandma, and my wife and daughter were spared.

I spent my quarantine in the spare bedroom which is where my home office is set up in any case. Comfortable furnishings, a big picture window, and various internet equipped devices. About as good as sitting in one room for a week-and-a-half can be. Mild case. After all, I'd already had it and was vaccinated and boosted. I got a lot of work done and a little on my side projects, too.

My wife's long-covid persists. She'd been making good progress (and is at least burning through a lot less supplemental oxygen), but in the fall she got first her flu shot then her Covid booster and each one set her back a week or so on her exercise. Then my folks came, then her folks, and, and, and. She was just starting to get back up to speed on exercise when I got sick and she suddenly got a bunch of worked dropped on her. Sigh.

Blockchain toasters?

During the original web boom, there were lots of great new ideas floating around but also a bunch of "internet toasters". That is ideas that came down to "we'll attach this [THING] to the internet and good stuff will magically happen" where toasters were used as the exemplar of the class of object which gained little by being connected to the internet.

Web2.0 had much the same thing allowing that we replaced "connect to the internet" with "build a social network community around". Of course that worked (and to the tune of huge amounts of money) for some notions, but it also failed for others. I don't recall if anyone called them "social toasters" but they should have.

Today I'm seeing a number of things along the lines of "we'll use blockchains to track/validate/protect/whatever" various kinds of data (often in the form of "let's make a NFT out of it!"). Given the math behind blockchains, I imagine that most of these notions are technologically feasible, but I wonder how many of them are blockchain toasters?

2022-01-09

UI standardization

I want to talk about microwave ovens, but first some historical perspective.

Have you ever read about the control system of early automobiles? And I don't mean just vehicles from the era when every car was a custom build. Even Ford's Model T has controls that differ significantly from the system we take for granted today. There is a hand throttle instead of the floor mounted accelerator pedal, some of the pedals had function we don't expect these days, and the pedal layout was different. And other manufactured cars from the same era had their own control layouts.

By the Model A we see the three pedals that a modern driver would expect to find on a standard transmission vehicle and in the expected order. However, there are more controls than you need on a modern engine (and not just the choke that lasted past WWII, there are spark advance controls and similar exotica). It took time for automation to be engineered to take over those aspects of regulating an engine.

Even today various details of things like headlight controls are not completely standard, though I can sit down in a unfamiliar rental car and find the things I need with only a couple of minutes of puzzling over the controls. And that's before we talk about the changes that Tesla has imposed on the throttle control to support their regenerative braking behavior.

Now back to microwaves. How long does it typically take you to puzzle out the panel on the microwave at a acquaintances' house? Can you just enter a time for default cooking or do you need to push a function button first. Is there a "quick-cook" system and if so how does it work? What about a time? Can you run the timer while you cook? If you can how will you know which one just finished? Etc.

Microwave ovens have been around for my entire life, yet the behavior of their control systems are in no way standardized.

I suspect this is partly because embedded control system used to be a significant expense, but these days you can get microcontrollers with more than enough horsepower and memory for less than a US dollar in bulk.1

Anyway, I propose that the standardization of microwave controls will represent a step toward utopia, unless a multi-touch gesture interface is chosen in which case it is a step toward dystopia.


1 Heck, you can get systems-on-a-chip that support stock linux for less than USD10. Remember when Andrew Tanenbaum criticized Linux for having too-heavy system requirements?