Skip to content

Decision: a boolean type (#1286 booleans row → CHANGE) #1637

Description

@InauguralPhysicist

Owner verdict, 2026-10-06: #1286's "Booleans folded into another type" row is change. Jon: "kind of a ridiculous gap to have for EigenScript at its current build." This is the next EigenScript work, ahead of Lua rung 1 round 2.

Why now (measured)

Decided semantics

  1. A distinct type bool, with keywords true and false. type of true is "bool".
  2. These produce bool: comparisons (== != < <= > >=), not, and boolean-returning builtins/predicates. and/or keep returning an operand, as now.
  3. Printing and strings: true/false. JSON: json_encode emits true/false; json_decode of true/false yields bool.
  4. Truthiness is unchanged: 0, 0.0, null, "", [] and {} stay falsy; false is falsy and true is truthy (SPEC §truthiness gains the two values).
  5. Arithmetic on a bool raises (Jon, 2026-10-06; option chosen over Python's bool-is-int): true + 1, sum of [1 < 2] and every numeric builtin given a bool raise a type error. Converting is explicit (e.g. if c: 1 else: 0, or a documented num of b if one is added). This matches the fail-soft-to-loud direction (Architecture review: finish the fail-soft→loud reform (2026-08-18 whole-language ledger) #975).
  6. Equality between a bool and a number RAISES (Jon, 2026-10-06). true == 1 and false != 0 are type errors, like arithmetic, so the ~300 existing (pred of x) == 1 checks fail loudly and are migrated (to if pred of x: or == true) instead of silently flipping. Other mixed-type comparisons stay unequal as today (null == false is false, "true" == true is false). Bools are valid dict keys. Correction: an earlier version of this issue said true == 1 is false. That was the orchestrator's assumption, not an owner decision.
  7. No backward compatibility shim (zero external users). Every consumer that does arithmetic on a comparison is fixed in the same change.

Surfaces that must move together (touch-all-sites)

  • The value type and tag in the VM.
  • Comparison opcodes and the JIT's inline comparison fast paths, which must mirror the interpreter.
  • The trace tape's value encoding: a format bump under the versioning contract.
  • The observer: entropy band for the new type; never change the existing constants.
  • print/str/repr, type of, JSON, ==/hashing.
  • The lint/LSP type knowledge.
  • The ouroboros AOT: emitted comparisons, runtime ABI, the byte-exact VM differential.
  • The stdlib: every predicate returning 1/0.
  • SPEC/COMPARISON executable docs, and the playground/WASM build.

Done when

  • All seven semantics above hold, each pinned by a suite test that fails on v0.44.0, on the VM and the JIT, under ASan and the TSan lane.
  • Planted fault: make a comparison return num again; the new tests go red.
  • The tape format version is bumped per the binary-contract rule, and an old tape is refused loudly, not misread.
  • The ouroboros AOT matches the VM byte-for-byte on the parity suite, with booleans in it.
  • The consumer sweep: all 16 consumers' REAL CI commands pass on the new binary. Each break is fixed in its consumer repo, landed ahead of the pin bump.
  • Executable docs (SPEC/COMPARISON/README examples) are updated and CI-checked; #1286's table records the verdict with a link here.
  • Released, and EigenLua bumped. EigenLua's assert_semantics probe and the 9 boolean-only D rows re-graded, with the score posted here.

Activity

  1. InauguralPhysicist commented on Oct 6, 2026

    @InauguralPhysicist
    CollaboratorAuthor

    Design note (Jon, 2026-10-06): the slot layer already has the boolean type. Above it, the code discards it. Verified on origin/main:

    • src/value_slot.h: tag 0xFFF9 = BOOL (low bit = value), with slot_is_bool, slot_as_bool, slot_from_bool, slot_true/slot_false, and the refcount fast path treating it as an immediate.
    • The slot layer already prints bools as true/false (src/vm.c:120, src/trace.c:2714).
    • No producer: slot_from_bool has no callers. Comparisons build numbers.
    • Seven sites collapse a bool slot into a number when converting to a Value: src/eigenscript.c:1791, and src/vm.c lines 800, 840, 4042, 4408, 4490 and 6558 (make_num(slot_as_bool(s) ? 1.0 : 0.0)). The Value enum (src/eigenscript.h:253) has no VAL_BOOL for them to target.

    So the core of this change is to stop discarding the bool:

    1. add VAL_BOOL to the Value layer;
    2. make the comparison opcodes and not emit slot_from_bool;
    3. replace the seven collapses.

    The rest of the Done-when list still applies: keywords, JSON, arithmetic-raises, JIT fast paths, tape versioning, the AOT, and the consumer sweep.

  2. InauguralPhysicist commented on Oct 6, 2026

    @InauguralPhysicist
    CollaboratorAuthor

    Tracked as phase 1 of the value-model release #1644. Phase 1 lands and is released alone, because it unblocks EigenLua's Tier S. Phases 2-4 (numbers, strings, map keys) then share one tape bump and one consumer sweep. This issue's touch map is the template checklist for those phases.

  3. InauguralPhysicist commented on Oct 6, 2026

    @InauguralPhysicist
    CollaboratorAuthor

    Owner decision (Jon, 2026-10-06): bools as dict keys are DEFERRED to phase 4 (map keys) of #1644, unless a consumer needs them earlier. Item 6's "bools are valid dict keys" is superseded for this phase. In #1647, {true: 1} / d[true] raise like any non-string key. Phase 4 decides bool, number and int/float key unification together, with the guest-language matrix's worst-case target.

  4. 17 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:runtime-vmSubsystem: runtime-vmkind:decisionNeeds a decision recorded, not (only) code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions