Skip to content

Make Decidable a subtype of Bool - #2038

Closed
gebner wants to merge 7 commits into
leanprover:masterfrom
gebner:decsubtype
Closed

Make Decidable a subtype of Bool#2038
gebner wants to merge 7 commits into
leanprover:masterfrom
gebner:decsubtype

Conversation

@gebner

@gebner gebner commented Jan 15, 2023

Copy link
Copy Markdown
Member

This resolves the diamond between Decidable and BEq, which is starting to cause lots of headaches in mathlib.

-/
return false
/-
TODO: remove this hack after we refactor `Decidable` as suggested by Gabriel.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this TODO can now be removed, right? Or is the return false still a hack?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

From what I understand, the return false here is about instances that have more than one exit point, i.e., if c then { a := 42 } else { a := 1 }. We still allow that and I didn't remove class inductive either.

@gebner

gebner commented Jan 17, 2023

Copy link
Copy Markdown
Member Author

This PR is blocked by what I can only assume to be a miscompilation. Running stage1 immediately segfaults in the Syntax.identComponents function where we call lean_inc on a freed object that was allocated in splitNameLit.

Diffing the IR code is hard because we generate lots of block y := ..; case x of false-> jmp block 0; true -> jmp block 1 instead of a direct case distinction.

@Kha

Kha commented Jan 20, 2023

Copy link
Copy Markdown
Member

Regarding the explosion of join points (which perhaps is triggering the bug), I think the old compiler is very reliant on ite being a direct recursor application . At the LCNF level we expand macro_inline but no matchers. Unfortunately

@[macro_inline] def dite {α : Sort u} (c : Prop) [h : Decidable c] (t : c → α) (e : Not c → α) : α :=
  h.decide.casesOn
    (fun h => e (nomatch h.2 ·))
    (fun h => t (h.1 (.refl _)))
    h.decide_iff

@[macro_inline] def ite {α : Sort u} (c : Prop) [Decidable c] (t e : α) : α :=
  dite c (fun _ => t) (fun _ => e)

doesn't really help because the overapplication of Bool.casesOn seems to trigger a similar join point mess.

@gebner gebner added code-generator This issue is with the code generator and removed dev meeting labels Jan 23, 2023
@gebner gebner removed the code-generator This issue is with the code generator label Jan 28, 2023
@gebner

gebner commented Jan 28, 2023

Copy link
Copy Markdown
Member Author

Rebasing onto #2060 seemed to fix the compilation errors.

!bench

@gebner
gebner marked this pull request as ready for review January 28, 2023 02:21
@Kha

Kha commented Jan 31, 2023

Copy link
Copy Markdown
Member

!bench

@leanprover-bot

Copy link
Copy Markdown
Collaborator

Here are the benchmark results for commit 6cdf707.
There were significant changes against commit 345aa6f:

  Benchmark          Metric         Change
  ===================================================
- binarytrees        task-clock       3.9%   (10.5 σ)
- stdlib             instructions     2.1% (1236.2 σ)
- stdlib             maxrss           1.4%  (140.2 σ)
- stdlib             task-clock       1.5%   (19.4 σ)
- stdlib             wall-clock       1.3%   (67.3 σ)
- stdlib size        bytes .olean     2.2%
- workspaceSymbols   maxrss           1.4%   (15.2 σ)
+ workspaceSymbols   task-clock      -4.0%  (-20.8 σ)
+ workspaceSymbols   wall-clock      -4.0%  (-20.8 σ)

@fgdorais

Copy link
Copy Markdown
Contributor

Just a ping in eager anticipation of progress on this PR!

@Kha
Kha requested review from Kha and leodemoura as code owners November 20, 2023 08:15
@kmill

kmill commented Nov 21, 2023

Copy link
Copy Markdown
Collaborator

Something that might be a less invasive change is this:

