Skip to content
academia.sh

Lesson 11 / 27

Iteration Constructs

The same ten resources are written in three record forms: ten blocks by hand, count-based iteration, set-based iteration. Line count, the cost of an exception, and address stability are counted; it measures that the count-based form produces a six-item plan when a subregion is removed from the middle, and that five shifted addresses correspond to thirty-five missed meter-reading batches on the measurement network.

Published Updated

In this lesson

  • Writes the same ten resources in three forms and counts the record's cost: lines, conditional branches, address readability
  • Compares the count-based and set-based forms' plan items when an element is removed from the middle
  • Counts the shifted address's counterpart on the measurement network as missed meter-reading batches
Contents

The previous lesson’s record had two collector machines, written separately as two blocks, four lines identical letter for letter. The region has grown: the measurement network now reads meters in ten separate subregions. Copying the same block ten times is one option; telling the record itself “do this ten times” is another. This lesson adds a third option and compares all three on the same items.

The comparison’s starting point is this: all three forms produce the same reality. Ten machines, the same subnets, the same sizes, the same tags. The difference between them is not in reality, it is in the record and in the address the record gives a resource. That address is the only thing that determines which real resource the record means on the next apply — and every number in this lesson comes from there.

The measurement runs on the regional measurement network and is fictional; what runs is a model built with node.

DP31. The measurement network reads meters in ten subregions; the subregion names are in the order written in the list. DP32. All three record forms define the same ten machines; their attributes are identical. DP33. The address is the key under which a resource sits in state: a block name in the manual form, an index in the count-based form, a map key in the set-based form. DP34. One subregion carries twice the meters and needs a large machine; this is the only deviation placed here to measure how the forms carry an exception. DP35. The subnet and tag fields cannot be updated in place; if they change, the resource is recreated. size updates in place. DP36. Every collector pulls a meter-reading batch every 15 seconds. A recreated machine, with the previous lesson’s durations, is out of service for 105 seconds.

The Same Ten Resources, Three Records

// iteration.mjs — ten resources written in three forms; lines, address stability, and the plan are counted. Model.
export const SUBREGION = ["north_1", "north_2", "north_3", "east_1", "east_2",
  "east_3", "west_1", "west_2", "west_3", "west_4"];
const EXCEPTION = { east_2: "large" };            // DP34: one subregion carries twice the meters
const attributes = (b, exc) => ({ subnet: `subnet.${b}`, size: exc[b] ?? "medium",
  tag: `collector-${b}` });

export function manual(subregions, exc = EXCEPTION) {
  const text = subregions.map((b) => `resource machine "collector_${b}" {\n` +
    `  subnet = subnet.${b}.id\n  size   = "${exc[b] ?? "medium"}"\n` +
    `  tag    = "collector-${b}"\n}`).join("\n");
  const resolved = new Map(subregions.map((b) => [`machine.collector_${b}`, attributes(b, exc)]));
  return { text, resolved, branches: 0 };               // value in its own block, no condition
}

export function countBased(subregions, exc = EXCEPTION) {
  const branches = Object.entries(exc).map(([k, v]) =>
    `subregions[count.index] == "${k}" ? "${v}" : `).join("");
  const text = `local subregions = [${subregions.map((b) => `"${b}"`).join(", ")}]\n` +
    `resource machine "collector" {\n  count  = ${subregions.length}\n` +
    `  subnet = subnet[subregions[count.index]].id\n  size   = ${branches}"medium"\n` +
    `  tag    = "collector-" + subregions[count.index]\n}`;
  const resolved = new Map(subregions.map((b, i) => [`machine.collector[${i}]`, attributes(b, exc)]));
  return { text, resolved, branches: Object.keys(exc).length };
}

export function setBased(subregions, exc = EXCEPTION) {
  const text = "local subregions = {\n" +
    subregions.map((b) => `  ${b} = "${exc[b] ?? "medium"}"`).join("\n") + "\n}\n" +
    `resource machine "collector" {\n  set    = subregions\n` +
    "  subnet = subnet[set.key].id\n  size   = set.value\n" +
    '  tag    = "collector-" + set.key\n}';
  const resolved = new Map(subregions.map((b) => [`machine.collector["${b}"]`, attributes(b, exc)]));
  return { text, resolved, branches: 0 };               // exception lives in the map's value
}

const FORCES_RECREATE = ["subnet", "tag"];            // DP35: these fields cannot update in place

