Books / Constructions / Chapter 4

Part I · Chapter 4

Why We Got This Answer

10 min read

Contents of Constructions

We have already obtained several answers about the same water. With both abilities established, the model concluded that water was amphiprotic. When one piece of information was missing, the rule could not yield that conclusion. An explicit refutation of an ability gave a ground for a negative conclusion, while a conflict could stop both rules.

Now let us try explaining these differences to another person. Naming the state alone is not enough: we must show which information we used, how the answer followed from it, and where the rule itself came from. Start with the familiar positive case, then examine why the conclusion is missing in another case.

What the explanation shows

In scenario 14 both abilities of water are recorded. The explain command from the second chapter shows the following answer. The long rule-application identifier is shortened here with an ellipsis:

the species is amphiprotic(h2o): TRUE_ONLY
  rule_application  the species is amphiprotic(h2o)
    (urn:proof:apply:AmphiproticDonatesAndAccepts:f103e243…)
    assertion  the species donates a proton to another compound(h2o)
      (urn:proof:assert:urn:openstax:clir:chemistry-acids#k14-1)
    assertion  the species accepts a proton(h2o)
      (urn:proof:assert:urn:openstax:clir:chemistry-acids#k14-2)

Author’s paraphrase of the engine output above: water’s amphiproticity is established (TRUE_ONLY) by applying the rule AmphiproticDonatesAndAccepts to the two input entries — k14-1 about donating a proton and k14-2 about accepting one.

Read it bottom-up. Assertion k14-1 reports donating a proton, k14-2 accepting one. They concern one object. Above is the application of AmphiproticDonatesAndAccepts: the rule links these abilities to amphiproticity. The first line shows the result, TRUE_ONLY.

Such an analysis answers the question “what did this conclusion come from?” We can find the particular input entries and see the step from them to the conclusion. But another question remains: why does the model contain this rule?

Above its declaration we already saw this line:

@source(OS_14_1_AMPHIPROTIC)

This is a separate excerpt from the declaration, not a rule of its own. It points at the fragment named OS_14_1_AMPHIPROTIC in the same package’s sources.law file[^chapter-4-source]. Find it there: the locator field records 14.1/amphiprotic, and the text field holds the OpenStax Chemistry 2e paragraph about water’s two roles and amphiprotic particles. We will examine the structure of such entries in detail in chapter 12. For now it is enough to find, via the reference, the text the rule rests on.

There are two linked checks. The explanation lets us retrace the conclusion from the information in the case. The rule’s source reference lets us compare the formal rule with the textbook text. The paragraph defining amphiproticity does not replace checking the trustworthiness of each input assertion. In our example the information about water is taken from the textbook description; in another case we would need to investigate the grounds for each input assertion.

How to ask about a conclusion that failed

Return to the case where only the ability to donate a proton is known. The answer about amphiproticity will be NEITHER, as in the second chapter. Now we care about the reason: what kept the positive conclusion from coming about?

For that, there is the why_not question[^chapter-4-why]. It stands in place of the familiar truth. Here is scenario 20 in full from the check set:

test "urn:query:openstax-chem-acids-20-book-water-why-not-without-acceptance" {
    given {
        context {
            decision_time @2026-09-06T12:00:00Z;
            knowledge_time @2026-09-06T12:00:00Z;
            legal_time @2026-09-06;
            timezone "UTC";
        }
        assert donates_proton(entity_ref("urn:case:chem:h2o")) { id "k20-1"; origin case_input; }
    }
    evaluate why_not(amphiprotic(entity_ref("urn:case:chem:h2o")));
    expect truth_status == NEITHER;
    expect blocked_by("AmphiproticDonatesAndAccepts");
}

In given one entry about water remains. The line evaluate why_not(amphiprotic(…)) asks why amphiproticity is not established. The name inside the file uses an underscore: why_not.

The new expectation blocked_by("AmphiproticDonatesAndAccepts") checks that an entry about the named rule is present in the why_not result. Here the name is passed as a quoted string. That check alone neither establishes that the rule failed to fire nor checks the particular reason. It must be read together with the expected answer status and the condition’s explanation[^chapter-4-blocked].

First run the check over the whole set from the reader companion directory:

law engine test \
  corpus/laws/org/openstax/chemistry-acids/tests/openstax-chemistry-acids.lawtest \
  --program corpus/clir/openstax-chemistry-acids.lawir.json --imports \
  contexts/openstax-chemistry-acids.json

Among the results there will be:

test PASS: urn:query:openstax-chem-acids-20-book-water-why-not-without-acceptance

Author’s paraphrase of the engine output above: the check of scenario 20 passed — the NEITHER status and the mention of the rule AmphiproticDonatesAndAccepts in the why_not result matched the expectations.

To read the reason, make a copy of the check file, water-why.lawtest, in the reader companion directory. Keep in it the three opening language, package, namespace lines and only scenario 20. This is the same way of preparing a single scenario we used for explain.

chapter4_why_not=$(mktemp -d)
python3 reader_tools/prepare_question.py water-why.lawtest \
  --program corpus/clir/openstax-chemistry-acids.lawir.json --imports \
  contexts/openstax-chemistry-acids.json --out "$chapter4_why_not"
law engine why-not "$chapter4_why_not"

The command’s name uses a hyphen: why-not. The command asks why a positive conclusion is missing; it can also be applied to a single scenario with a truth question. Below is the substantive part of the answer. The closing section on kinds of diagnostics is omitted here.

статус результата: NEITHER

блокираторов, подтверждённых документом, нет

не определено (1) — условие НЕ опровергнуто, вывода просто нет:
  urn:openstax:clir:chemistry-acids#AmphiproticDonatesAndAccepts
    правило a species capable of either donating or accepting protons is amphiprotic (14.1) не сработало, но условие НЕ опровергнуто: the species accepts a proton(h2o) — пара опор §62: NEITHER

Author’s paraphrase of the engine output above (the engine’s text is in Russian and is quoted verbatim; what follows is the author’s English retelling): the result status is NEITHER, with no document-confirmed blockers. The rule AmphiproticDonatesAndAccepts did not fire, but its condition was not refuted either — the premise about water accepting a proton has NEITHER support (no ground for or against, cf. §62), so the conclusion is simply missing for lack of information.

The answer names the rule and the particular premise: accepts_proton(h2o). Its state is NEITHER: no ground either for or against accepting a proton. So the rule’s condition is not established, but not refuted either.

Together, “no blockers” and “undetermined” mean that the condition is not refuted; the conclusion is missing for lack of information. The blocked_by expectation still passes because the analysis contains an entry about the named rule. The name guarantees less than it may suggest: finding the rule in the analysis does not establish that it was blocked, or explain why it did or did not apply.

The §62 reference in the last line points at the specification’s description of the four states. The familiar NEITHER suffices for reading the example. The missing ground did not become a negation, and the explanation pinned down which piece of information is lacking.

What an extra entry changes

Now check the reverse situation: both needed abilities are already recorded, and we add one more piece of information. The vocabulary holds a relation:

pub relation conjugate_acid_of(acid: Species, base: Species) kind empirical {
    label ru unofficial "первая частица — сопряжённая кислота второй";
    label en unofficial "the first species is the conjugate acid of the second";
}

It takes two arguments of type Species: first the particle that acts as the conjugate acid, then its base. The argument order sets the meaning of the link. This is a small extension of the familiar form: where a relation used to describe one particle, it now links two.

In scenario 21 an assertion is added to water’s two abilities:

assert conjugate_acid_of(entity_ref("urn:case:chem:h3o"), entity_ref("urn:case:chem:h2o")) { id "k21-3"; origin case_input; }

It reports that hydronium h3o is the conjugate acid of water h2o. This is a separate excerpt; the complete scenario lives in the same check file. The question is still about water’s amphiproticity.

The rule AmphiproticDonatesAndAccepts has two premises: donating and accepting a proton. In this package there is no chain of rules that would use the added link for an answer about amphiproticity. So the result stays TRUE_ONLY.

Check scenario 21 and view its explanation the same way as scenario 14: keep only the needed block in the copy and call explain. The explanation will hold assertions k21-1 and k21-2, while k21-3 will not appear. Deleting the third entry while keeping the rest changes neither this scenario’s answer nor its explanation.

That observation concerns the particular question and model. In another model the link could turn out to be a premise of a separate rule. To call information extraneous, check whether it takes part — directly or through further conclusions — in obtaining the answer.

Where to look for an error in the entry

There is a change that looks smaller still. In a copy of scenario 14, replace only in the assertion about accepting a proton urn:case:chem:h2o with urn:case:chem:h20: the last character becomes a zero instead of the letter “o”.

To a reader, that is a typo. To the engine, it is a valid identifier of a different object. Now donating belongs to h2o and accepting to h20. The two premises no longer come together for water, and the answer about its amphiproticity becomes NEITHER.

The old TRUE_ONLY and rule-application expectations will fail: the check prints FAIL. Then the why-not command will point at the unknown status of proton acceptance by h2o in particular. It will not say that we mistyped the last character, nor offer to equate the two names automatically. We discover the typo by matching the named premise against the input entries.

So expectation and explanation help each other. The expectation catches a mismatch with the intended result; the explanation narrows the search to a particular premise. In this exercise the identifier needs fixing. Simply replacing the expectation with NEITHER would make the check pass but leave the wrongly recorded case in place.

A watchmaker with a loupe examines the open mechanism of a huge alarm clock; a separate gear lies on the ground nearby.

What a table or a program needs

Two abilities can be kept in a table, with the third property computed by a formula. In Python a short program suffices for that too. The question is which distinctions the chosen data representation keeps.

Consider a small self-contained Python example. Lines with >>> are commands being entered; the following lines, the result. Here one entry links a particle’s name to the value True:

>>> recorded = {"h2o": True}
>>> recorded.get("nh3", False)
False
>>> records = [("h2o", True), ("h2o", False)]
>>> dict(records)["h2o"]
False

In the first case ammonia is absent from the dictionary. The get call returns the preselected default, False. Missing information thus becomes indistinguishable from a recorded negation. In the second case two entries about one name collapse into one: the dictionary keeps the last value. A conflict is no longer visible in such a representation.

Both effects follow from how the information is stored. In Python, support for an assertion and support against it can be kept separately to recover all four states. In a table, separate columns can be added for them. To reproduce this chapter’s explanation, the input entries’ names, information about applied rules, and source references are needed as well.

Arxo sets a shared way to link these elements: assertion states, rule-application conditions, and result explanations. The model’s author remains responsible for the choice of rules and input information. When a model is handed to another person, the shared conventions let them read the entry and check how the conclusion was reached without knowing how a particular program is built.

Try it yourself

In each exercise work with a separate copy holding the file header and one scenario. Substitute the copy’s name in the commands; the model path after --program stays the same. Try each exercise before reading the discussion that follows.

  1. Take scenario 18 from the last chapter: accepting a proton is recorded, while the question asks about donating. Call why-not. How does the reason for this NEITHER result differ from the one in scenario 20? Is there a rule in the vocabulary that could derive donation of a proton?
  2. Reproduce the typo in scenario 14. First check the old expectations, then read why-not and find the mismatching identifiers in the input entries. Fix the typo and make sure the old expectations hold again.
  3. Delete only entry k21-3 from a copy of scenario 21. Compare the answer and the explanation before and after deletion. Name both premises that keep supporting the conclusion.

Check your predictions

In the first exercise the explanation will point out that this program has no rule with donates_proton as its conclusion: the information must come from the case. In scenario 20 there is a rule for the sought property, but it lacks a premise. In the second exercise fixing the name returns TRUE_ONLY and the rule’s application. In the third, deleting the entry keeps both grounds and the old answer.

We have walked the first small example from vocabulary to explanation. Now try telling someone who has not read the chapter about it: what information is set, what follows by rule, why a missing premise differs from a refuted one, and where to find the text the rule refers to.

In the next chapter the subject changes: instead of water’s properties, triangle side lengths appear. But start with the familiar question — whether the input conditions suffice for the conclusion.

[^chapter-4-source]: The model’s source entries, fragment OS_14_1_AMPHIPROTIC; its use — in the rule’s declaration. [^chapter-4-why]: Arxo specification, §§183–185: explanation and why-not. A missing condition must not be declared false. [^chapter-4-blocked]: Arxo specification, §267.4: expectation-observations. Example — scenario 20 in the complete check set.

Contents · PDF · EPUB · Original text under CC BY 4.0 · Source-material terms

Constructions

Contents

You’re reading · Part I

Reading the First Model

Reading settings

Text size
Appearance

Preferences and your reading place are saved only in this browser.