skip to content

On the control-versus-data-flow axis, what in a short code sample tells you which side a language sits on?

level: middleimportance: must knowfreq 55%

answer

  1. who fixes the order, text or dependencies
  2. swap two adjacent lines mentally
  3. what carries information between lines
  4. a place with a before and an after
  5. latitude to reorder, batch or skip

basics

~20 s

Whether the text fixes the order or only the dependencies. Control flow names the next step and relies on what the previous one left behind; data flow names which values feed which, leaving the evaluator free to order anything the dependencies do not constrain.

solid answer

~40 s

I apply the shuffle test to the sample: take two adjacent lines and ask whether swapping them changes the meaning. If it does, because the earlier line left something behind that the later one reads - an accumulator updated, a cursor advanced, a flag set - the program is specifying **control flow**. If it does not, because each line only defines a value in terms of other values it names, the program is specifying **data flow**, and an evaluator may run those two in either order, together, or not at all if nothing demands them. The tell is what carries information from one line to the next: an updated location means control flow, a value consumed by name means data flow.

code

pseudocode · 11 lines
pseudocode
// order fixed by the text
total = 0
for each line in order.lines
    total = total + line.price
shipping = rateFor(order.zone)
grandTotal = total + shipping

// order fixed only by dependencies
lineTotal  = sumOf(pricesOf(order.lines))
shipping   = rateFor(order.zone)
grandTotal = lineTotal + shipping

go deeper

for a junior

Know that some programs say do this, then that, while others say this value is computed from those values, and that only the second leaves an evaluator free to pick an order.

for a middle

Run the shuffle test out loud on a sample and name what carries information between two lines - an updated location or a value consumed by name - and say what latitude the second buys.

for a senior

Show what the latitude costs in a running system: timing, cost and the point at which a failure appears stop being readable from the text once the evaluator owns the order.

for a principal

Weigh a step-ordered codebase against a dependency-ordered one for work that must be scheduled, retried or distributed, and say what the team gives up in single-step debuggability either way.

## The two ways a program can say "next" Every program has an order in which things happen, but languages differ in **who decides it**. On one side the text decides: the program is a list of commands, and what makes the second command correct is the state the first one left behind. The order you read is the order that runs, and moving a line is a semantic change. On the other side the dependencies decide: the text says which values are computed from which, the evaluator derives an order from that, and any order consistent with the dependencies is equally valid. This is the second axis on the worksheet, and it is not the same question as the state axis. A language can be immutable-leaning and still fix every step's order; a language can allow writes everywhere and still let an evaluator reorder work those writes do not constrain. ## The shuffle test The fastest reading is mechanical. Take two adjacent lines of the sample and swap them: 1. **If the meaning changes and you can point at the thing that carried the difference** - a counter that had been incremented, a buffer that had been appended to, a position that had moved - the sample is specifying control flow. The lines communicate through a place, and a place has a before and an after. 2. **If the meaning does not change**, look at how the second line got its inputs. If it named them, and the names are defined by expressions rather than left behind by steps, the sample is specifying data flow. 3. **If the meaning changes but you cannot point at any value flowing between the lines**, look for an effect: something written outside the program's own values. Ordered effects put a sample on the control-flow side however expression-shaped its syntax looks. That last case is the one candidates miss. Expression syntax is not evidence of data flow; **effects are evidence of control flow**, because an effect's order is part of what the program means. ## What each side buys | | Order fixed by the text | Order fixed by dependencies | |---|---|---| | Who schedules | the author | the evaluator | | Reordering | a semantic change | legal wherever no dependency forbids it | | Work nothing needs | still runs | may be skipped | | Independent work | runs where written | may be run together | | Reading a line in isolation | needs the preceding steps | needs only the names it mentions | | Debugging | follow the sequence | follow the dependency chain backwards | The control-flow side buys a program you can single-step: the machine's behaviour and the text's shape match, and the cost model is right there in the order. The data-flow side buys **latitude**: because nothing but a dependency constrains the order, an evaluator may reorder, batch, cache, run independent work at the same time, or skip work whose result nothing demands. When reordering can never change the result, that property has a name - **confluence**, the Church-Rosser property - and it is what makes the latitude safe rather than merely convenient. The bill for that latitude is paid in predictability. When the evaluator owns the order, you can no longer read the order off the page, so cost, timing and the point at which a failure appears become properties of the evaluator's choices rather than of the text. ## What the axis does not decide - It does not decide performance. Latitude permits parallel or skipped work; it does not promise any, and an evaluator that schedules badly can be slower than the written order would have been. - It does not mean the data-flow side has no order. It has exactly the order its dependencies require and no more. - It is not an all-or-nothing score for a language. Most languages let you write both shapes, and the axis records which one the language's defaults and standard forms push you toward. - It is not the same reading as the state axis, though the two correlate: shared writes create ordering requirements that the text does not show, which is why a sample with heavy shared state rarely scores far toward the dependency side. ## Reading it in practice On an unfamiliar language, three signals settle the axis quickly: whether routines mostly return values or mostly perform effects; whether the common iteration form accumulates into a location or describes a transformation of a whole collection; and whether the language has any notion of an expression that is written but not yet run. Write the score down and move on - a language that scored toward dependencies here will make your existing step-ordered idioms feel wrong in exactly the places the axis predicts.

  • A sample is written entirely as expressions but each one writes to a log. Which side of the axis is it on?
    The control-flow side. The order of those writes is part of what the program means, so the evaluator has no latitude to reorder or skip them even though the syntax looks expression-shaped. Expression form is not evidence of data flow; observable effects are evidence against it.
  • What does a language give up by letting the evaluator choose the order?
    Readability of timing and cost. You can no longer tell from the page when a piece of work runs, how often, or whether it runs at all, so cost analysis and failure locations become properties of the evaluator rather than of the text. That is the price paid for reordering, batching and skipping.

saying these in an interview costs you the question

  • Says data flow simply means the language has no loops
  • Treats textual order as proof that the order is required
  • Calls any program written with expressions a data-flow program
  • Thinks dependency ordering lets the evaluator reorder dependent work
  • Assumes latitude to reorder always produces a speedup