export function plan(before, after) {
  const p = { add: 0, update: 0, recreate: 0, delete: 0 };
  for (const [address, y] of after.resolved) {
    const e = before.resolved.get(address);
    if (!e) { p.add++; continue; }
    const diff = Object.keys(y).filter((k) => e[k] !== y[k]);
    if (diff.length) diff.some((k) => FORCES_RECREATE.includes(k)) ? p.recreate++ : p.update++;
  }
  for (const address of before.resolved.keys()) if (!after.resolved.has(address)) p.delete++;
  return p;
}

export const FORM = [["manual, ten blocks", manual], ["count-based", countBased],
  ["set-based", setBased]];
const lineCount = (m) => m.split("\n").length;
const THREE = { east_2: "large", west_1: "large", north_3: "small" };

if (import.meta.url === `file://${process.argv[1]}`) {
  // Do all three forms produce the same ten real resources: compare attributes, not the address.
  const signature = (c) => [...c.values()].map((v) => JSON.stringify(v)).sort().join("|");
  const signatures = FORM.map(([, f]) => signature(f(SUBREGION).resolved));
  console.log("do all three forms produce the same reality: " +
    `${signatures.every((x) => x === signatures[0]) ? "yes" : "no"} ` +
    `(${manual(SUBREGION).resolved.size} resources, same attributes)`);

  console.log("\nthe record's cost (10 resources)");
  console.log(`${"form".padEnd(21)}${"lines".padStart(7)}${"cond branch".padStart(13)}${"lines/branch at 3 exc".padStart(24)}${"subregion in address".padStart(23)}`);
  for (const [name, f] of FORM) {
    const r = f(SUBREGION), u = f(SUBREGION, THREE);
    const addressed = [...r.resolved.keys()].some((a) => a.includes("east_2"));
    console.log(`${name.padEnd(21)}${String(lineCount(r.text)).padStart(7)}${String(r.branches).padStart(13)}` +
      `${`${lineCount(u.text)}/${u.branches}`.padStart(24)}${(addressed ? "yes" : "no").padStart(23)}`);
  }

  const SCENARIO = [
    ["a subregion drops out of the middle (east_2 closes)", SUBREGION.filter((b) => b !== "east_2")],
    ["a subregion is inserted in the middle (east_1b opens)", [...SUBREGION.slice(0, 4), "east_1b", ...SUBREGION.slice(4)]],
    ["a subregion is appended at the end (west_5 opens)", [...SUBREGION, "west_5"]],
  ];

  for (const [name, next] of SCENARIO) {
    console.log(`\n${name}`);
    console.log(`${"form".padEnd(21)}${"add".padStart(5)}${"update".padStart(8)}${"recreate".padStart(10)}${"delete".padStart(8)}${"total items".padStart(13)}${"shifted address".padStart(18)}`);
    for (const [formName, f] of FORM) {
      const before = f(SUBREGION), after = f(next), p = plan(before, after);
      const shifted = [...after.resolved].filter(([a, v]) =>
        before.resolved.has(a) && JSON.stringify(before.resolved.get(a)) !== JSON.stringify(v)).length;
      console.log(`${formName.padEnd(21)}${String(p.add).padStart(5)}${String(p.update).padStart(8)}` +
        `${String(p.recreate).padStart(10)}${String(p.delete).padStart(8)}` +
        `${String(p.add + p.update + p.recreate + p.delete).padStart(13)}${String(shifted).padStart(18)}`);
    }
  }
}
do all three forms produce the same reality: yes (10 resources, same attributes)

the record's cost (10 resources)
form                   lines  cond branch   lines/branch at 3 exc   subregion in address
manual, ten blocks        50            0                    50/0                    yes
count-based                7            1                     7/3                     no
set-based                 18            0                    18/0                    yes

a subregion drops out of the middle (east_2 closes)
form                   add  update  recreate  delete  total items   shifted address
manual, ten blocks       0       0         0       1            1                 0
count-based              0       0         5       1            6                 5
set-based                0       0         0       1            1                 0

a subregion is inserted in the middle (east_1b opens)
form                   add  update  recreate  delete  total items   shifted address
manual, ten blocks       1       0         0       0            1                 0
count-based              1       0         6       0            7                 6
set-based                1       0         0       0            1                 0

