Published on [Permalink]
Reading time: 4 minutes

Predictable asynchrony problems

The first prototype is not awfully informative, except to reinforce what everyone knows: having a lot of asynchrony in a program is a great way to produce race conditions.

I thought I’d be deliberately naive and see how fast asynchrony came to bite me. Pretty fast, as it happens. As a result, I’m already deviating from my thoughts in the last episode.

I won’t be explaining code for this prototype.

Brian and the app animal independently see and manipulate the environment

The context is this: “Brian” and the “app animal” are simultaneously and asynchronously changing a Paragraph structure, which is encased in an Elixir process (as is required for any changing data). The app animal is watching what happens to the paragraph as characters are typed. It is looking for two newlines being typed in a row, which is the sign that Brian is embarking on a “Big Edit”.

The analogy was a cat intently watching a mouse hole for any flicker of movement.

Once a Big Edit starts, the app animal should transition to looking for a sign that a fragment has been created. If one stretch of two (or more) newlines signifies a Big Edit, two such stretches (which I call gaps) signifies that the text between is a fragment that should be removed from the paragraph and put somewhere else.

Most interesting, perhaps, is that I was doing a certain amount of state management (representation?) by having a one process hand over work to another when it (the first) noticed something important happening in the paragraph it was watching.

About.Edit receives a steady stream of characters

To be specific: whenever the app animal focused on a paragraph, a persistent process named About.BigEdit was spawned and started receiving signals from the Paragraph as each new character was entered/typed. Its stored “model” of the Paragraph was limited to remembering what character was typed last.

Two newlines typed in a row would create the gap that signals a Big Edit. Once that happened, About.BigEdit would start a successor process, About.Fragments, arrange for it to oversee further paragraph changes, and exit.

About.Fragments replaces About.BigEdit

About.Fragments would continue to consume characters from the environment and do [something I never got around to implementing] to detect the creation of fragments.

But the (yes, entirely predictable) problem was that Brian could be typing new characters while About.BigEdit was launching About.Fragments. If some new characters were already queued up Recall that a process processes one message at a time. So if a message announcing a later character arrives while an earlier one is still being processed, it gets put on the process’s message queue. for About.BigEdit, they would be lost to About.Fragments unless I implemented some kludgy forwarding mechanism that seems not biologically plausible. Losing characters is bad when processes are trying to keep a model – even a shallow model – up to date.

The simple fact of the matter is that any decision-making (“control”) processes have to be more detached from the details of the environment: they need to consume generalizations from perceptual processes, not be so closely coupled to the environment.

Therefore, I resolved the next prototype would contain an “edge detector” that treated pairs of newlines as an edge between a gap and real text. (Your brain does something similar.)

In the real brain, the edge detector is constantly producing data that’s transmitted to control processes downstream of it. Or is it? Your visual focus is constantly bouncing around (“saccades”), so an “always on” edge detector is not continuously providing a summary to one process. Rather, I think of it as switching between different consumers depending on where the eye is focused. I decided to simulate that switching of attention by putting the edge detector on a timer. Every few seconds, it will inform its two downstream processes. One will look for a pattern that shows a Big Edit has started, the other for a fragment between gaps. These processes will run in parallel, rather than in sequence. They – and what actions they’ll cause – will be independent and non-interfering.

As it happens, I’ve already completed the full pathway from “perceptual system” to “control system” to “motor system” for the first of those processes. That’s the next writeup.