Skip to content

Decline a connective over a number rather than answering (#897) - #908

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
fix/no-numbers-in-connectives
Aug 12, 2026
Merged

Decline a connective over a number rather than answering (#897)#908
Rafael-SOWNet merged 1 commit into
masterfrom
fix/no-numbers-in-connectives

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Collaborator

Closes #897, taking the first of its two options: a number is not a truth value here. A truth value
that is neither true nor false is what MathS.Quantum is for.

expression was is
true and 0, false or 0, false xor 0 NaN left as written
false and 0, true or 0 False, True — by short-circuit left as written
true xor 0 not 0 left as written
not 0, 0 xor 0 left as written unchanged
true and 1, true and 1/2, true and i NaN left as written

The NaN was not the connective's judgement

Which is why it read as arbitrary. Andf's table answers (Boolean(true), _) => right, hands back the
0, and then the domain check in InnerSimplifyWithCheck finds a non-boolean where Domain.Boolean is
declared and returns MathS.NaN. So the logic never claimed nonexistence — a guard downstream converted
"this isn't a boolean" into "this doesn't exist". The connectives now decline the pair outright and
nothing reaches that check.

NaN was the worst of the three answers for the reason AGENTS.md gives: it means this does
not exist
, and true and 0 exists perfectly well — it is not a proposition. That is a wrong claim about
the mathematics, and the one an if on EvaluableBoolean will not save a caller from.

Two answers are withdrawn on purpose

false and 0 and true or 0 were False and True, settled by short-circuit before the number was
looked at. Those are gone. Whether an operand is admissible cannot depend on whether the operator
happened to need it
, and there is no proposition there for False to be the truth of.

Nothing is lost for a caller who insists: EvalBoolean() throws CannotEvalException for an unevaluated
node exactly as it did for NaN, so the type error still surfaces at the layer where insisting happens.
DomainCondition stops contradicting evaluation as a side effect — domain(a xor 0) says True, and
there is no longer a NaN for it to disagree with, which was #897's second complaint.

Why this is not a retreat from #880

A number and an undefined truth value are different things and must not be treated alike. NaN is how
this library spells no truth value — what an order comparison over the complex plane produces — and a
connective settles what it can with one, which is what #907 just landed. A number is a value of the
wrong sort.

(0/0) and false    =>  False     (unchanged — NaN is a missing truth value)
(i < 0) and false  =>  False     (unchanged)
false and x        =>  False     (unchanged — a bare variable is Domain.Any and may yet be one)
true and 0         =>  left as written

The guard therefore excludes NaN explicitly, and it is on the four connectives only. Comparison nodes
are untouched: Equalsf, Greaterf and their kind take numbers, which is their whole job.

A correction to the issue's own table

Two rows I wrote up as defects are not, and inspection says so:

  • false and 0 → False was not "correct by accident." The table answers False without consulting
    the operand, which is Kleene-sound and type-clean. It is withdrawn here for the sort reason above, not
    because it was wrong logic.
  • true xor 0 → not 0 was not "nonsense." Notf declares Domain.Boolean, so it passed the guard
    and came back as an unevaluated node — which is the honest "I could not settle this", not a claim.

So only three of the eight rows were wrong answers, and they were the three where the connective handed
back its non-boolean operand. The uniform decline is what makes the remaining five consistent with them
rather than merely harmless.

Measured

The new tests fail 11 of 284 against master and pass 284 of 284 here, checked by restoring master's
file underneath them.

Suite 6389 passed / 0 failed, F# wrapper 130 passed. casbench 117/119 with 0 wrong, rootcheck 596/596,
simpsweep 10463/10463, propcheck 1340 checks with 0 failures, crashcheck 1652 cases with 0 crashes and 0
unexpected throws, boundcheck unchanged at 2 disagreements.

Cut from master at 63bd11d7.

A number is not a truth value: 0 is not False and 1 is not True. The same category error was
reported three ways, depending on which operator met it and in which position -- NaN where the
operator had to look at the number, False or True by short-circuit where it did not, and a
rewritten `not 0` for `true xor 0`. Now none of them answers.

NaN was the worst of the three. It means "this does not exist", and `true and 0` exists
perfectly well; it is not a proposition. That is a wrong claim about the mathematics rather
than a graceful failure, and it is the one an if on EvaluableBoolean does not save a caller
from.

The NaN was not the connective's judgement, which is why it read as arbitrary: Andf's table
answers `(Boolean(true), _) => right`, hands back the 0, and the domain check in
InnerSimplifyWithCheck then finds a non-boolean where Domain.Boolean was declared and mints
MathS.NaN. The connectives now decline the pair outright, so nothing reaches that check.

Two answers are withdrawn on purpose. `false and 0` and `true or 0` were False and True by
short-circuit, and whether an operand is admissible cannot depend on whether the operator
happened to need it. There is no proposition there for False to be the truth of. A caller who
insists is unaffected: EvalBoolean throws CannotEvalException for an unevaluated node exactly
as it did for NaN, so the type error still surfaces where insisting happens.

The distinction this rests on, and the reason it is not a retreat from #880: NaN is how this
library spells "no truth value", which is what an order comparison over the complex plane
produces, and a connective settles what it can with one. A number is a value of the wrong sort.
So (0/0) and False and (i < 0) and False are both still False, and a bare variable is
Domain.Any and may yet be a truth value, so `false and x` is still False. Only a genuine number
declines. Comparison nodes are untouched -- Equalsf, Greaterf and their kind take numbers,
which is their job.

The new tests fail 11 of 284 against master and pass 284 of 284 here. Suite 6389 passed, F#
wrapper 130 passed; casbench 117/119 with 0 wrong; rootcheck 596/596; simpsweep 10463/10463;
propcheck 1340 checks 0 failures; crashcheck 1652 cases 0 crashes and 0 unexpected throws;
boundcheck unchanged at 2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Rafael-SOWNet
Rafael-SOWNet merged commit 7c3c01a into master Aug 12, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A boolean operator over a number gives NaN, an unevaluated node, or a correct answer, depending on whether short-circuiting looked at it

1 participant