a subregion is appended at the end (west_5 opens)
form                   add  update  recreate  delete  total items   shifted address
manual, ten blocks       1       0         0       0            1                 0
count-based              1       0         0       0            1                 0
set-based                1       0         0       0            1                 0

The first line is the comparison’s foundation: all three forms produce the ten resources with the same attributes. The record’s form does not change reality.

The lines column gives the first cost. The manual form is 50 lines, set-based is 18, count-based is 7. 40 of the manual form’s 50 lines are repetition: the same three fields ten times, only the subregion name changing. The count-based form is shortest because it packs the list into a single line. The set-based form sits in between, and its extra length is not an accident — every line of the map writes a subregion by its name and its value.

The conditional branch column gives the exception’s price. A subregion wanting a large machine is, in the manual form, a value written on that block’s own line; in the set-based form, the value at that key in the map; both carry zero conditions. In the count-based form, though, a branch enters the expression. Looking from the last column to the one before it shows how this grows: when the exception count rises to three, the line and branch counts do not change in the manual and set-based forms, but in the count-based form, three nested conditions pile up in a single line. In the count-based form, the place that carries the exception is not the record’s data, it is its expression.

The last column, though, is this lesson’s real item. In the manual and set-based forms, the address carries the subregion name: machine.collector_east_2 or machine.collector["east_2"]. In the count-based form, the address is machine.collector[4], and which subregion the fourth position belongs to is not written in the address — that information lives in the list’s state at apply time. To look at a resource in state and say which subregion it belongs to, you have to find that day’s version of the record.

What the address is looks like a detail, but in this course’s measure it is a central item. When the record is applied, every resource’s real identity is written under an address; on the next apply, that address is the only thing that matches the record to reality. If the address carries the resource’s identity, the match can be read from the record itself; if it carries an index, the match depends on the record’s order that day. In the second case, the link between record and reality rests on something not written in the record at all — the list’s past state.

When an Element Drops Out of the Middle

Three scenarios are subsections of the same output, and reading them together makes the rule plain.

When subregion east_2 closes, the manual and set-based forms produce a single-item plan: one resource is deleted, the other nine stay in place untouched. The count-based form produces six items for the same real change: five recreations and one delete. The reason is arithmetic. When the list loses its fourth element, the fifth element shifts to the fourth position, the sixth to the fifth; the address machine.collector[4] pointed to east_2 yesterday, today it points to east_3. The record says “the fourth machine’s subnet and tag changed”; since these two fields cannot be updated in place, the decision is recreation. The shifted address column counts this directly: five addresses now point to a different real resource.

Inserting a subregion in the middle is even more expensive: seven items, six shifted addresses. Every address after the insertion point shifts by one.

The third scenario draws the boundary. When a subregion is appended to the end of the list, all three forms produce a single item; the count-based form is clean too, because no address shifts. So count-based iteration is not wrong — it is the correct form for sets whose order carries no meaning and that only grow from the end. The subregion list is not that kind of set: subregions close, and new ones get inserted in the middle.

Here, the plan’s predictive accuracy asks an inverse question. The plan is not wrong — in the count-based form it says “five resources will be recreated,” and apply recreates exactly five, a ratio of 5/5. What is wrong is not the plan, it is the expectation of the person reading it: one subregion closed, one machine should be gone. The expectation is one item, the plan is six. The five items in between are born not from reality but from the record’s form.

That the record cannot tell this apart on its own is also a measurable limit. The record cannot see the difference between “the fifth machine’s subnet changed” and “position five is now a different subregion,” because all it has is an address and attributes. Both look like the same thing: an address, changed attributes. Only the person who wrote the record knows the difference, and even that person might mistake the five items for a real change while reading the plan. The count-based form’s risk is not hidden — it is written plainly in the plan — but telling apart the plan’s six items from reality’s one item is left to the reader.

The Shifted Address’s Counterpart on the Measurement Network

// cost.mjs — counts a plan item in the measurement network's own unit: missed meter-reading batches. Model.
import { SUBREGION, FORM, plan } from "./iteration.mjs";
const OUTAGE = 105, PERIOD = 15, RECREATE_S = 110, DELETE_S = 10;   // DP36, previous lesson's durations
const next = SUBREGION.filter((b) => b !== "east_2");

