TLDR
- An algorithm can be analysed as logic plus control. Logic states the problem knowledge; control decides how to use it.
- Meaning and behaviour are different. Several sound control regimes can preserve answers while changing time, space and order.
- Representation still matters. A clearer or stronger logic component can outweigh any improvement in search control.
- Horn clauses admit two readings. They are logical implications and, under top-down use, procedures with flexible call order and modes.
- The separation is an engineering discipline, not an identity of nature. Kowalski says the boundary is useful but not wholly unambiguous.
The equation is a lens, not a claim that programmes arrive in two boxes
Kowalski’s compact equation, A = L + C, proposes a way to analyse an algorithm. The logic component L specifies the knowledge that may be used in solving a problem. The control component C specifies the problem-solving strategy by which that knowledge is used.
The distinction begins with meaning. L says which relationships hold and what counts as a solution. C decides such matters as whether reasoning proceeds from data towards consequences or from a goal towards subgoals, which pending call is handled next, whether alternatives are explored sequentially or together, and how relational data are stored and retrieved. On Kowalski’s first approximation, admissible changes to C alter behaviour and efficiency but do not alter the problems solved or the answers returned.
That qualification matters. The paper does not say control is irrelevant, nor that logic alone is an executable algorithm. A correct declarative relation can be paired with control that repeats work explosively, consumes unnecessary memory or never reaches an existing answer. Conversely, good control cannot turn a poor representation into an efficient method in every case. Both sides affect performance, but they affect it in different ways.
The motivating promise is replaceability. If A₁ = L + C₁ and A₂ = L + C₂, then, under meaning-preserving control, A₁ and A₂ are equivalent in result while possibly very different in cost. That creates a clean target for optimisation: improve C while holding L fixed. It also creates a clean target for reasoning: establish what L means without first committing to every scheduling choice.
Kowalski is careful not to reify the boundary. The same behaviour can often be analysed as L₁ + C₁ or L₂ + C₂. Work embedded in a more elaborate logical representation can be shifted into a more capable interpreter, and work performed by control can instead be compiled into the clauses. The equation is therefore a disciplined decomposition chosen for explanatory and engineering value.
Worked example 1 · one factorial definition, two directions
The clauses “factorial 0 is 1” and “factorial x+1 is (x+1) times factorial x” can generate 0!, 1!, 2! and so on from the base case. That is bottom-up use and behaves like iteration. The same clauses can reduce the request for 6! to a request for 5!, then 4!, down to the base case. That is top-down use and behaves like recursive evaluation. The mathematical relation is unchanged; the direction of activity changes.
Causal probe 1 · freeze L, vary C
If an optimisation changes which inputs have answers, it did not merely improve control under Kowalski’s first approximation. It changed the effective meaning, lost completeness, admitted an unsound result or exposed a termination difference. Testing only speed cannot establish that C preserved L.
Logic supplies abstract definitions and data relations; control supplies motion
The paper expands its equation into an inventory. The logic component contains abstract procedure definitions and definitions of data structures. The control component contains direction, scheduling of procedure calls, investigation of alternative procedures, and schemes for storing relational data.
This inventory blocks a common misreading. “Logic” does not mean a pile of facts with no structure. It includes recursively defined relations, abstractions such as sorting, and representations of data. Nor does “control” mean a single program counter. It includes forward versus backward reasoning, sequential versus parallel calls, depth-first versus breadth-first exploration, indexing and retrieval. An interpreter can own some of these choices; an advanced programmer can provide additional control information in a separate language.
The distinction also locates responsibility. Domain-specific knowledge belongs primarily in L. General and problem-specific problem-solving strategies belong in C, provided they preserve meaning. At a high level of programming, the system should assume more responsibility for control. Kowalski uses database query systems as evidence: the user states a relational query, while the system chooses storage and retrieval schemes.
Control may still use domain knowledge. A programmer can state that y is functionally determined by x in F(x,y), allowing a backtracking engine to avoid seeking a second F answer after a later goal fails. A diagnosis system can state that procedure Q is more likely than R to explain symptom P, changing search order. These facts guide effort without becoming new object-level solutions: if they are correct and the interpreter honours them soundly.
The resulting architecture is asymmetric. L must be specified before C in Kowalski’s programme of successive refinement. Control is subordinate to meaning: it may choose how the relation is realised, but it is not entitled to rewrite the relation silently. That asymmetry is the reason the split can support both correctness arguments and performance work.
Worked example 2 · a functional dependency as control information
Suppose F(A,y) can return at most one y, followed by G(y). If G fails, a generic backtracker might ask F for another y. The dependency tells it not to waste that search. The allowed F pairs have not changed; only the investigation strategy has.
Causal probe 2 · corrupt the hint
If F actually has two outputs for A, declaring it functional can suppress a valid second answer. The optimisation then changes observable meaning. The example exposes a permanent engineering obligation: control annotations require their own correctness case.
Horn clauses make the split inspectable
Kowalski chooses clausal predicate logic because one notation can carry a declarative reading and support several operational readings. He narrows attention to Horn clauses: clauses with at most one conclusion: because their form resembles procedures without surrendering their logical interpretation.
An assertion has a conclusion and no conditions. A procedure declaration has one conclusion B and zero or more conditions A₁…Aₙ. A denial has conditions but no conclusion and represents a problem to be refuted. The empty clause is contradiction. Variables are implicitly universal within each clause; variables in different clauses remain distinct even if printed with the same letter.
The logical reading of “Parent(x,y) if Father(x,y)” states a relation. The recursive pair for Ancestor states that a parent is an ancestor and that ancestry composes through an intermediate individual. None of this commits the system to left-to-right calls, depth-first search or a particular index. Those are additions made by control.
Problems are expressed as denials. To ask whether Zeus is a grandparent of Harmonia, deny that relation and seek a refutation. If the denial contains variables, a successful refutation returns substitutions that witness answers. Thus a query for descendants of Zeus can produce Ares, Harmonia and Dionisius rather than merely a Boolean.
Horn form is expressive enough for the paper’s procedural argument, but Kowalski does not pretend it is always the most natural surface form. His subset example shows that converting a compact quantified definition into clauses can obscure its meaning. The restriction is methodological: study the part of logic whose declarative and procedural structures line up most clearly.
procedure declaration: B ← A₁, …, Aₙ
denial / goal: ← B₁, …, Bₘ
contradiction: ←
Worked example 3 · recovering a witness
Invoke Grandparent(Zeus,y). The rule introduces z and reduces the request to Parent(Zeus,z) and Parent(z,y). The fact Parent(Zeus,Ares) binds z to Ares; Parent(Ares,Harmonia) then binds y to Harmonia. The answer substitution is not an extra convention pasted onto logic: it is recovered from the refutation.
Causal probe 3 · reverse the conditions
Parent(z,y), Parent(Zeus,z) is logically the same conjunction. Operationally, if y is unbound and the database is large, beginning with Parent(z,y) may enumerate almost every parent pair. The result relation is unchanged, but a textual engine with fixed left-to-right control can make equivalent clause bodies behave very differently.
A definition may propagate from evidence or pull backwards from a goal
The same family clauses solve the grandparent problem in two directions. Bottom-up reasoning begins with assertions and derives consequences until the goal appears. Top-down reasoning begins with the goal and reduces it to subgoals until assertions discharge them.
Bottom-up begins from Father(Zeus,Ares) and Father(Ares,Harmonia). Parent rules derive Parent(Zeus,Ares) and Parent(Ares,Harmonia). The grandparent rule then derives Grandparent(Zeus,Harmonia). Processing moves from available data towards the requested consequence. It may also derive facts irrelevant to the particular request, such as Male(Zeus), because forward closure is not automatically goal-directed.
Top-down begins from Grandparent(Zeus,Harmonia). The grandparent rule replaces that goal with Parent(Zeus,z) and Parent(z,Harmonia). The parent rules reduce those subgoals to father or mother alternatives; compatible facts bind z to Ares and close the proof. Processing moves from the requested consequence towards supporting data. It can still make irrelevant choices: for example, trying Mother(Zeus,z) before Father(Zeus,z).
Direction is control because the clauses do not nominate a starting end. Top-down proof procedures include model-elimination and linear-resolution families; bottom-up procedures include hyper-resolution. Kowalski also points to connection graphs and natural-deduction systems that can mix directions. This is not a choice between logic and something non-logical. Both are deduction, organised differently.
Direction has characteristic failure modes. Forward reasoning can materialise a broad closure that a narrow query never needs. Backward reasoning can revisit the same subproblem or follow an unproductive branch. Neither direction is universally superior. The purpose of separation is to permit the choice to respond to query shape, data distribution and available execution machinery.
Worked example 4 · forward closure for many questions
If thousands of later queries ask about many ancestors, computing and indexing a useful closure can amortise the work. For one isolated grandparent query, that same closure may be wasteful. “Top-down is faster” is therefore not the thesis. The thesis is that direction belongs to C and can be selected without rewriting what Parent or Grandparent means.
Causal probe 4 · add an irrelevant family branch
Add a hundred parent facts unrelated to Zeus. A naïve bottom-up evaluator may derive their consequences; a goal-directed evaluator need not touch them. Add instead a cyclic ancestor rule and repeated subgoals: a naïve top-down evaluator may loop or duplicate work while a tabulating or bottom-up method terminates. Data and rule shape cause the ranking to reverse.
A top-down Horn clause is stranger: and more flexible: than a conventional procedure
Under the procedural interpretation, the conclusion names the problems a clause can solve and the body is a set of procedure calls. Invoking a clause means matching a goal with its conclusion, replacing the goal by the body, and applying the matching substitution.
The substitution transmits information in both directions. Matching Grandparent(Zeus,Harmonia) with Grandparent(x,y) binds x and y, sending input to new parent calls. Matching Father(Zeus,z) with the fact Father(Zeus,Ares) binds z, sending output back to the remaining goal. A single invocation can transmit both input and output.
This differs from conventional procedure calls in three ways emphasised by Kowalski. First, a clause body is logically a set or conjunction, not a compulsory sequence; calls may run in any order or in parallel. Secondly, several clauses may have conclusions matching the same call, so choosing a clause is a search problem. Thirdly, argument modes are not fixed once and for all. The same relation can test a pair, find an output from an input, or search for inputs consistent with an output.
Those freedoms are precisely the control surface. A concrete language such as Prolog usually commits to a more particular regime: textual clause order, left-to-right subgoals and depth-first backtracking. Such a regime is one C, not the procedural meaning of Horn clauses in full. Treating it as inevitable collapses the analytical separation.
Flexibility does not remove operational preconditions. A relation can be mathematically symmetric while a chosen implementation expects one argument to be sufficiently instantiated. A reorder can move an expensive generator before a selective test. Parallel calls can require synchronisation on shared variables. The logic tells us which joint assignments satisfy the clause; control must realise them without losing or inventing answers.
Worked example 5 · one relation, several modes
Parent(Zeus,Ares) tests a ground pair. Parent(Zeus,y) enumerates Zeus’s children. Parent(x,Ares) finds Ares’s parents. Parent(x,y) enumerates the whole relation. A conventional function signature would normally privilege one of these directions; the relation itself does not.
Causal probe 5 · confuse a set with an execution list
If the body is treated as an immutable sequence, swapping two conjuncts looks like a programme change rather than a control change. Restoring the logical conjunction exposes what must be invariant: the joint relation: and what may vary: the schedule used to establish it.
Changing logic can matter more than polishing control
The equation does not make L a frozen ideal. Kowalski contrasts two logical accounts of sorting to show that representation can dominate the effect of a control strategy.
The direct definition says that sorting x gives y when y is a permutation of x and y is ordered. It is transparent as a specification. Yet ordinary ways of controlling those two relations generate permutations and test them, or interleave generation with orderedness checks. Better scheduling prunes some work, but the representation still exposes an enormous search space.
A quicksort-style logical definition introduces partitioning around the first element, recursively sorting the two partitions and appending the results. Even simple top-down sequential control now behaves efficiently compared with permutation generation. The improvement came from L: the relation was decomposed using stronger problem knowledge. The result relation: ordered permutations: remains the intended one, but the logical route is more informative.
This supports a two-stage discipline. Specify and refine a logic component that says clearly what is to be done and captures useful domain structure. Then improve its control. Some programme transformations can derive a new, more efficient L by deduction; other optimisations leave L fixed and change C. Calling both “optimisation” should not hide the distinction.
The boundary remains movable. A sophisticated interpreter might recover useful partitioning or constraints from a more abstract specification, shifting work back into C. A simple interpreter may require the programmer to encode more of that knowledge in L. Kowalski’s long-term preference is for clear logic paired with powerful problem-solving machinery, not for deliberate representational weakness.
Worked example 6 · pruning is not partition knowledge
When constructing a candidate permutation left to right, an orderedness test can reject [3,1,…] early. That is useful control. It still begins inside the permutation representation. Quicksort’s logic states a structural fact: every output element belongs on one side of a pivot. That knowledge prevents most candidates from arising at all.
Causal probe 6 · compare only runtimes
If two versions differ in both L and C, a faster result cannot be credited to “better search” alone. Hold L fixed to test a control claim; hold C fixed to test a representation claim. Kowalski’s decomposition turns an undifferentiated benchmark win into a causal experiment.
Abstract relations can outlive their storage representation
Kowalski places definitions of data structures inside L, yet argues that higher-level procedures should be separated from those definitions. The point is not to abolish representation but to make the dependency explicit and replaceable.
A list can be represented by terms: nil names the empty list and cons(x,y) names a list whose first element is x and whose rest is y. Higher-level sorting can interact with this representation through relations such as empty, first and rest. Those interface predicates state what the structure provides without forcing every sorting clause to expose cons cells.
A preliminary deduction can eliminate the interface predicates and specialise the abstract quicksort definition into clauses that pattern-match directly on nil and cons. The specialised form avoids interface calls at runtime but mixes procedure and representation. Kowalski connects this to macro expansion: flatten the abstraction before execution, much as a theorem prover or programme transformer can derive a lower-level logic component.
Separation brings two benefits. The list representation can be replaced without rewriting higher-level sorting, and well-named interface relations make the programme largely self-documenting. Mixing representation into every procedure may gain speed, but it transfers documentation outside the programme and increases the cost of change.
This is why Kowalski’s terminology deliberately conflicts with Wirth’s “program = algorithm + data structure”. In A = L + C, data-structure definitions belong to L. The control side manages the scheme by which relational data are stored and retrieved. A relation such as Parent can be presented independently of whether tuples live in an array, hash table, tree or semantic network.
Worked example 7 · swap the list representation
An abstract partition relation consumes “first” and “rest” observations. A linked list, an array slice or a stream can implement those observations differently. If sorting clauses mention cons throughout, the representation leaks into every layer. With an interface, a transformer can later specialise the chosen representation while the higher-level account remains stable.
Causal probe 7 · inline before understanding
Inlining may remove call overhead, yet it also erases the boundary that states what the representation is for. Measure the specialised artefact, but preserve the abstract relation as the correctness reference. Otherwise an optimisation changes the only readable statement of intent.
Sequence, coroutining and parallelism are different controls over one conjunction
A clause body does not logically prescribe an execution order. Kowalski uses sorting and tree-leaf comparison to show how alternative schedules can preserve the joint condition while changing when failure is discovered and how much intermediate data are built.
For “y is a permutation of x, and y is ordered”, sequential execution can generate a complete permutation before checking order. Coroutining can suspend generation after each new element and ask whether the partial sequence remains ordered. Invalid prefixes are abandoned early. The two calls still define the same acceptable y values; their communication schedule changes.
Quicksort exposes pipeline parallelism. Partitioning can begin as input elements arrive. Recursive sorts can begin on emerging partitions, and append can begin as sorted output becomes available. The clause lists the joint relations. Control can realise them as a staged sequence, coroutines or communicating processes.
The tree example makes memory visible. To test whether two trees have the same leaf list, a sequential strategy constructs both complete lists and compares them. A communicating strategy lets each traversal emit one leaf, compares the pair immediately and stops at the first mismatch. When the trees disagree early, it avoids most traversal; even when they agree, it need not retain the consumed prefixes.
Kowalski’s claim that parallel top-down subproblems “seems never” to decrease efficiency belongs to the paper’s 1979 framing, not a universal modern cost theorem. Real parallel systems pay scheduling, communication and synchronisation costs. The durable insight is analytical: the conjunction defines compatibility, while the execution regime determines materialisation, latency and resource use.
Worked example 8 · mismatch on the third leaf
Let X emit [a,b,c,d,…] and Y emit [a,b,x,d,…]. Sequential construction does all work needed to build both lists before the comparison reaches position three. A lockstep schedule retains only the current pair, accepts a and b, then rejects c versus x. The equality relation over leaf sequences is unchanged.
Causal probe 8 · include coordination cost
If every emitted leaf crosses a slow process boundary, a small matching pair of trees may run faster sequentially. That does not refute the split. It shows that C’s cost model must include communication, not merely logical inference steps.
Fibonacci breaks the habit of equating recursion with top-down execution
Mathematicians often read recursive definitions bottom-up, generating a sequence from base cases. Conventional implementations encouraged programmers to read them top-down. Kowalski’s Fibonacci example shows why the execution direction should remain a choice.
With two base assertions and the relation Fib(u+2)=Fib(u+1)+Fib(u), a naïve top-down request for Fib(n) expands into requests for Fib(n−1) and Fib(n−2). Those subtrees overlap: Fib(n−2) is solved independently inside both branches, and the duplication compounds. The number of calls grows exponentially with n, while the stack grows with n.
Bottom-up execution begins with the two base values and generates each later value once. It performs linear work. With an appropriate deletion policy it needs to retain only the previous two assertions, using constant working storage. Here the supposedly “declarative” direction looks like the familiar iterative algorithm.
The comparison is not a timeless verdict against top-down reasoning. Kowalski notes that its disadvantage disappears when repeated calls are solved only once: a strategy related in the paper to Warren’s generalisation of Earley parsing. Today one might call the idea memoisation or tabling. That is another C over the same recursive L: demand-driven like top-down evaluation, but sharing answers rather than rebuilding subtrees.
The lesson is causal. The recursive definition does not force exponential work. A particular control regime causes repeated evaluation. Replacing the definition with an imperative loop can fix the symptom, but it also hides that the useful transformation was a change in direction or sharing.
Worked example 9 · three controls over Fib(6)
Naïve backward recursion repeatedly solves Fib(4), Fib(3) and smaller calls. Forward execution derives F2 through F6 once. A tabled backward evaluator asks only for F6 but stores completed subanswers and reuses them. All return the same value, 13 under the paper’s base convention F0=1 and F1=1; their work graphs differ.
Causal probe 9 · add sharing without changing the clauses
If memoisation turns the top-down tree into a directed acyclic graph, the asymptotic gap nearly vanishes while L remains fixed. That intervention identifies duplicated subproblem control: not recursion: as the source of the original explosion.
Search strategy completes the algorithm: and exposes the boundary’s mobility
When several clauses match a call, logic does not say which alternative to try first. Kowalski treats sequential investigation as search through nondeterministic alternatives, then uses path finding to show how direction and representation interact.
A grandchild query may try parent facts one at a time, nesting one loop of alternatives inside another and leaving as soon as it finds a witness. In a conventional programme that looks like iteration over an array. In a relational account it is depth-first backtracking over matching assertions. Breadth-first, parallel and indexed searches are other controls over the same facts.
In the first path representation, Go(x) means that x is reachable from a fixed initial node. Arcs are clauses such as Go(B) if Go(A). Bottom-up reasoning from Go(A) is forward search. Top-down reasoning from the goal Go(Z) is backward search. Combining them yields bidirectional search; depth-first versus breadth-first is the strategy for investigating alternatives.
In the second representation, Go*(x,y) means that y is reachable from x, and transitivity is a clause connecting x to z and z to y. A query Go*(A,Z) can still move forward by solving the first subgoal first, backward by solving the second first, or bidirectionally by coroutining them. What counted as inference direction in the one-place representation partly becomes subgoal scheduling in the two-place representation. The same behaviour has different L/C analyses.
The paper closes its technical development with arrows attached to atoms. Arrows state where processing activity can flow: conditions-to-conclusion for top-down use, conclusion-to-conditions for bottom-up use, with numeric annotations for sequencing. The notation can drive a connection-graph prover, but Kowalski marks its limit. Efficient grandparent query order depends on which argument is bound, and a static arrow cannot express that input-sensitive choice. A useful decomposition must admit that its control language is incomplete.
Worked example 10 · input-sensitive join order
For “names of suppliers of pens”, finding Part rows for pens first can sharply restrict Supply and Supplier. For “names of parts supplied by Jones”, finding Jones’s Supplier row first is selective. The relational conjunction is the same kind of object, but the best first call depends on which constant appears in the query. Static arrows cannot encode the general decision.
Causal probe 10 · move behaviour across the equals sign
Represent reachability with Go(x) and forward versus backward is inference direction. Represent it with Go*(x,y) plus transitivity and the same movement can be selected by subgoal order. If a performance explanation changes when the representation changes, name both L and C; neither label alone identifies the cause.
Hold the graph fixed; change only the frontier discipline
The laboratory fixes a reachability relation and runs three complete controls: depth-first, breadth-first and goal-directed best-first search. Each must agree with exhaustive closure about whether the target is reachable, but its visited order, witness path and amount of work may differ.
The graph is L. Its directed edges determine which paths exist. The start and goal form the problem. The frontier policy is C. Depth-first commits to the earliest open branch, breadth-first expands by distance from the start, and best-first ranks open nodes by their geometric distance to the displayed goal. Duplicate suppression is shared so every strategy terminates on this finite graph.
The three exact checks separate semantics from behaviour. First, the reported reachability must match a control-independent exhaustive closure. Secondly, any returned witness must begin at the start, end at the goal and traverse only declared edges; an unreachable result must return no witness. Thirdly, the reported expansion count must equal a duplicate-free visited trace. These checks do not assert that the controls have equal cost. They assert that cost differences occur inside a preserved relation.
Reachability under three controls
How to read the artefact
Node colour records operational attention, not truth. A node can be reachable even if one strategy stops after finding the goal before visiting it. The green path is a witness, not the complete meaning of the graph. The logical answer is the invariant checked against full closure.
Meaning preservation is a condition to prove, not a benefit that arrives automatically
A slogan can make control sound harmless. The paper’s argument is stronger when read conditionally: improve C without changing L only when the chosen strategy remains correct for the intended relation.
Search order can interact with termination. A depth-first path may descend indefinitely while another branch contains a finite proof. A fair breadth-first procedure may find the proof. Both operate over the same clauses, yet the first may produce no observable answer. For partial computations, “same meaning” requires a semantic account broader than immediate runtime behaviour and an explicit completeness or fairness premise.
Pruning can change the answer set. A functional-dependency annotation, a cut, a beam width or an early stopping rule may discard alternatives. If the discarded branches cannot contribute new valid answers, the pruning is meaning-preserving; otherwise C has reduced completeness. Later logic-programming work makes this obligation explicit by proving correctness and completeness separately and checking that pruning preserves both for the chosen specification.
Modes and floundering complicate the boundary. A relation may support several logical directions, but a concrete implementation can delay calls until arguments are instantiated. Some schedules terminate, others suspend forever. Control still explains the difference, but the engineering claim needs a declared query class and operational preconditions.
Cost itself can leak into meaning when resources are bounded. A system killed after one second does not return the relation’s full answer set. Parallelism can add races or non-deterministic side effects that are absent from pure Horn clauses. Kowalski’s clean separation is most exact for logical, side-effect-free definitions paired with sound problem-solving strategies. Extending it to real systems requires naming resource limits and effects.
The paper also says the L/C division is not wholly unambiguous. A heuristic can be represented as meta-level control or compiled into object-level predicates. An index may look like storage control or a materialised logical relation. The test is not whether every feature has one metaphysical home. It is whether the chosen boundary makes invariants, transformations and obligations easier to see.
| Intervention | Intended control benefit | Meaning-preservation obligation |
|---|---|---|
| Reorder subgoals | Run selective or instantiated calls first | Retain all compatible solutions and avoid new nontermination for the stated query class |
| Prune alternatives | Avoid redundant branches | Prove removed branches cannot supply required answers |
| Parallelise calls | Reduce latency and materialisation | Preserve shared-variable consistency and account for side effects |
| Change storage/index | Improve retrieval cost | Return the same relation under updates and duplicates |
| Bound search | Meet a resource budget | Return “unknown” or an approximation instead of claiming the full logical answer |
The paper joins controlled deduction, logic programming and relational databases
Kowalski credits Pat Hayes’s “computation = controlled deduction”, connects the argument to Bibel and Vaughan Pratt, reads Hewitt’s PLANNER as evidence that logic needs control, and invokes Codd’s separation of a relational view from storage and retrieval.
The 1979 article is not the first appearance of every ingredient. Kowalski’s earlier work on predicate logic as a programming language and connection-graph proof procedures supplied foundations; Colmerauer, Roussel, Warren, Clark, van Emden and others shaped the operational and semantic programme. The article’s contribution is synthetic: it turns those strands into an engineering equation and walks through the dimensions that can move independently.
The database analogy is especially durable. A query states a relation; an optimiser chooses join order, access paths and materialisation. Two physical plans can be extensionally equivalent while having radically different costs. Kowalski’s supplier/part examples anticipate input-sensitive ordering: begin with the relation constrained by the known constant, not with a static leftmost call.
Later work sharpens the premises. Drabent’s construction of a practical Prolog SAT solver treats a logic programme as a declarative object, proves correctness and completeness, then adds delay and pruning controls and separately proves that the required properties survive. Brand and Yap generalise the form to “Propagation = Logic + Control”, distinguishing the truth conditions of complex constraints from information about which propagation is relevant.
Other descendants move the boundary again. Knowledge-base systems such as IDP treat logic as a model of a domain and provide several inference services rather than one procedural reading. Tabled logic programming changes top-down control by retaining answers. Deductive databases compile declarative rules into physical plans. None of these developments makes the 1979 equation a complete architecture; together they show why its separation of concerns remained productive.
Fair historical claim
The companion treats A = L + C as Kowalski’s influential formulation, not as an isolated invention of declarative programming, search control or database optimisation. The primary paper itself names the ideas and collaborators on which it builds.
What survived formal, historical and delivery challenge
Review one: scholarly fidelity and teaching. The reconstruction was checked across the complete author-hosted scan: abstract, A=L+C qualification, component diagram, clause notation, top-down and bottom-up grandparent derivations, procedural interpretation, permutation/quicksort contrast, data-structure separation, coroutining, Fibonacci, alternative-clause search, both path analyses, arrow notation, its input-sensitive limitation and the conclusion.
The largest fidelity risk was turning the equation into an absolute partition. The paper explicitly calls the boundary not wholly unambiguous and shows A=L₁+C₁=L₂+C₂. A second risk was claiming that all control preserves behaviour. The page keeps Kowalski’s first-approximation restriction and adds explicit soundness, completeness, termination and resource caveats from later work.
Review two: evidence fairness, language and accessibility. Historical credit remains distributed across Hayes, Codd, Hewitt, Bibel and the logic-programming collaborators cited by Kowalski. Later developments are labelled as context, not read backwards into 1979. The prose avoids treating Prolog’s particular left-to-right depth-first regime as the whole procedural interpretation.
All ten figures carry titles, descriptions, captions and source scopes. Animation is redundant, subtle and disabled under reduced-motion preference. The laboratory writes every state as text, verifies three independent invariants, distinguishes unreachable from unfinished search and exposes work rather than declaring one strategy universally best. Desktop, tablet and phone layouts are part of the release test.
Review verdict
Pass 1: formal and historical reconstruction: passed after restoring representation dependence, the boundary’s ambiguity and meaning-preservation premises. Pass 2: evidence and delivery: passed after distinguishing logical reachability, witness paths, operational traces, termination and bounded-resource outcomes.
Twelve cards for durable recall
Negation’s proof boundary becomes programming’s control boundary
Paper 0091 asked when a failed proof search justifies a negative conclusion. Paper 0092 broadens the view: proof search itself is the control component of an algorithm whose logic component states what counts as an answer.
Clark’s finite-failure condition already showed that operational details have semantic consequences. A loop cannot license negation, and a selection rule can determine whether failure becomes visible. Kowalski supplies the organising vocabulary: the completed or relational content belongs to L; selection, direction and alternative search belong to C.
The connection is not that meaning and control are independent in every observable sense. It is that separating them makes their interaction testable. One can ask whether a proposed C terminates, remains complete and respects L. Without that split, a missed answer is merely “programme behaviour”; with it, the miss can be diagnosed as representation, search, pruning or scheduling.
Canon 0093 moves to Raymond Reiter’s “A Logic for Default Reasoning”. Clark and Kowalski have established two pressures that default logic answers in a different register: knowledge bases need principled defeasible conclusions, and reasoning systems need to distinguish object-level rules from the conditions that govern their use.
A programme becomes easier to improve when its promises survive a change of method
The equation’s deepest value is not that every line of code can be coloured logic or control. It is that an engineered system should expose a promise that remains stable while methods of keeping it change.
That promise is L: the relation between legitimate inputs and answers, expressed with enough domain structure to be intelligible and useful. The methods are C: which direction to reason, which subproblem to attend to, which alternative to explore, what to retain and how to retrieve it. Improvement becomes a controlled comparison only when the promise is held fixed.
The paper also resists a naïve celebration of abstraction. Permutation plus orderedness is beautifully direct but computationally weak under ordinary controls. A quicksort relation moves problem knowledge into L. Data abstraction improves clarity, yet specialisation may be required for speed. The useful boundary must serve both proof and execution.
Modern systems repeat this pattern. Database users state relations while optimisers select plans. Constraint modellers state conditions while propagators schedule inference. A graph query has a meaning while engines choose indexes and traversal orders. A machine-learning pipeline has a metric contract while schedulers choose batching and placement. In every case, performance work becomes safer when the invariant is executable.
The hard part is the verb “preserve”. A fast search that misses an answer, a parallel schedule that races, or an index that serves stale data has changed the effective programme. Kowalski’s equation is therefore not permission to ignore control. It is a demand to make control replaceable and then prove, test or monitor the boundary.
Worked example 11 · a query optimiser as C
A three-table join has a declarative result independent of join order. Starting with the most selective relation can reduce intermediate rows by orders of magnitude. The optimiser is allowed to choose that order because equivalence rules justify the same result. A stale statistic may make the plan slow; an incorrect rewrite may make it wrong. Performance and semantics require different checks.
Causal probe 11 · run metamorphic controls
Execute several valid controls over the same small instance and compare complete answer sets. Differences identify a preservation failure; equal answers with different traces reveal genuine control effects. The Paper 0092 laboratory is this experiment in miniature.
Worked example 12 · bounded agency
An agent can represent the rule “approve only if every required check passes” as L. It can order checks cheaply first, run independent checks in parallel and cache stable results as C. If a timeout is reported as approval, control has violated logic. If it is reported as unknown, the system preserves the distinction between truth and unfinished work.
Causal probe 12 · remove the semantic oracle
Without a control-independent specification or exhaustive small-instance oracle, two fast implementations can agree on the same mistake. Preserve L as a testable artefact; do not let the currently fastest C become the only definition of correctness.
Primary source, companion scope and verification contract
This companion reconstructs Robert Kowalski’s “Algorithm = Logic + Control”, published in Communications of the ACM, volume 22, number 7, July 1979, pages 424–436, DOI 10.1145/359131.359136. The primary text was checked from the author-hosted 13-page scan, including diagrams, received/revised dates and references. The article covers the analytical equation, Horn-clause notation, reasoning direction, procedural interpretation, logical representation, data structures, call scheduling, alternative procedures, relational storage, path-finding analyses and graphical control notation.
Verification contract. Five orientation claims, ten accessible animated SVG figures, twelve worked examples, twelve causal probes, twelve memory cards, two comprehensive reviews and three executable exact tests. The release checks British English, reduced-motion support, responsive rendering at 1440, 768 and 390 pixels, source boundaries, complete reference metadata, explicit unreachable outcomes and identical source/generated pages.
Primary text and scholarly context
- Kowalski, R. A. (1979). “Algorithm = Logic + Control.” Communications of the ACM, 22(7), 424–436. DOI: 10.1145/359131.359136. Author-hosted scan: .
- Kowalski, R. A. (1974). “Predicate Logic as Programming Language.” Information Processing 74, 569–574.
- van Emden, M. H., & Kowalski, R. A. (1976). “The Semantics of Predicate Logic as a Programming Language.” Journal of the ACM, 23(4), 733–742.
- Codd, E. F. (1970). “A Relational Model of Data for Large Shared Data Banks.” Communications of the ACM, 13(6), 377–387.
- Clark, K. L. (1978). “Negation as Failure.” In H. Gallaire & J. Minker (eds.), Logic and Data Bases, 293–322.
- Kowalski, R. A. (1988). “The Early Years of Logic Programming.” Communications of the ACM, 31(1), 38–43.
- McCarthy, J. (1982). “Coloring Maps and the Kowalski Doctrine.” Stanford University Computer Science Department.
- Drabent, W. (2018). “Logic + Control: On Program Construction and Verification.” Theory and Practice of Logic Programming, 18(1), 1–38. arXiv:1110.4978.
- Brand, S., & Yap, R. H. C. (2006). “Towards ‘Propagation = Logic + Control’.” In Principles and Practice of Constraint Programming. arXiv:cs/0608015.
- De Cat, B., Bogaerts, B., Bruynooghe, M., Janssens, G., & Denecker, M. (2018). “Predicate Logic as a Modelling Language: The IDP System.” In Declarative Logic Programming. arXiv:1401.6312.