class Decidable (p : Prop) where
  /-- The truth value of the proposition `p` as a `Bool`.
  If `true` then `p` is true, and if `false` then `p` is false. -/
  decide : Bool
  /-- `decide p` evaluates to the Boolean `true` if and only if `p` is a true proposition.  -/
  cond_decide : cond decide p (Not p)

/-- Prove that `p` is decidable by supplying a proof of `p` -/
@[match_pattern] def Decidable.isTrue {p : Prop} (h : p) : Decidable p where
  decide := true
  cond_decide := h

/-- Prove that `p` is decidable by supplying a proof of `¬p` -/
@[match_pattern] def Decidable.isFalse {p : Prop} (h : Not p) : Decidable p where
  decide := false
  cond_decide := h

In my limited testing, you don't need to touch pre-existing match patterns, which would be great if that meant downstream projects don't need to be updated.

However, this does need a modification to erase_irrelevant.cpp and perhaps other parts of the compiler, since properties of Decidable are hard-coded into it, and changing Decidable out from under it causes assertion violations.

@urkud

urkud commented Jul 2, 2024

Copy link
Copy Markdown

@kmill I tried your definition on current version of Lean4, then

@[macro_inline] def dite {α : Sort u} (c : Prop) [h : Decidable c] (t : c → α) (e : Not c → α) : α :=
  match h with
  | .isTrue h => t h
  | .isFalse h => e h

says "'unreachable' code was reached". UPD: I see that the assertion in decidable_to_bool_cases fails, I'll fix it tonight after day job.

@nomeata

nomeata commented Oct 18, 2024

Copy link
Copy Markdown
Collaborator

I am also getting interested in the change, in the form that Kyle and I came up with: It seem it would allow me to first write a decision procedure using normal booleans, and then do the proof separately, and finally when using decide, the kernel will quickly start reducing the plain boolean function, which is (maybe) more efficient than carrying the extra type parameter around all the time.

@leanprover-bot leanprover-bot added the P-low We are not planning to work on this issue label Mar 19, 2025
@kim-em
kim-em requested a review from zwarich as a code owner June 4, 2025 09:31
@zwarich

zwarich commented Jun 27, 2025

Copy link
Copy Markdown
Contributor

This PR is subsumed by #8309, which uses the approach suggested by Kyle and Joachim with the new compiler.

@zwarich zwarich closed this Jun 27, 2025
yermakoffivan pushed a commit to yermakoffivan/lean4 that referenced this pull request Aug 20, 2026
This PR changes the definition of `Decidable p` to a structure
containing a `Bool` and a proof of either `p` or `¬p`.

This is basically the approach proposed by @kmill in leanprover#2038.

Due to bugs in the old compiler, this was previously not possible;
however, now that the new compiler is enabled, this works perfectly
fine.

Using `Bool` in the definition of `Decidable` has several advantages, in
particular
- There are many more definitional equalities, e.g.
  ```lean
  variable (a b : Bool)

  #check (rfl : decide (a = true) = a)
  #check (rfl : decide (a = false) = !a)
  #check (rfl : decide (a = true ∧ b = true) = a && b)
  #check (rfl : decide (a = true ∨ b = true) = a || b)
  #check (rfl : decide (¬a) = !a)
  #check (rfl : decide (a = true ↔ b = true) = (a == b))
  ```
- The `decide` tactic no longer needs to carry proofs with it, improving
performance for well-written `Decidable` instances.
- `LawfulBEq` and `DecidableEq` are now compatible: When using the
`DecidableEq` instance provided by `LawfulBEq`, `decide (a = b)` is
definitionally equivalent to `a == b`.
- `Decidable` no longer needs special casing in the compiler.

In order to take full advantage from these changes, it is recommended to
use the `decidable_of_bool` and `decidable_of_iff` functions to
construct `Decidable` instances.

This is a breaking change, but in part due to `Decidable.isTrue` and
`Decidable.isFalse` remaining as `match_pattern`s, surprisingly few
(meta-)programs break.

---------

Co-authored-by: Julia Markus Himmel <2065352+TwoFX@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P-low We are not planning to work on this issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.