console.log("east_2 closed — same real change, three record forms");
console.log(`${"form".padEnd(21)}${"recreate".padStart(9)}${"out-of-service machine-s".padStart(27)}${"missed batch".padStart(15)}${"apply s".padStart(10)}`);
for (const [name, f] of FORM) {
  const p = plan(f(SUBREGION), f(next));
  const s = p.recreate * OUTAGE;
  console.log(`${name.padEnd(21)}${String(p.recreate).padStart(9)}${String(s).padStart(27)}` +
    `${String(p.recreate * Math.floor(OUTAGE / PERIOD)).padStart(15)}` +
    `${String(p.recreate * RECREATE_S + p.delete * DELETE_S).padStart(10)}`);
}
console.log("\nthe closing subregion's own batches were never going to be collected anyway;");
console.log("the loss above belongs to the nine sound subregions.");
east_2 closed — same real change, three record forms
form                  recreate   out-of-service machine-s   missed batch   apply s
manual, ten blocks           0                          0              0        10
count-based                  5                        525             35       560
set-based                    0                          0              0        10

the closing subregion's own batches were never going to be collected anyway;
the loss above belongs to the nine sound subregions.

When the numbers are read in the measurement network’s unit, the decision becomes clear. The closing subregion’s machine was going away regardless; the loss above belongs to the nine subregions standing sound. In the count-based form, five sound machines each go out of service for 105 seconds: 525 machine-seconds and 35 missed meter-reading batches. In the manual and set-based forms, these items are zero. Apply time is 560 seconds against 10.

A missed batch does not come back. The meters keep reading, but because nobody pulls during that window, a 105-second gap opens in those five subregions of the measurement network — and there is no trace of that gap anywhere in the record. The record is correct, the plan is correct, apply is correct; the only thing lost is data.

This section should close by naming an item the record does not cover: the subregion list itself. Ten subregion names are written in the record, but which subregion has meters is not something the record knows — it was hand-copied from a list the field team keeps. When a subregion closes, the record does not learn this on its own; someone goes and deletes it from the list. The trigger for this change — the record’s most expensive item — sits entirely outside the record.

The Record Form Decision

Form Lines (10 resources) Items on removal from the middle Shifted address Exception branch When
Manual, ten blocks 50 1 0 0 Resources genuinely differ from one another
Count-based 7 6 5 1 per exception Order is meaningless, a set that only grows from the end
Set-based 18 1 0 0 A set whose elements have identity

The table is summarized by a single measure: the price of saving lines is address stability. The count-based form brings the manual form’s 50 lines down to 7 — sevenfold — and in return produces five to six needless recreations on every change in the middle. The set-based form comes down to a third of the manual form at 18 lines and never loses address stability; the price it pays is that subregion names get written a second time in the record, as map keys.

The decision is not between three forms, it is between two questions. First: are these ten resources copies of one another, or does each have its own identity? On the measurement network, the second is true — east_2 is not an index, it is a subregion standing in the field. Second: does this set change from the middle? The subregion list changes through closings and openings, so it does. The two answers together point to the set-based form; the count-based form is not cheap enough here to make its 7 lines worth choosing over 18.

Summary

  • All three record forms produce the same 10 real resources with the same attributes; the difference is not in reality, it is in the address the record gives a resource.
  • Lines: manual 50, set-based 18, count-based 7. When the exception count rises to 3, the count-based form piles up 3 nested conditions in a single expression, while the branch count stays 0 in the other two.
  • When east_2 closed, the manual and set-based forms produced 1 item, the count-based form produced 6; 5 addresses shifted to a different real resource. Inserting a subregion in the middle came to 1 versus 7 and 6.
  • Appending at the end came to 1 item in all three forms: count-based iteration is the right form for sets whose order carries no meaning and that only grow from the end.
  • The plan was not wrong — it said “5 will be recreated” and 5 were recreated, a ratio of 5/5. The 5-item gap between expectation and plan was born from the record’s form.
  • In the measurement network’s unit: 5 sound machines out of service for 105 seconds each, 525 machine-seconds, 35 missed meter-reading batches, a 560-second apply — in the set-based form, all of that is 0 and 10 seconds.

Next Step

This lesson counted plan items but always took the plan as a given: the items came from somewhere. The next lesson builds that somewhere — it writes the planner itself, the thing that compares the record against state and produces an add, update, recreate, or delete decision item by item. What gets measured is the plan’s own accuracy: how many items are not in the plan but happen at apply, how many items are in the plan but do not happen, and how many items marked “will be recreated” are actually recreated.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close