Skip to content

choose operator in Boole#1365

Open
kondylidou wants to merge 3 commits into
strata-org:main2from
kondylidou:pr/choose-expr
Open

choose operator in Boole#1365
kondylidou wants to merge 3 commits into
strata-org:main2from
kondylidou:pr/choose-expr

Conversation

@kondylidou

Copy link
Copy Markdown
Contributor

Summary

choose-function declarations (command_choosefndef)

Introduces function f(params) : R := choose z : T . pred(z, params); as a
first-class Boole construct for defining functions by a property rather than an
implementation — similar to Verus spec choose.

Lowering (in StrataBoole/Verify.lean):

  1. An uninterpreted function declaration for f.
  2. An axiom in 3-forall form:
    ∀ p1:T1, …, ∀ pn:Tn, ∀ z:Tz, (z = f(p1,…,pn)) → pred(z,p1,…,pn)

The 3-forall form avoids de Bruijn index shifting: pred's natural bvar indices
(0 = z, 1 = pn, …, n = p1) match the quantifier nesting exactly.

DDM scope chain: @[scope(b)] v + @[scope(v)] pred gives pred transitive
access to all parameters via resultContext chaining.

Dot separator for choose

Extends normalizeUnicodeQuantifierSeparators (and its byte-position companion
normalizedQuantifierSepStep) in StrataDDM/Elab.lean to recognise the keyword
choose as a binder, rewriting choose z : T . predchoose z : T :: pred
before DDM parsing. Mirrors the existing / dot normalization.

Both choose_assign and command_choosefndef now accept . or ::.

Files changed

  • StrataDDM/StrataDDM/Elab.lean — state-machine extension for choose keyword
  • StrataBoole/StrataBoole/Grammar.lean — new command_choosefndef op
  • StrataBoole/StrataBoole/Verify.lean — lowering for command_choosefndef
  • StrataBooleTest/FeatureRequests/choose_fn.lean — new test
  • StrataBooleTest/FeatureRequests/choose_operator.lean — updated to . syntax

@kondylidou kondylidou requested a review from a team as a code owner June 12, 2026 11:23
@joscoh joscoh added the CSLib PRs and issues marked with this label indicate contributions from/for the CSLib community. label Jun 16, 2026

@joscoh joscoh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why we need separate syntax for functions defined by choose and why we can't use use an inlined function with the existing choose expressions?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in FeatureRequests or just regular tests?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merged into choose_operator.lean (which is already imported by StrataBooleTest.lean).
The file wasn't imported before, so its tests weren't running.


procedure test_choose_fn(x: int) returns (w: int)
spec {
requires ∃ z: int :: good(z, x);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to see the failing/unknown result if this precondition is omitted.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added chooseFnNoPrecondSeed — without the precondition the ensures still passes,
because the axiom unconditionally asserts good(best(x), x). Comment explains this.

Comment thread StrataDDM/StrataDDM/Elab.lean Outdated
| matchChoose3 -- matched 'cho'
| matchChoose4 -- matched 'choo'
| matchChoose5 -- matched 'choos'
| afterChoose -- matched 'choose', awaiting whitespace to confirm keyword boundary

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is ultimately a style preference, and maybe there are other considerations, but I would rather just use \epsilon to avoid this hardcoded 5-character matching. Then choose can be handled exactly like quantifiers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks! I replaced the 5-state machine with a single ch == 'ε' check alongside /.
Updated Grammar.lean surface syntax and all test programs accordingly.

| .command_choosefndef _ ⟨_, n⟩ ⟨_, targs?⟩ bs ret v pred =>
-- `function f(params) : R := choose z :: pred(z, params)`
-- Emits: uninterpreted function declaration + axiom
-- ∀ p1:T1,...,pn:Tn, ∀ z:Tz, (z = f(p1,...,pn)) → pred(z, p1,...,pn)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you need an assumption that pred is inhabited?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment: unlike w := ε z . pred (which guards with an existence assertion),
the function form emits the axiom unconditionally. Soundness requires the caller to
guarantee inhabitation, e.g. via requires ∃ z . pred(z, params).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand this. When you say "Soundness requires the caller to
guarantee inhabitation", is there anything enforcing this or is it up to the user to make sure they don't declare unsound function definitions. In the latter case, how is this better than just writing an axiom directly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing enforces it. You're right that it's essentially syntactic sugar for:

function f(params) : R; -- uninterpreted
axiom ∀ params, pred(f(params), params);

The benefit over writing those two lines directly is just convenience. Soundness is the user's responsibility exactly as with axiom. We could require a proof obligation ∃ z . pred(z, params) at the declaration site, but that would need quantifier alternation (∀ params, ∃ z . pred) which is harder to discharge automatically. For now the design mirrors Verus spec functions, where choose-based definitions are also trusted by convention.

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

Labels

CSLib PRs and issues marked with this label indicate contributions from/for the CSLib community. Waiting-For-Review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants