Compare commits

...

398 Commits

Author SHA1 Message Date
Kim Morrison
921472c67e initial exploration for a ExtHashMapD 2025-05-19 13:24:18 +10:00
Cameron Zwarich
fbac0d2ddb chore: use LitValue.toExpr instead of duplicating its definition (#8398) 2025-05-19 01:33:47 +00:00
Eric Wieser
e7b8df0c0e fix: change Array. lemma to be about Array (#8392)
This PR corrects some `Array` lemmas to be about `Array` not `List`.

Discovered [on
Zulip](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/duplicate.20declarations/near/518942094)
2025-05-19 00:29:35 +00:00
Kim Morrison
601ea24e31 chore: add failing grind tests for noncommutative/non-negation rings (#8396) 2025-05-19 00:26:16 +00:00
Cameron Zwarich
ca037ded0d chore: rename LitValue.natVal/strVal to .nat/str (#8394) 2025-05-18 22:10:58 +00:00
Cameron Zwarich
006d2925ba chore: rename LetValue.value to .lit (#8393) 2025-05-18 21:12:35 +00:00
Mac Malone
c8290bd942 fix: lake: import Lake w/ precompiled modules on MacOS (#8383)
This PR fixes the use of `import Lake` with precompiled modules, which
was previously broken on MacOS.

Closes #7388.
2025-05-16 21:24:13 +00:00
Henrik Böving
b7b95896aa fix: tests that suffer from renaming (#8386) 2025-05-16 17:18:52 +00:00
Lean stage0 autoupdater
e46daa8ee6 chore: update stage0 2025-05-16 16:17:48 +00:00
Kyle Miller
3854ba87b6 feat: pretty print letFun using have syntax (#8372)
This PR modifies the pretty printer to use `have` syntax instead of
`let_fun` syntax.
2025-05-16 15:10:01 +00:00
Sebastian Ullrich
4d58a3d124 feat: revamp aux decl name generation (#8363)
This PR unifies various ways of naming auxiliary declarations in a
conflict-free way and ensures the method is compatible with diverging
branches of elaboration such as parallelism or Aesop-like
backtracking+replaying search.
2025-05-16 14:57:18 +00:00
Joachim Breitner
6b7a803bf4 fix: mapError to store message data context (#8375)
This PR ensures that using `mapError` to expand an error message uses
`addMessageContext` to include the current context, so that expressions
are rendered correctly. Also adds a `preprendError` variant with a more
convenient argument order for the common cases of
prepending-and-indenting.
2025-05-16 14:46:23 +00:00
Joachim Breitner
0e96318c72 chore: update DTreeMap proofs with more unfolding induction (#8382)
This is a post-stage0 update following #8359.
2025-05-16 14:41:37 +00:00
Sebastian Ullrich
7994e55d80 chore: try refining some benchmark settings (#8377) 2025-05-16 11:24:11 +00:00
Lean stage0 autoupdater
d24aa91232 chore: update stage0 2025-05-16 10:08:06 +00:00
Joachim Breitner
e7b61232c9 feat: more parameters in .fun_cases theorem (#8359)
This PR improves the functional cases principles, by making a more
educated guess which function parameters should be targets and which
should remain parameters (or be dropped). This simplifies the
principles, and increases the chance that `fun_cases` can unfold the
function call.

Fixes #8296 (at least for the common cases, I hope.)
2025-05-16 09:06:21 +00:00
Sebastian Ullrich
af7eb01f29 chore: build leanc with Lake under USE_LAKE (#8336)
Removes the last use of stdlib.make.in in this configuration outside
stage 0.
2025-05-16 08:07:34 +00:00
Markus Himmel
ca9b3eb75f chore: variants of dite_eq_left_iff (#8357)
This PR adds variants of `dite_eq_left_iff` that will be useful in a
future PR.
2025-05-16 05:42:12 +00:00
Cameron Zwarich
a817067295 chore: adopt Option.getD (#8374) 2025-05-16 05:07:49 +00:00
Cameron Zwarich
fcb6bcee67 fix: revert #8023 now that it is redundant (#8371)
This PR reverts #8023 now that it has been made redundant by the more
general fix in #8367.
2025-05-16 00:53:30 +00:00
Kim Morrison
73509d03f3 chore: cleanup previously failing grind test (#8370)
This test is superseded by the `qsort_grind` branch.
2025-05-16 00:24:33 +00:00
Leonardo de Moura
6448547f41 fix: instantiateTheorem in grind (#8369)
This PR fixes a type error at `instantiateTheorem` function used in
`grind`. It was failing to instantiate theorems such as
```lean
theorem getElem_reverse {xs : Array α} {i : Nat} (hi : i < xs.reverse.size)
    : (xs.reverse)[i] = xs[xs.size - 1 - i]'(by simp at hi; omega)
```
in examples such as
```lean
example (xs : Array Nat) (w : xs.reverse = xs) (j : Nat) (hj : 0 ≤ j) (hj' : j < xs.size / 2)
    : xs[j] = xs[xs.size - 1 - j]
```
generating the issue
```lean
  [issue] type error constructing proof for Array.getElem_reverse
      when assigning metavariable ?hi with
        ‹j < xs.toList.length›
      has type
        j < xs.toList.length : Prop
      but is expected to have type
        j < xs.reverse.size : Prop
```
2025-05-15 23:06:32 +00:00
Cameron Zwarich
632b688cb7 feat: add an LCNF pass to convert structure projections to cases expressions (#8367)
This PR adds a new `structProjCases` pass to the new compiler, analogous
to the `struct_cases_on` pass in the old compiler, which converts all
projections from structs into `cases` expressions. When lowered to IR,
this causes all of the projections from a single structure to be grouped
together, which is an invariant relied upon by the IR RC passes (at
least for linearity, if not general correctness).
2025-05-15 21:54:25 +00:00
Cameron Zwarich
c5335b6f9a fix: give Ordering.then the expose attribute (#8366)
This PR adds the `expose` attribute to `Ordering.then`. This is required
for building with the new compiler, but works fine with the old compiler
because it silently ignores the missing definition.
2025-05-15 21:25:40 +00:00
Leonardo de Moura
a594f655da fix: use withReducibleAndIntances to match ground patterns (#8365)
This PR fixes the transparency mode for ground patterns. This is
important for implicit instances. Here is a mwe for an issue detected
while testing `grind` in Mathlib.
```lean
example (a : Nat) : max a a = a := by
  grind

instance : Max Nat where
  max := Nat.max

example (a : Nat) : max a a = a := by
  grind -- Should work
```
2025-05-15 19:50:46 +00:00
Leonardo de Moura
7a6bca5276 feat: basic support for eta reduction in grind (#7977)
This PR adds basic support for eta-reduction to `grind`.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Kim Morrison <scott.morrison@gmail.com>
2025-05-15 18:34:56 +00:00
Joachim Breitner
e5393cf6bc fix: cases tactic to handle non-atomic eliminator well (#8361)
This PR fixes a bug in the `cases` tacic introduced in #3188 that arises
when cases (not induction) is used with a non-atomic expression in using
and the argument indexing gets confused.

This fixes #8360.
2025-05-15 16:59:11 +00:00
Joachim Breitner
3481f43130 fix: FunInd: strip MData when creating the unfolding theorem (#8354)
This PR makes sure that when generating the unfolding functional
induction theorem, `mdata` does not get in the way.
2025-05-15 16:09:20 +00:00
Joachim Breitner
528fe0b0ed fix: FunInd: clean up packed arguments more throughly (#8356)
This PR tries harder to clean internals of the argument packing of n-ary
functions from the functional induction theorem, in particular the
unfolding variant
2025-05-15 12:58:52 +00:00
Sebastian Ullrich
01dbbeed99 feat: do not export def bodies by default (#8221)
This PR adjusts the experimental module system to not export the bodies
of `def`s unless opted out by the new attribute `@[expose]` on the `def`
or on a surrounding `section`.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-05-15 12:16:54 +00:00
Sebastian Ullrich
9486421fcc chore: tame some slow benchmarks (#8352)
No single-topic benchmark should take half as long as stdlib.

Bench run time reduced from 27min to 21min.
2025-05-15 11:53:10 +00:00
JovanGerb
d69a8eff3f fix: deduplicate elaboration of constant argument to rw (#8232)
This PR fixes elaboration of constants in the `rewrite` tactic.
previously, `rw [eq_self]` would elaborate `eq_self` twice, and add it
to the infotree twice. This would lead to the "Expected type" being
delaborated with an unknown universe metavariable.

I added a test to show this error during delaboration of the "Expected
type".

This was reported on Zulip as a panic message during delaboration:
[#mathlib4 > Crash in &#96;sup&#96;/&#96;inf&#96; /
&#96;max&#96;/&#96;min&#96;
delaborators](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Crash.20in.20.60sup.60.2F.60inf.60.20.2F.20.60max.60.2F.60min.60.20delaborators/with/515946714)
2025-05-15 11:33:10 +00:00
Kim Morrison
8154aaa1b3 feat: preparation for semirings and noncommutative rings in grind (#8343)
This PR splits `Lean.Grind.CommRing` into 4 typeclasses, for semirings
and noncommutative rings. This does not yet change the behaviour of
`grind`, which expects to find all 4 typeclasses. Later we will make
some generalizations.
2025-05-15 11:25:57 +00:00
Kim Morrison
abc85c2f3c chore: fix Inv.inv notation (#8351) 2025-05-15 11:22:48 +00:00
Jakob von Raumer
436221986a fix: fix typo in inhabited instance for ExtDHashMap (#8349)
This PR fixes the signature of the intended `Inhabited` instance for
`ExtDHashMap`.
2025-05-15 08:40:23 +00:00
Sebastian Ullrich
49369f9c7c chore: change chatty test to interactive test (#8348) 2025-05-15 07:56:26 +00:00
Kim Morrison
305fba625d feat: missing lemmas about Int order/multiplication (#8346)
This PR adds some missing lemmas about consequences of
positivity/non-negativity of `a * b : Int`.
2025-05-15 06:17:46 +00:00
Kim Morrison
83001213e3 chore: upstream Inv notation typeclass (#8345) 2025-05-15 03:56:23 +00:00
Leonardo de Moura
06ef738aec fix: etaStruct and preprocessing issues in grind (#8344)
This PR fixes term normalization issues in `grind`, and the new option
`grind +etaStruct`.
2025-05-15 03:32:10 +00:00
Kim Morrison
37529a5518 chore: initial work on grind attributes for TreeMap (#8342) 2025-05-15 02:24:51 +00:00
Leonardo de Moura
fad3e0ef5e fix: propagateCtor (#8341)
This PR fixes the `propagateCtor` constraint propagator used in `grind`.
2025-05-15 00:32:25 +00:00
Anne Baanen
e982bf9472 feat: implement "linter sets" that can be turned on as a group (#8106)
This PR adds a `register_linter_set` command for declaring linter sets.
The `getLinterValue` function now checks if the present linter is
contained in a set that has been enabled (using the `set_option` command
or on the command line).

The implementation stores linter set membership in an environment
extension. As a consequence, we need to pass more data to
`getLinterValue`: the argument of ype `Options` has been replaced with a
`LinterOptions`, which you can access by writing `getLinterOptions`
instead of `getOptions`. (The alternative I considered is to modify the
`Options` structure. The current approach seems a bit higher-level and
lower-impact.)

The logic for checking whether a linter should be enabled now goes in
four steps:
1. If the linter has been explicitly en/disabled, return that.
2. If `linter.all` has been explicitly set, return that.
3. If the linter is in any set that has been enabled, return true.
4. Return the default setting for the linter.

Reasoning:
* The linter's explicit setting should take precedence.
* We want to be able to disable all but the explicitly enabled linters
with `linter.all`, so it should take precedence over linter sets.
* We want to progressively enable more linters as they become available,
so the check over sets should be *any*.
* Falling back to the default value last, ensures compatibility with the
current way we define linters.

The public-facing API currently does not allow modifying sets: all
linters have to be added when the set is declared. This way, there is
one place where all the contents of the set are listed.

Linter sets can be declared to contain linters that have not been
declared (yet): this allows declaring linter sets low down in the import
hierarchy when not all the requested linters are defined yet.

---------

Co-authored-by: grunweg <rothgami@math.hu-berlin.de>
2025-05-14 23:30:42 +00:00
Lean stage0 autoupdater
4efef5760c chore: update stage0 2025-05-14 23:49:50 +00:00
JovanGerb
0a32ba371a perf: store dsimp cache in a simp call (#7428)
This PR adds a `dsimp` cache to `simp`. Previously each `dsimp` call
from `simp` started with a fresh cache.

For example, when simplifying `a * b` for `a b : A`, the type `A` is now
only visited once by `dsimp`, instead of at least 3 times.
[
Mathlib
bench](https://github.com/leanprover-community/mathlib4/pull/22812#issuecomment-2712043349):
```
Metric                 Change
=============================
instructions            -8.1%
task-clock              -7.4%
simp                   -45.6%
instantiate metavars   -11.7%
share common exprs      -8.2%
```

[#lean4 > Enormous speedup from &#96;dsimp&#96; caching in
&#96;simp&#96;](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Enormous.20speedup.20from.20.60dsimp.60.20caching.20in.20.60simp.60)

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2025-05-14 22:21:06 +00:00
JovanGerb
f699e18212 perf: dsimp shouldn't visit proofs (#6973)
This PR stops `dsimp` from visiting proof terms, which should make
`simp` and `dsimp` more efficient.
In this attempt I have `dsimp` leave the proofs in place as-is, instead
of simplifying the proof type.

Closes #6960
2025-05-14 22:09:25 +00:00
Rob23oba
b8c941d39a chore: use mutual inductives for data structures in the new compiler (#8332)
This PR changes the types `AltCore`, `FunDeclCore` and `CasesCore` used
in the IRs of the new compiler into the mutual inductives `Alt`,
`FunDecl` and `Cases`.
2025-05-14 18:04:49 +00:00
jrr6
995fa4766b fix: reduce ambiguity of "final" in application type mismatch message (#8322)
This PR refines the new wording of the "application type mismatch" error
message to avoid ambiguity in references to the "final" argument in a
subexpression that may be followed by additional arguments.

It does so by replacing "final" with "last," rephrasing the message so
that this adjective modifies the argument itself rather than the word
"argument," and only displaying this wording when two arguments could be
confused (determined by expression equality).

These changes were motivated by a report that in cases where a function
application `f a b c` fails to elaborate because `b` is incorrectly
typed, the existing error message's reference to `b` being the "final"
argument in the application `f a b` may create confusion because it is
not the final argument in the full application expression.
2025-05-14 16:12:10 +00:00
Sebastian Ullrich
e635eeacd3 chore: CI: replace Nix CI with Linux release (#8335)
Nix cannot handle the awesomeness of our stdlib anymore.
2025-05-14 15:31:40 +00:00
Lean stage0 autoupdater
7240ae2fa2 chore: update stage0 2025-05-14 16:03:07 +00:00
David Thrane Christiansen
12ff2d8c49 chore: remove old documentation site (#7974)
This PR removes the old documentation overview site, as its content has
moved to the main Lean website infrastructure.

This should be merged when the new website section is deployed, after
installing appropriate redirects.

Developer documentation is remaining in Markdown form, but it will no
longer be part of the documentation hosted on the Lean website. Example
code stays here for CI, but it is now rendered via a Verso plugin.
2025-05-14 14:31:33 +00:00
euprunin
88078930a9 chore: fix spelling mistakes (#8324)
Co-authored-by: euprunin <euprunin@users.noreply.github.com>
2025-05-14 06:52:16 +00:00
Leonardo de Moura
6ca31baa55 feat: structure extensionality in grind (#8330)
This PR improves support for structure extensionality in `grind`. It now
uses eta expansion for structures instead of the extensionality theorems
generated by `[ext]`. Examples:

```lean
opaque f (a : Nat) : Nat × Bool

attribute [grind ext] Prod Subtype

example (a b : Nat) : (f a).1 = (f b).1 → (f a).2 = (f b).2 → f a = f b := by
  grind

def g (a : Nat) : { x : Nat // x > 1 } :=
  ⟨a + 2, by grind⟩

example (a b : Nat) : (g a).1 = (g b).1 → g a = g b := by
  grind

@[grind ext] structure S where
  x : Nat
  y : Int

example (x y : S) : x.1 = y.1 → x.2 = y.2 → x = y := by
  grind
```
2025-05-14 02:43:52 +00:00
Kim Morrison
1312d08eda chore: update grind test (#8329) 2025-05-14 00:36:03 +00:00
Kim Morrison
8c80c3ca49 feat: follow-up to lemmas about List.intersperse (#8318)
This PR is follow-up to #8272, combining the conditional lemmas for
`getElem_intersperse` into a single lemma with an `if` on the RHS.
2025-05-14 00:24:13 +00:00
Kim Morrison
7688fbb067 feat: add @[grind] annotations to contains_iff_mem lemmas (#8328)
This PR adds the `@[grind =]` attribute to all `contains_iff_mem`
lemmas.
2025-05-14 00:03:46 +00:00
Kim Morrison
5b2e39e3b5 feat: add @[grind] annotations for generic GetElem lemmas (#8327)
This PR adds `@[grind]` annotations to the generic
`getElem?_eq_none_iff`, `isSome_getElem?`, and `get_getElem?`.
2025-05-14 00:03:38 +00:00
Kim Morrison
b5bf0b1d05 chore: cleanup of grind if-then-else example (#8326) 2025-05-13 23:01:34 +00:00
Kim Morrison
ff85acedb9 chore: move a grind test (#8325)
This PR moves a previously failing `grind` test from `tests/lean/grind/`
to `tests/lean/run/`.
2025-05-13 19:50:12 +00:00
Henrik Böving
337685a38a feat: bv_decide support for BitVec.reverse (#8323)
This PR adds support for bv_decide to understand `BitVec.reverse` in
bitblasting.
2025-05-13 18:32:12 +00:00
Joachim Breitner
127776288b fix: GuessLex: also look for negations of Nat comparisons (#8321)
This PR lets the termination argument inference consider negations of
Nat comparisons. Fixes #8257.
2025-05-13 15:10:19 +00:00
Joachim Breitner
1d90eac631 test: more fundind unfolding test for #8293 (#8320) 2025-05-13 13:19:37 +00:00
Marc Huisinga
92b59ae4f6 test: goals accomplished (#8319)
This PR adds a test for the goals accomplished diagnostics so that we
notice when they break.

Follow-up for #8242.
2025-05-13 13:00:47 +00:00
Eric Wieser
aa3e7848c2 fix: correct whitespace in omit/include (#8169)
This PR makes the whitespace handling in the syntax of `omit` and
`include` consistent with `variable`.

Zulip thread: [#lean4 > Pretty printing instances in &#96;omit&#96; @
💬](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Pretty.20printing.20instances.20in.20.60omit.60/near/515185216)
2025-05-13 12:50:11 +00:00
Leonardo de Moura
e0a266780b feat: add instance Grind.CommRing (Fin n) (#8276)
This PR adds the instances `Grind.CommRing (Fin n)` and `Grind.IsCharP
(Fin n) n`. New tests:
```lean
example (x y z : Fin 13) :
    (x + y + z) ^ 2 = x ^ 2 + y ^ 2 + z ^ 2 + 2 * (x * y + y * z + z * x) := by
  grind +ring

example (x y : Fin 17) : (x + y) ^ 3 = x ^ 3 + y ^ 3 + 3 * x * y * (x + y) := by
  grind +ring

example (x y : Fin 19) : (x - y) * (x ^ 2 + x * y + y ^ 2) = x ^ 3 - y ^ 3 := by
  grind +ring
```

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
2025-05-13 12:09:02 +00:00
Joachim Breitner
2299c3c9ec chore: post-stage0 adaptions for #8104 (#8317) 2025-05-13 11:38:21 +00:00
Kim Morrison
2cf3ac9461 feat: split Std.Classes.Ord (#8315)
This PR splits `Std.Classes.Ord` into `Std.Classes.Ord.Basic` (with few
imports) and `Std.Classes.Ord.SInt` and `Std.Classes.Ord.Vector`. These
changes avoid importing `Init.Data.BitVec.Lemmas` unnecessarily into
various basic files.
As the new import-only file `Std.Classes.Ord` imports all three of
these, end-users are not affected.
2025-05-13 11:22:19 +00:00
Kim Morrison
384c78ae13 chore: remove >6 month old deprecations (#8312) 2025-05-13 11:11:22 +00:00
Marcus Rossel
a6a2833c68 feat: lemmas about List.intersperse (#8272)
This PR adds lemmas about the length and use of `[]?` on results of
`List.intersperse`.

This was suggested by @TwoFX as discussed in
https://github.com/TwoFX/human-eval-lean/pull/164#discussion_r2074101914.

I am unsure about the correct naming of `intersperse_getElem?_even` and
`intersperse_getElem?_odd`.
2025-05-13 10:58:35 +00:00
Lean stage0 autoupdater
b04ecaefd7 chore: update stage0 2025-05-13 10:47:03 +00:00
Joachim Breitner
e575736cae feat: fun_induction to unfold function application in the goal (#8104)
This PR makes `fun_induction` and `fun_cases` (try to) unfold the
function application of interest in the goal. The old behavior can be
enabled with `set_option tactic.fun_induction.unfolding false`. For
`fun_cases` this does not work yet when the function’s result type
depends on one of the arguments, see issue #8296.
2025-05-13 09:37:39 +00:00
Markus Himmel
8eec1e4cfb feat: Option lemmas and cleanup (#8298)
This PR adds various `Option` lemmas and defines `Option.filterM` for
applicative functors.
2025-05-13 08:42:03 +00:00
Marc Huisinga
9659469998 fix: broken unknown identifier code actions (#8180)
This PR fixes the new unknown identifier code actions so that they work
in non-trivial files.

It's very unfortunate that we didn't notice this sooner.
2025-05-13 08:32:36 +00:00
Marc Huisinga
efcf94298a feat: improve workspace symbol performance (#8091)
This PR improves the performance of the workspace symbol request.

In my testing on my machine, the time to respond to the workspace symbol
request containing just `c` in Mathlib has been reduced to ~1200ms from
~11000ms.

We also serve the nearest-matching 1000 symbols instead of just the
first 100 now and use the length of the symbol as a tie-breaker for when
the fuzzy matching score is equal.

Some further improvements might be gained in the future when #8087 is
fixed and we can switch back to `qsort`.
2025-05-13 08:29:49 +00:00
Kim Morrison
f75e36dcdb chore: Vector doesn't extend Array (#8313)
This PR changes the definition of `Vector` so it no longer extends
`Array`. This prevents `Array` API from "leaking through".
2025-05-13 07:13:23 +00:00
Kim Morrison
aa647f3cd6 chore: cleaning up imports (#8314) 2025-05-13 07:09:21 +00:00
Kim Morrison
77302b6572 chore: add grind test for fastEraseDups (#8310)
This PR adds @TwoFX's `List.fastEraseDups` example, with the proof
golfed further using `grind`, as a test case for `grind`.
2025-05-13 06:55:39 +00:00
Kim Morrison
29cc75531a chore: remove accidental grind trace options (#8311) 2025-05-13 05:58:46 +00:00
Kim Morrison
a08d182359 feat: add @[grind] annotations for HashMap (#8246)
This PR add `@[grind]` annotations for HashMap and variants.
2025-05-13 04:56:41 +00:00
Cameron Zwarich
ef77434a49 fix: make new compiler's specialization closure behavior match old compiler (#8308)
This PR makes the new compiler's specialization pass compute closures
the same way as the old compiler, in particular when it comes to
variables captured by lambdas.
2025-05-12 21:31:03 +00:00
Henrik Böving
aa54390c85 fix: bv_decide preprocessing in dependently typed situations (#8306)
This PR makes it possible for `bv_decide` to tackle situations for its
enum type preprocessing where the enums themselves are use in a
dependently type context (for example inside of a `GetElem` body) and
thus not trivially accessible to `simp` for rewriting. To do this we
drop`GetElem` on `BitVec` as well as `dite` as early as possible in the
pipeline.
2025-05-12 21:03:58 +00:00
Cameron Zwarich
579d0ad15d chore: add @zwarich to the compiler CODEOWNERS (#8305) 2025-05-12 18:42:54 +00:00
Rob23oba
e212890dfc perf: optimize Lean/Compiler/IR/ToIR compilation time and size (#8286)
This PR optimizes the `ToIR.lean` module, reducing the size of the
compiled C code by a bit over a factor of 3. This significantly improves
the compilation time, making `ToIR` relatively quick to compile.

Closes #8269
2025-05-12 18:34:07 +00:00
Leonardo de Moura
1aa16f1e3c fix: missing foldProjs (#8303)
This PR fixes missing occurrences of `foldProjs` in `grind`.
2025-05-12 18:32:57 +00:00
Joachim Breitner
cc80f7943d fix: cases to fail gracefully when motive has complex argument of dependent type (#8302)
This PR lets `cases` fail gracefully when the motive has an complex
argument whose type is dependent type on the targets. While the
`induction` tactic can handle this well, `cases` does not. This change
at least gracefully degrades to not instantiating that motive parameter.
See issue #8296 for more details on this issue.
2025-05-12 16:04:26 +00:00
Joachim Breitner
c55bf5172d feat: unfolding induction theorems to unfold bif (#8301)
This PR unfolds functions in the unfolding induction principle properly
when they use `bif` (a.k.a. `Bool.cond`).
2025-05-12 16:00:30 +00:00
Leonardo de Moura
3f75f08e1d feat: abstract metavars in grind preprocessor (#8299)
This PR implements a missing preprocessing step in `grind`: abstract
metavariables in the goal
2025-05-12 14:53:54 +00:00
Markus Himmel
eda467e066 fix: typo in application type mismatch error message (#8290)
This PR fixes a typo that was introduced recently.
2025-05-12 13:35:29 +00:00
Lean stage0 autoupdater
ab5b8ffed1 chore: update stage0 2025-05-12 13:49:07 +00:00
Kim Morrison
7f6f4c889d feat: use NeZero in Fin lemmas where possible (#8291)
This PR changes the statements of `Fin` lemmas to use `[NeZero n] (i :
Fin n)` rather than `(i : Fin (n+1))` where possible.
2025-05-12 12:40:10 +00:00
Kim Morrison
294360518a chore: adjust @[grind] attributes on List lemmas (#8295) 2025-05-12 12:31:29 +00:00
Sebastian Ullrich
c7acb7e481 chore: reserve [expose] attribute (#8292)
To be used in the module system.
2025-05-12 12:19:30 +00:00
Siddharth
9105c01757 feat: BitVec.neg_ofNat_eq_ofInt_neg (#8206)
This PR shows that negating a bitvector created from a natural number
equals creating a bitvector from the the negative of that number (as an
integer).

```lean
theorem neg_ofNat_eq_ofInt_neg {w : Nat} (x : Nat) :
    - BitVec.ofNat w x = BitVec.ofInt w (- x) := by
  apply BitVec.eq_of_toInt_eq
  simp [BitVec.toInt_neg, BitVec.toInt_ofNat]
```

---------

Co-authored-by: Luisa Cicolini <48860705+luisacicolini@users.noreply.github.com>
2025-05-12 10:00:49 +00:00
Henrik Böving
d0c4d19270 fix: bv_decide can handle universe polymorphic enums (#8270)
This PR makes the enum pass of `bv_decide` handle enum types that are
universe polymorphic.
2025-05-12 08:22:57 +00:00
Kim Morrison
60ea92fdb0 chore: add failing grind tests (#8289) 2025-05-12 06:33:38 +00:00
Kim Morrison
2b4f372317 chore: add failing grind test (#8288) 2025-05-12 06:10:25 +00:00
Kim Morrison
10bda559f9 chore: begin development cycle for v4.21.0 (#8287) 2025-05-12 05:02:41 +00:00
Joachim Breitner
33aaabaed7 fix: FunInd: rewrite matches more reliably in .induct_unfolding (#8277)
This PR improves the generation of `.induct_unfolding` by rewriting
`match` statements more reliably, using the new “congruence equations”
introduced in #8284. Fixes #8195.
2025-05-11 15:26:28 +00:00
Joachim Breitner
dc1a70fa43 feat: congruence equations for matchers (#8284)
This PR adds a new variant of equations for matchers, namely “congruence
equations” that generalize the normal matcher equations. They have
unrestricted left-hand-sides, extra equality assumptions relating the
discriminiants with the patterns and thus prove heterogenous equalities.
In that sense they combine congruence with rewriting. They can be used
to rewrite matcher applications where, due to dependencies, `simp` would
fail to rewrite the discriminants, and will be used when producing the
unfolding induction theorems.
2025-05-11 13:04:59 +00:00
Joachim Breitner
ca73223d4c fix: left-over free variables in splitter (#8285)
This PR fixes “declaration has free variables” errors when generating a
splitter for a match statement with named patterns. Fixes #8274.
2025-05-11 13:04:45 +00:00
Sebastian Ullrich
1f85fd2db8 fix: rfl theorem tracking in the module system (#8215)
We need to track rfl status in both the private and public scope once
defs may become irreducible in the latter.
2025-05-11 07:57:19 +00:00
Leonardo de Moura
e681855428 feat: improve procedure for proving auxiliary type casting equalities in grind (#8281)
This PR improves the module used to prove auxiliary type cast equalities
in `grind`.
2025-05-11 04:15:41 +00:00
Leonardo de Moura
9096eb168d fix: arrow congruence in grind (#8280)
This PR the support for arrows in the congruence closure procedure used
in `grind`.
2025-05-11 03:18:18 +00:00
Cameron Zwarich
575b4786f9 feat: optimize lean_nat_shiftr for scalars (#8268)
This PR optimizes lean_nat_shiftr for scalar operands. The new compiler
converts Nat divisions into right shifts, so this now shows up as hot in
some profiles.
2025-05-11 01:39:59 +00:00
Leonardo de Moura
ddf5512c9a feat: add support for implies_congr in grind (#8275)
This PR ensures the congruence closure in `grind` and find non-dependent
arrow congruences. That is, it can apply the `implies_congr` theorem.
2025-05-10 12:09:45 +00:00
Leonardo de Moura
eabde77d84 fix: improve type-as-hole error message (#8262)
This PR improves the type-as-hole error message. Type-as-hole error for
theorem declarations should not admit the possibility of omitting the
type entirely.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-05-09 22:49:37 +00:00
Rob23oba
5df7770977 feat: consider universes and projections in addPPExplicitToExposeDiff (#8271)
This PR changes `addPPExplicitToExposeDiff` to show universe differences
and to visit into projections, e.g.:
```
error: tactic 'rfl' failed, the left-hand side
  (Test.mk (∀ (x : PUnit.{1}), True)).1
is not definitionally equal to the right-hand side
  (Test.mk (∀ (x : PUnit.{2}), True)).1
```
for
```lean
inductive Test where
  | mk (x : Prop)

example : (Test.mk (∀ _ : PUnit.{1}, True)).1 = (Test.mk (∀ _ : PUnit.{2}, True)).1 := by
  rfl
```
2025-05-09 15:07:50 +00:00
Joachim Breitner
0e49576fe4 feat: guard_msgs to treat trace messages separate (#8267)
This PR makes `#guard_msgs` to treat `trace` messages separate from
`info`, `warning` and `error`. It also introduce the ability to say
`#guard_msgs (pass info`, like `(drop info)` so far, and also adds
`(check info)` as the explicit form of `(info)`, for completeness.

Fixes #8266
2025-05-09 05:44:34 +00:00
Kim Morrison
33afaa061e feat: improve 'apply' unification error message (#8261)
This PR adjusts the error message when `apply` fails to unify. It is
clearer about distinguishing the term being applied and the goal, as
well as distinguishing the "conclusion" of the given term and the term
itself.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-05-08 16:00:42 +00:00
Markus Himmel
1db53b39c4 chore: improve application type mismatch error message (#8264)
This PR rewords the `application type mismatch` error message by more
specifically mentioning that the problem is with the final argument.
This is useful when the same argument is passed to the function multiple
times.

We decided against using a wording which specifically mentions the
"function expression", because users who are not used to currying might
not think of the `f a` in `f a b` as a function.
2025-05-08 15:34:40 +00:00
jrr6
836d7b703a feat: add labeled subcomponents and helper functions for error messages (#8225)
This PR adds additional infrastructure for error message formatting.
Specifically, it adds convenience formatters for hints and notes,
including the ability to attach code actions to hint messages using a
"Try This"-like widget, along with several convenience formatters for
message data.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2025-05-07 21:15:27 +00:00
Luisa Cicolini
732471fddf chore: fix typo in Int/DivMod/Basic (#8255)
This PR fixes the typo `Int.edivx y` to `Int.ediv x y` in
`Int/DivMod/Basic`
2025-05-07 10:00:12 +00:00
Leonardo de Moura
02cbe4969f fix: exponential compilation times due to inlined instances (#8254)
This PR fixes unintended inlining of `ToJson`, `FromJson`, and `Repr`
instances, which was causing exponential compilation times in `deriving`
clauses for large structures.
2025-05-07 08:27:14 +00:00
plp127
e602bdc80c fix: have rename ignore implementation detail hypotheses (#8241)
This PR changes the behavior of the `rename` tactic to skip over
implementation detail hypotheses when finding a hypothesis to rename.

Closes #8240.
2025-05-07 06:53:13 +00:00
Lean stage0 autoupdater
529fb5c67f chore: update stage0 2025-05-06 18:39:27 +00:00
Joachim Breitner
edcad9a14b chore: post-stage0 fixes for #8171 (#8250) 2025-05-06 17:10:45 +00:00
Cameron Zwarich
cd100b8832 chore: make builtinRuntimeTypes an Array rather than a List (#8249) 2025-05-06 16:27:05 +00:00
Lean stage0 autoupdater
c96dfa54a4 chore: update stage0 2025-05-06 10:10:59 +00:00
Joachim Breitner
898eec78cd feat: FunInd: omit cases proved by contradiction (#8171)
This PR omits cases from functional induction/cases principles that are
implemented `by contradiction` (or, more generally, `False.elim`,
`absurd` or `noConfusion). Breaking change in the sense that there are
fewer goals to prove after using functional induction.

Fixes #8103.
2025-05-06 09:07:33 +00:00
Marc Huisinga
65b37b40ff fix: broken goals accomplished (#8242)
This PR fixes the 'goals accomplished' diagnostics. They were
accidentally broken in #7902.

Regression test tbd in a future PR.
2025-05-06 08:42:36 +00:00
Sebastian Ullrich
af51e3e4b1 fix: make sure all kernel constants are persisted eventually (#8238)
This PR avoids an issue where, through other potential bugs, constants
that are tracked by `Kernel.Environment` but not `Environment` are not
persisted.
2025-05-05 17:20:55 +00:00
Sebastian Ullrich
9c7cb147b9 fix: extern_lib and precompileModules on macOS (#8236)
This PR fixes an issue where the combination of `extern_lib` and
`precompileModules` would lead to "symbol not found" errors.
2025-05-05 14:59:50 +00:00
Kim Morrison
9576e48e1a chore: update release_checklist.py to check new release notes page (#8235) 2025-05-05 13:29:53 +00:00
Kim Morrison
77b9e510fc fix: apply? produces a non-synthetic sorry (#8231)
This PR changes the behaviour of `apply?` so that the `sorry` it uses to
close the goal is non-synthetic. (Recall that correct use of synthetic
sorries requires that the tactic also generates an error message, which
we don't want to do in this situation.) Either this PR or #8230 are
sufficient to defend against the problem reported in #8212.
2025-05-05 12:31:08 +00:00
Sebastian Ullrich
cdb18f48cd fix: ld.so linking on Linux (#8228)
This PR fixes an issue where, depending on the host glibc version,
Lean-built executables fail with an assertion in `ld.so`.
2025-05-05 11:50:59 +00:00
Kim Morrison
208ff3e2b3 feat: upgrades to release_checklist.py script (#8192)
This PR includes upgrades to the `release_checklist.py` script prepared
while releasing v4.20.0-rc1.
2025-05-05 09:03:57 +00:00
Leonardo de Moura
ef603cf37d fix: simplifyBasis (#8226)
This PR fixes the `simplifyBasis` procedure in the commutative ring
procedure in `grind`.
2025-05-05 02:35:52 +00:00
Leonardo de Moura
8cc4505bb1 feat: diagnostics for comm ring procedure in grind (#8224)
This PR adds diagnostic information for the commutative ring procedure
in `grind`.
2025-05-04 22:55:40 +00:00
Mac Malone
70917fac9f feat: lean --setup (#8024)
This PR adds the `--setup` option to the `lean` CLI. It takes a path to
a JSON file containing information about a module's imports and
configuration, superseding that in the module's own file header. This
will be used by Lake to specify paths to module artifacts (e.g., oleans
and ileans) separate from the `LEAN_PATH` schema.

To facilitate JSON serialization of the header data structure, `NameMap`
JSON instances have been added to core, and `LeanOptions` now makes use
of them.
2025-05-03 23:57:37 +00:00
Kim Morrison
132c608ebc chore: more @[grind] annotations for List/Array/Vector (#8218)
This PR continues adding `@[grind]` attributes for List/Array/Vector,
particularly to the lemmas involving the `toList`/`toArray` functions.
2025-05-03 19:28:54 +00:00
Kim Morrison
d005a306f9 chore: cleanup of @[grind] lemmas for Option (#8217) 2025-05-03 18:59:30 +00:00
Kim Morrison
80349ac77b feat: complete addition of @[grind] annotations for Option (#8216)
This PR completes adding `@[grind]` annotations for `Option` lemmas, and
incidentally fills in some `Option` API gaps/defects.
2025-05-03 17:14:25 +00:00
Kim Morrison
6e2e1a4f89 chore: consistently add @[simp] to getKey_eq map lemmas (#8186)
These lemmas were inconsistently marked as `@[simp]`, but they seem
generally useful, so this uniformly marks this lemmas as `@[simp]` for
all map variants.
2025-05-03 16:12:33 +00:00
Cameron Zwarich
afab374305 feat: LCNF -> IR translation (#8211)
This PR adds support for generating IR from the LCNF representation of
the new compiler.
2025-05-03 05:34:37 +00:00
Lean stage0 autoupdater
bc1d30de38 chore: update stage0 2025-05-03 00:16:43 +00:00
Leonardo de Moura
14d647f219 fix: nondeterminism in grind (#8209)
This PR fixes a nondeterminism issue in the `grind` tactic. It was a bug
in the model-based theory combination module.
2025-05-02 20:01:38 +00:00
Henrik Böving
daf7a579ed perf: use less defeq in frequently applied bv_decide simp rules (#8208)
This PR reduces the need for defeq in frequently used bv_decide rewrite
by turning them into simprocs that work on structural equality instead.
As the intended meaning of these rewrites is to simply work with
structural equality anyways this should not change the proving power of
`bv_decide`'s rewriter but just make it faster on certain very large
problems.
2025-05-02 19:15:34 +00:00
Sebastian Ullrich
9f48af3edd fix: cadical distribution on Linux (#8201)
Compile it with the same flags as other executables
2025-05-02 18:25:16 +00:00
Kim Morrison
63cf1052f4 chore: remove grind ext lemmas for List/Array/Vector (#8207) 2025-05-02 17:41:02 +00:00
Kim Morrison
0fd516a1df feat: add simpler getElem_map statements given LawfulBEq for all HashMap variants (#8188)
This PR takes the existing `getElem_map` statements for `HashMap`
variants (also `getElem?`, `getElem!`, and `getD` statements), adds a
prime to their name and an explanatory comment, and replaces the
unprimed statement with a simpler statement that is only true with
`LawfulBEq` present. The original statements which were simp lemmas are
now low priority simp lemmas, so the nicer statements should fire when
`LawfulBEq` is available.
2025-05-02 17:16:35 +00:00
Kim Morrison
34d944c4a9 feat: add ofList_eq_insertMany_empty lemmas for map types (#8182)
This PR adds `ofList_eq_insertMany_empty` lemmas for all the hash/tree
map types, with the exception of
`Std.HashSet.Raw.ofList_eq_insertMany_empty`.
2025-05-02 17:16:23 +00:00
David Thrane Christiansen
7f4f6b3457 doc: add documentation style guide (#8199)
This PR adds a style guide for documentation, including both general
principles and docstring-specific concerns.
2025-05-02 13:05:18 +00:00
Siddharth
43e8288e3f feat: Bitvector 0 equals bitvector 1 iff width is zero (#8202)
This PR adds an inference that was repeatedly needed when proving
`BitVec.msb_sdiv`, and is the symmetric version of
`BitVec.one_eq_zero_iff`
2025-05-02 10:32:01 +00:00
Leonardo de Moura
d26d7973ad fix: theory propagation in grind (#8198)
This PR fixes an issue in the theory propagation used in `grind`. When
two equivalence classes are merged, the core may need to push additional
equalities or disequalities down to the satellite theory solvers (e.g.,
`cutsat`, `comm ring`, etc). Some solvers (e.g. `cutsat`) assume that
all of the core’s invariants hold before they receive those facts.
Propagating immediately therefore risks violating a solver’s
pre-conditions midway through the merge. To decouple the merge operation
from propagation and to keep the core solver-agnostic, this PR adds the
helper type `PendingTheoryPropagation`.
2025-05-02 02:19:56 +00:00
Leonardo de Moura
1143b4766c chore: remove dead code (#8197) 2025-05-02 01:33:41 +00:00
Leonardo de Moura
af4c693030 feat: improve E-matching pattern inference in grind (#8196)
This PR improves the E-matching pattern inference procedure in `grind`.
Consider the following theorem:
```lean
@[grind →]
theorem eq_empty_of_append_eq_empty {xs ys : Array α} (h : xs ++ ys = #[]) : xs = #[] ∧ ys = #[] :=
  append_eq_empty_iff.mp h
```
Before this PR, `grind` inferred the following pattern:
```lean
@HAppend.hAppend _ _ _ _ #2 #1
```
Note that this pattern would match any `++` application, even if it had
nothing to do with arrays. With this PR, the inferred pattern becomes:
```lean
@HAppend.hAppend (Array #3) (Array _) (Array _) _ #2 #1
```
With the new pattern, the theorem will not be considered by `grind` for
goals that do not involve `Array`s.
2025-05-01 23:48:32 +00:00
Sebastian Ullrich
92775557d9 fix: go to import (#8193)
This silently broke with the import syntax changes.

TODO: figure out a way to test
2025-05-01 15:55:04 +00:00
Sebastian Ullrich
29fc6a46a8 chore: CI: exclude test not compatible with Lake CI 2025-05-01 12:58:44 +02:00
Kim Morrison
f634bfe0fc chore: update stage0 2025-05-01 12:42:44 +02:00
James Sully
2b80f801f6 doc: Fix typo in Tactics.lean: fun_cass -> fun_cases (#8191) 2025-05-01 06:38:39 +00:00
Mac Malone
18a9a694b3 doc: lake: add needs and native library options to README (#8190)
This PR adds documentation for native library options (e.g., `dynlibs`,
`plugins`, `moreLinkObjs`, `moreLinkLibs`) and `needs` to the Lake
README. It is also includes information about specifying targets on the
Lake CLI and in Lean and TOML configuration files.
2025-05-01 02:16:07 +00:00
Mac Malone
05153d66b1 chore: more verbose tests & related fixes (#8183)
This PR makes Lake tests much more verbose in output. It also fixes some
bugs that had been missed due to disabled tests. Most significantly, the
target specifier `@pkg` (e.g., in `lake build`) is now always
interpreted as a package. It was previously ambiguously interpreted due
to changes in #7909.
2025-05-01 01:20:50 +00:00
Leonardo de Moura
ae5fe802ce feat: stepwise proof terms for the commutative ring procedure in grind (#8189)
This PR implements **stepwise proof terms** in the commutative ring
procedure used by `grind`. These terms serve as an alternative
representation to the traditional Nullstellensatz certificates, aiming
to address the **exponential worst-case complexity** often associated
with certificate construction.

While various compression techniques for Nullstellensatz certificates
exist, they are not implemented in our procedure. Moreover, many of
these techniques rely on additional properties not available in
arbitrary commutative rings. In contrast, the stepwise proof terms
encode the **actual derivation** used during simplification, offering
significantly better scalability in practice.
Here is a motivating example:
```lean
example {α} [CommRing α] [IsCharP α 0] (d t c : α) (d_inv PSO3_inv : α)
  (Δ40 : d^2 * (d + t - d * t - 2) * (d + t + d * t) = 0)
  (Δ41 : -d^4 * (d + t - d * t - 2) *
         (2 * d + 2 * d * t - 4 * d * t^2 + 2 * d * t^4 + 2 * d^2 * t^4 - c * (d + t + d * t)) = 0)
  (_ : d * d_inv = 1)
  (_ : (d + t - d * t - 2) * PSO3_inv = 1) :
  t^2 = t + 1 := by grind +ring
```
In this case, the Nullstellensatz certificate generated by our procedure
contains **over 20,000 terms**, which overwhelms the Lean kernel during
verification. @kim-em also computed certificates using Mathematica with
various variable orderings, producing results between **500 and 2,000
terms**: still quite large.

By switching to stepwise derivations:
- `grind` completes the goal in **under 10 ms**
- The Lean kernel checks the resulting proof term in **under 1 second**

This change dramatically improves both the performance and robustness of
`grind` for nontrivial algebraic goals.
2025-04-30 18:45:29 +00:00
Kim Morrison
1e9864363f chore: fix statement of Std.HashMap.Equiv.getElem?_eq (#8185) 2025-04-30 17:12:47 +00:00
Kim Morrison
1d5110e140 feat: insertMany_append lemmas for map variants (#8184)
This PR adds the `insertMany_append` lemma for all map variants.
2025-04-30 17:09:51 +00:00
Kim Morrison
670158345a feat: unconditional lemmas for HashMap/TreeMap.getElem?_insertMany_list (#8154)
This PR adds unconditional lemmas for
`HashMap.getElem?_insertMany_list`, alongside the existing ones that
have quite strong preconditions. Also for TreeMap (and
dependent/extensional variants).
2025-04-30 16:20:41 +00:00
Wojciech Rozowski
96fcc94acb feat: add support for lattice-theoretic (co)inductive predicates (#8097)
This PR adds support for inductive and coinductive predicates defined
using lattice theoretic structures on `Prop`. These are syntactically
defined using `greatest_fixpoint` or `least_fixpoint` termination
clauses for recursive `Prop`-valued functions. The functionality relies
on `partial_fixpoint` machinery and requires function definitions to be
monotone. For non-mutually recursive predicates, an appropriate
(co)induction proof principle (given by Park induction) is generated.

Summary of changes:
- `Interal.Order.Basic` now contains `CompleteLattice` class, as well as
version of Knaster-Tarski fixpoint theorem (with an associated Park
induction principle) for the internal use for defining (co)inductive
predicates. `Prop` is shown to have two complete lattice structures (one
given by implication order for defining inductive predicates, and one
given by reverse implication for defining coinductive predicates).
Additionally, proofs that lattices are closed under products and
function spaces are included.
- Partial fixpoint's `EqnInfo` now additionally carries an information
whether something is defined as a lattice-theoretic fixpoint or via
CCPOs.
- When constructing a (co)inductive predicate,`PartialFixpoint/Main`
builds an appropriate lattice structure on the type of the predicate
using product lattice, function space lattice and an appropriate lattice
instance on `Prop`.
- `PartialFixpoint/Eqns` is modified to be able to perform rewrite under
lattice-theoretic fixpoint construction
- `PartialFixpoint/Induction`contains a case split for handling of the
(co)inductive predicates. In the case of lattice-theoretic fixpoints, it
appropriately desugars the Park induction principle.
2025-04-30 15:48:58 +00:00
Kim Morrison
86db67c444 chore: add failing grind test (#8179) 2025-04-30 14:54:51 +00:00
Kim Morrison
a9f4170372 feat: lemmas about List/Array/Vector.contains (#8175)
This PR adds simp/grind lemmas about `List`/`Array`/`Vector.contains`.
In the presence of `LawfulBEq` these effectively already held, via
simplifying `contains` to `mem`, but now these also fire without
`LawfulBEq`.
2025-04-30 14:38:56 +00:00
Kim Morrison
7ffeacf967 chore: move Array.qsort to Basic file (#8177)
No change to content, just moving into a subdirectory, to ease keeping a
branch adding theorems in sync.
2025-04-30 13:32:05 +00:00
Kim Morrison
8a8b9e4556 chore: further cleanup of the if-normalization example (#8176) 2025-04-30 13:02:08 +00:00
Sebastian Ullrich
4c497eaa32 chore: disable #print axioms under the module system (#8174)
No need for extra tracking to enable it considering how easy it is to
opt out
2025-04-30 12:00:09 +00:00
Marc Huisinga
98b864d25b fix: broken import completion (#8164)
This PR fixes import completion being broken by the recent changes to
import syntax for the module system.

Fixes #8162.
2025-04-30 11:31:45 +00:00
Sebastian Ullrich
e2f757d5a7 feat: private import and import all (#8159)
This PR adds support for the following import variants to the
experimental module system:

* `private import`: Makes the imported constants available only in
non-exported contexts such as proofs. In particular, the import will not
be loaded, or required to exist at all, when the current module is
imported into other modules.
* `import all`: Makes non-exported information such as proofs of the
imported module available in non-exported contexts in the current
module. Main purpose is to allow for reasoning about imported
definitions when they would otherwise be opaque. TODO: adjust name
resolution so that imported `private` decls are accessible through
syntax.

They can be combined into `private import all`, which will likely be the
most common usage of `import all`.
2025-04-30 10:06:54 +00:00
Joachim Breitner
d16862fd33 feat: induction: allow complex arguments to motive in conclusion of eliminator (#8096)
This PR lets `induction` accept eliminator where the motive application
in the conclusion has complex arguments; these are abstracted over using
`kabstract` if possible. This feature will go well with unfolding
induction principles (#8088).
2025-04-30 08:56:17 +00:00
Siddharth
0f7eb710e2 feat: add bv-concat-extract normalization simprocs (#8077)
This PR adds simprocs to simplify appends of non-overlapping Bitvector
adds. We add a simproc instead of just a `simp` lemma to ensure that we
correctly rewrite bitvector appends. Since bitvector appends lead to
computation at the bitvector width level, it seems to be more stable to
write a simproc.

As I write this, I realize that I can maybe write the `simp` lemma using
`no_index` to recover the same behaviour, so I'll try that too.
2025-04-30 08:31:38 +00:00
Leonardo de Moura
a1989c2387 feat: infrastructure for creating stepwise proof terms in the commutative ring procedure in grind (#8170)
This PR adds the infrastructure for creating stepwise proof terms in the
commutative procedure used in `grind`.
2025-04-30 05:01:02 +00:00
Lean stage0 autoupdater
9168840e2b chore: update stage0 2025-04-30 04:03:29 +00:00
Mac Malone
de0187ab8b fix: lake: extern_lib loading in non-precompiled module builds (#8152)
This PR fixes a regression where non-precompiled module builds would
`--load-dynlib` package `extern_lib` targets.

A reappearance of #4565. Thanks to Daniil [on
Zulip](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Multiple.20extern_lib/near/514772675)
for the report! This was not caught by the old test due to the removal
of `extern_lib` from the FFI example.
2025-04-30 01:04:59 +00:00
Leonardo de Moura
0eb9671787 fix: proof term for Nullstellensatz certificate (#8168)
This PR fixes a bug when constructing the proof term for a
Nullstellensatz certificate produced by the new commutative ring
procedure in `grind`. The kernel was rejecting the proof term.
2025-04-30 01:03:57 +00:00
Leonardo de Moura
e0230d8377 perf: improve heuristics for commutative ring procedure in grind (#8167)
This PR improves the heuristics used to compute the basis and simplify
polynomials in the commutative procedure used in `grind`.
2025-04-29 22:35:36 +00:00
Markus Himmel
925e53fcba fix: include libuv outside of namespace (#8166)
This PR makes sure we never `#include <uv.h>` while inside a namespace,
which recent GCC versions don't seem to like.
2025-04-29 22:19:17 +00:00
Kim Morrison
a4f2c51049 chore: add failing grind +ring tests (#8163)
This PR adds some currently failing tests for `grind +ring`, resulting
in either kernel type mismatches (bugs) or a kernel deep recursion
(perhaps just a too-large problem).
2025-04-29 21:30:43 +00:00
Henrik Böving
7b6c16a44b feat: implement a Selector for async UDP (#8139)
This PR is a follow up to #8055 and implements a `Selector` for async
UDP in order to allow IO multiplexing using UDP sockets.

The technical approach taken for this PR is basically a copy of #8078
but adjusted for UDP. The libuv API gives the same guarantee that was
used in that PR.
2025-04-29 21:01:14 +00:00
Kim Morrison
febf6c10f0 fix: update Grind.CommRing to avoid constructing non-defeq NatCast instance (#8161)
This PR changes `Lean.Grind.CommRing` to inline the `NatCast` instance
(i.e. to be provided by the user) rather than constructing one from the
existing data. Without this change we can't construct instances in
Mathlib that `grind` can use.
2025-04-29 16:50:54 +00:00
Joachim Breitner
3d1d8fc1de feat: unfolding functional induction principles (#8088)
This PR adds the “unfolding” variant of the functional induction and
functional cases principles, under the name `foo.induct_unfolding` resp.
`foo.fun_cases_unfolding`. These theorems combine induction over the
structure of a recursive function with the unfolding of that function,
and should be more reliable, easier to use and more efficient than just
case-splitting and then rewriting with equational theorems.

For example  instead of
```
ackermann.induct
  (motive : Nat → Nat → Prop)
  (case1 : ∀ (m : Nat), motive 0 m)
  (case2 : ∀ (n : Nat), motive n 1 → motive (Nat.succ n) 0)
  (case3 : ∀ (n m : Nat), motive (n + 1) m → motive n (ackermann (n + 1) m) → motive (Nat.succ n) (Nat.succ m))
  (x x : Nat) : motive x x
```
one gets
```
ackermann.fun_cases_unfolding
  (motive : Nat → Nat → Nat → Prop)
  (case1 : ∀ (m : Nat), motive 0 m (m + 1))
  (case2 : ∀ (n : Nat), motive n.succ 0 (ackermann n 1))
  (case3 : ∀ (n m : Nat), motive n.succ m.succ (ackermann n (ackermann (n + 1) m)))
  (x✝ x✝¹ : Nat) : motive x✝ x✝¹ (ackermann x✝ x✝¹)
```
2025-04-29 16:43:06 +00:00
Rob23oba
b5cfd86a89 fix: Substring.isNat for empty string (#8067)
This PR fixes the behavior of `Substring.isNat` to disallow empty
strings.

Closes #8005
2025-04-29 15:54:29 +00:00
Henrik Böving
eaa5d3498c feat: implement a Selector for channels (#8150)
This PR is a follow up to #8055 and implements a Selector for
`Std.Channel` in order to allow
 multiplexing using channels.

There is one subtlety to the implementation: Suppose we are in a
situation where we run `select` in a loop on two channels. One of the
channels is always quiet while the other has data available occasionally
(however not always as this would trigger the `tryFn` fast path and hide
the issue). In this situation the select receivers that are enqueued on
the silent channel would usually just remain there indefinitely as
nothing ever happens, causing a memleak. To avoid this we want to make a
channel select clean up after itself, even if it fails.

In an imperative programming language we could implement the receive
queue as a doubly linked list and simply make each receive select
maintain a pointer to its element in the queue and then remove itself in
`O(1)` upon failure. As that is not possible in Lean trivially we
decided to go for another approach for now: simply filter the queue for
selects that have failed in `unregisterFn`. While this approach is
`O(n)` we expect the amount of receivers enqueued on a channel to not be
terribly large and thus this to be a reasonably fast operation compared
to the remaining overhead. If it ever ends up becoming an issue, we
could switch to an approach that uses a `TreeMap` with numbered
receivers instead at a certain wait queue size and go to `O(log(n))`.
2025-04-29 15:15:38 +00:00
Sebastian Ullrich
db35bbb1a0 test: disable flaky test 2025-04-29 17:34:10 +02:00
Tom Levy
877d51bb15 doc: fix time complexity of List.merge (#8116)
This PR fixes a mistake in documented time complexity of List.merge.

The running time would only be `O(min |l| |r|)` in the very specific
best case where all the elements in the shorter list are less than all
the elements in the longer list. The worst-case (and average-case) time
complexity is `O(|l| + |r|)`.

Also update the variables in the time complexity to match the names of
the parameters.
2025-04-29 11:02:44 +00:00
Sebastian Ullrich
b677702b02 chore: update stage0 2025-04-29 11:01:57 +02:00
Sebastian Ullrich
d544ca5174 chore: fix default of Import.isExported 2025-04-29 10:58:27 +02:00
Rob23oba
9f06aff834 feat: optimized division without remainder for Int and Nat (#8089)
This PR adds optimized division functions for `Int` and `Nat` when the
arguments are known to be divisible (such as when normalizing
rationals). These are backed by the gmp functions `mpz_divexact` and
`mpz_divexact_ui`. See also leanprover-community/batteries#1202.
2025-04-29 07:23:35 +00:00
Cameron Zwarich
2929d547dc fix: make the lcnf expr cache depend on the value of root, not just… (#8156)
This PR fixes a bug where the old compiler's lcnf conversion expr cache
was not including all of the relevant information in the key, leading to
terms inadvertently being erased. The `root` variable is used to
determine whether lambda arguments to applications should get let
bindings or not, which in turn affects later decisions about type
erasure (erase_irrelevant assumes that any non-atomic argument is
irrelevant).
2025-04-29 00:37:52 +00:00
Leonardo de Moura
245ed056a3 fix: grind +splitImp, arrow propagator, missing normalization rule (#8158)
This PR fixes the `grind +splitImp` and the arrow propagator. Given `p :
Prop`, the propagator was incorrectly assuming `A` was always a
proposition in an arrow `A -> p`. This PR also adds a missing
normalization rule to `grind`.
2025-04-28 22:59:43 +00:00
Sebastian Ullrich
eaf1c6b4e1 fix: replayConst with native_decide (#8157)
This PR fixes an incompatibility of `replayConst` as used by e.g.
`aesop` with `native_decide`-using tactics such as `bv_decide`
2025-04-28 20:35:15 +00:00
Cameron Zwarich
d1ed57e92a fix: support borrowed params in the new compiler (#8127)
This PR adds support for borrowed params in the new compiler, which
requires adding support for .mdata expressions to LCNF type handling.
2025-04-28 17:02:47 +00:00
Sebastian Ullrich
0afcda8654 chore: robustify Nix shell (#8141)
* use tarballs directly from releases.nixos.org instead of GitHub
zipballs
* build cadical from source like everyone else since it's so small
2025-04-28 15:08:32 +00:00
Markus Himmel
290507396a chore: Option.guard accepts Bool predicate instead of Prop predicate (#8144)
This PR changes the predicate for `Option.guard` to be `p : α → Bool`
instead of `p : α → Prop`. This brings it in line with other comparable
functions like `Option.filter`.
2025-04-28 13:57:07 +00:00
Kim Morrison
8b3d70d2ab chore: fix statements of HashMap.getKey_insert (#8146) 2025-04-28 13:56:39 +00:00
Kim Morrison
b2ea6b6a02 feat: initial @[grind] attributes for List/Array/Vector (#8136)
This PR adds an initial set of `@[grind]` annotations for
`List`/`Array`/`Vector`, enough to set up some regression tests using
`grind` in proofs about `List`. More annotations to follow.
2025-04-28 13:48:20 +00:00
Kim Morrison
d10d17ce03 chore: add HashMap/TreeMap.isSome_X simp lemmas (#8143)
These lemmas were previously only stated the other way round, but in
this direction they are both good simp lemmas, and good grind lemmas.
2025-04-28 13:48:06 +00:00
Joachim Breitner
bca36b2eba refactor: realizeConst: do not set declPrefix (#8107)
This PR makes `realizeConst` to not set a `declPrefix`. This allows the
realization of both `foo.eq_def` and `bar.eq_def`, where `foo` and `bar`
are mutually recursive, all attached to the same function's environment.
2025-04-28 13:43:52 +00:00
Kim Morrison
573d824b81 feat: add List.eraseDupsBy and basic lemmas (#8148)
This PR generalises `List.eraseDups` to allow for an arbitrary
comparison relation. Further, it proves `eraseDups_append : (as ++
bs).eraseDups = as.eraseDups ++ (bs.removeAll as).eraseDups`.
2025-04-28 11:12:41 +00:00
Kim Morrison
436ebdad78 feat: add List.findRev? and findSomeRev?, and simp lemmas (#8147)
This PR adds `List.findRev?` and `List.findSomeRev?`, for parity with
the existing Array API, and simp lemmas converting these into existing
operations.
2025-04-28 11:09:51 +00:00
Lean stage0 autoupdater
6c9158b5b7 chore: update stage0 2025-04-28 11:29:55 +00:00
Kim Morrison
ecf690f1f1 chore: failing test for grind (#8065)
This PR adds a (failing) test case for an obstacle I've been running
into setting up `grind` for `HashMap`.
2025-04-28 10:46:19 +00:00
Sebastian Ullrich
eb559d58a8 refactor: introduce VisibilityMap in Lean.Environment, use it to split base in preparation for private import (#8145) 2025-04-28 10:17:18 +00:00
Kim Morrison
0b634e59f0 chore: add @[simp] to HashMap.get_getKey? (#8140) 2025-04-28 09:07:21 +00:00
Rob23oba
747ea853b5 feat: extensional hash maps (#8004)
This PR adds extensional hash maps and hash sets under the names
`Std.ExtDHashMap`, `Std.ExtHashMap` and `Std.ExtHashSet`. Extensional
hash maps work like regular hash maps, except that they have
extensionality lemmas which make them easier to use in proofs. This
however makes it also impossible to regularly iterate over its entries.
2025-04-28 06:48:25 +00:00
Leonardo de Moura
2ba021ecc2 fix: equality propagation and simplification in the comm ring procedure (#8137)
This PR improves equality propagation (also known as theory combination)
and polynomial simplification for rings that do not implement the
`NoZeroNatDivisors` class. With these fixes, `grind` can now solve:
```lean
example [CommRing α] (a b c : α) (f : α → Nat)
  : a + b + c = 3 →
    a^2 + b^2 + c^2 = 5 →
    a^3 + b^3 + c^3 = 7 →
    f (a^4 + b^4) + f (9 - c^4) ≠ 1 := by
  grind +ring
```
This example uses the commutative ring procedure, the linear integer
arithmetic solver, and congruence closure.
For rings that implement `NoZeroNatDivisors`, a polynomial is now also
divided by the greatest common divisor (gcd) of its coefficients when it
is inserted into the basis.
2025-04-28 00:55:18 +00:00
Leonardo de Moura
b77e9edd44 feat: add checkInvariants to CommRing (#8135)
This PR implements the sanity check function `CommRing.checkInvariants`.
2025-04-27 21:43:10 +00:00
Sebastian Ullrich
1b1c05916f chore: refine module imports (#8120)
* bump whole imported module closure to private if necessary
* disallow import of non-`module` from `module`
2025-04-27 20:45:31 +00:00
Leonardo de Moura
9a5d961c5e fix: grind.debug true when using grind +ring (#8134)
This PR ensures that `set_option grind.debug true` works properly when
using `grind +ring`. It also adds the helper functions `mkPropEq` and
`mkExpectedPropHint`.
2025-04-27 20:28:08 +00:00
Leonardo de Moura
d6ad3e1a85 fix: monomial order in the CommRing module (#8133)
This PR fixes the monomial order used by the commutative ring procedure
in `grind`. The following new test now terminates quickly.
```lean
example [CommRing α] (a b c : α)
  : a + b + c = 3 →
    a^2 + b^2 + c^2 = 5 →
    a^3 + b^3 + c^3 = 7 →
    a^4 + b^4 + c^4 = 9 := by
  grind +ring
```
2025-04-27 19:05:12 +00:00
Leonardo de Moura
d73557321b feat: add grind (ringSteps := <num>) (#8131)
This PR adds a configuration option that controls the maximum number of
steps the commutative-ring procedure in `grind` performs.
2025-04-27 17:46:02 +00:00
Cameron Zwarich
36ed58351d fix: add support for builtin casesOn recursors to the new compiler (#8132)
This PR adds support for lowering `casesOn` for builtin types in the new
compiler.
2025-04-27 17:11:36 +00:00
Leonardo de Moura
26138a5362 feat: equality propagation for comm ring procedure in grind (#8128)
This PR implements equality propagation in the new commutative ring
procedure in `grind`. The idea is to propagate implied equalities back
to the `grind` core module that does congruence closure. In the
following example, the equalities: `x^2*y = 1` and `x*y^2 - y = 0` imply
that `y*x` is equal to `y*x*y`, which implies by congruence that `f
(y*x) = f (y*x*y)`.
```lean
example [CommRing α] (x y : α) (f : α → Nat) : x^2*y = 1 → x*y^2 - y = 0 → f (y*x) = f (y*x*y) := by
  grind +ring
```
2025-04-27 15:05:56 +00:00
Joachim Breitner
f9d191d7b8 fix: allow ascii <- in if let clauses (#8102)
This PR allows ASCII `<-` in `if let` clauses, for consistency with
bind, where both are allowed. Fixes #8098.
2025-04-27 13:17:58 +00:00
Kim Morrison
cf35e13c60 feat: use fun_induction in if-normalization example (#8129)
This PR updates the If-Normalization example, to separately give an
implementation and subsequently prove the spec (using fun_induction),
instead of previously building a term in the subtype directly. At the
same time, adds a (failing) `grind` test case illustrating a problem
with unused match witnesses.
2025-04-27 12:27:17 +00:00
Sebastian Ullrich
b6259e61f2 chore: update stage0 2025-04-27 07:41:07 +02:00
Sebastian Ullrich
965dca1625 feat: import private 2025-04-27 07:41:07 +02:00
Leonardo de Moura
c3a1669398 feat: process comm ring module todo-queue in grind (#8126)
This PR implements the main loop of the new commutative ring procedure
in `grind`. In the main loop, for each polynomial `p` in the todo queue,
the procedure:
- Simplifies it using the current basis.
- Computes critical pairs with polynomials already in the basis and adds
them to the queue.

After the queue is empty, the disequalities are re-simplified using the
new basis. `grind` can now solve examples such as:
```lean
example [CommRing α] (x y : α) : x*y*x = 1 → x*y*y = y → y = 1 := by
  grind +ring

example [CommRing α] (x y : α) : x^2*y = 1 → x*y^2 = y → y*x = 1 := by
  grind +ring

example (x y : BitVec 16) : x^2*y = 1 → x*y^2 = y → y*x = 1 := by
  grind +ring
```
2025-04-27 01:04:45 +00:00
Cameron Zwarich
c633725b3e fix: add support for the init attribute to the new compiler (#8125)
This PR adds support for the `init` attribute to the new compiler.
2025-04-27 01:01:44 +00:00
Cameron Zwarich
763a43c241 fix: correctly handle escaping functions in LCNF's elimDeadBranches pass (#8124)
This PR correctly handles escaping functions in the LCNF
elimDeadBranches pass, by setting all params to top instead of
potentially leaving them at their default bottom value.
2025-04-26 23:56:01 +00:00
Leonardo de Moura
d64ae32965 feat: generate Nullstellensatz proof terms in grind (#8122)
This PR implements the generation of compact proof terms for
Nullstellensatz certificates in the new commutative ring procedure in
`grind`. Some examples:
```lean
example [CommRing α] (x y : α) : x = 1 → y = 2 → 2*x + y = 4 := by
  grind +ring

example [CommRing α] [IsCharP α 7] (x y : α) : 3*x = 1 → 3*y = 2 → x + y = 1 := by
  grind +ring

example [CommRing α] [NoZeroNatDivisors α] (x y : α) : 3*x = 1 → 3*y = 2 → x + y = 1 := by
  grind +ring

example (x y z : BitVec 8) : z = y → (x + 1)*(x - 1)*y + y = z*x^2 + 1 → False := by
  grind +ring
```
2025-04-26 22:52:00 +00:00
Lean stage0 autoupdater
685aa9b359 chore: update stage0 2025-04-26 17:01:41 +00:00
Sebastian Ullrich
f285867137 perf: no need to register axioms outside of the module system (#8121) 2025-04-26 16:14:00 +00:00
Sebastian Ullrich
87dccb9d1b fix: restore what simp theorems are recorded as rfl (#8114)
#8090 accidentally affected `dsimp` applications even outside the module
system, restore previous extension data.
2025-04-26 16:09:20 +00:00
Sebastian Ullrich
82723489c9 fix: linter should have access to all messages, really (#8117)
Continuation of #8101
2025-04-26 15:23:07 +00:00
Leonardo de Moura
d81a922a20 feat: NoZeroNatDivisors helper class for grind (#8111)
This PR adds the helper type class `NoZeroNatDivisors` for the
commutative ring procedure in `grind`. Core only implements it for
`Int`. It can be instantiated in Mathlib for any type `A` that
implements `NoZeroSMulDivisors Nat A`.
See `findSimp?` and `PolyDerivation` for details on how this instance
impacts the commutative ring procedure.
2025-04-26 15:14:27 +00:00
Kim Morrison
18f8a18bfc chore: fix TreeMap deprecations (#8100)
This PR fixes some incorrect deprecations in TreeMap.
2025-04-26 13:10:05 +00:00
Sebastian Ullrich
4323507b91 fix: linter should have access to complete command message log (#8101)
This PR fixes a parallelism regression where linters that e.g. check for
errors in the command would no longer find such messages.

---------

Co-authored-by: damiano <adomani@gmail.com>
2025-04-26 11:36:21 +00:00
Sebastian Ullrich
20a9db6357 chore: CI: run Linux Lake in all configurations
Otherwise master never has a cache for it
2025-04-26 13:25:29 +02:00
Sebastian Ullrich
c268602795 fix: wf preprocess of ite (#8112)
`[wf_preprocess]` expects a dsimp theorem, which in `Init` temporarily
have a simplistic syntactic representation until a more robust solution
is implemented.
2025-04-26 07:30:45 +00:00
Leonardo de Moura
60ee8c2f76 chore: broken test after update stage0 (#8110)
This is a temporary fix for `master` after update stage0 breakage.

cc @Kha @nomeata
2025-04-26 00:02:23 +00:00
Lean stage0 autoupdater
882d1ab812 chore: update stage0 2025-04-25 21:29:05 +00:00
Sebastian Ullrich
62c6edffef feat: do not export theorem bodies (#8090)
This PR adjusts the experimental module system to elide theorem bodies
(i.e. proofs) from being imported into other modules.
2025-04-25 20:22:32 +00:00
Markus Himmel
6cdabf58c6 chore: deprecate some Int.ofNat_* lemmas (#8000)
This PR deprecates some `Int.ofNat_*` lemmas in favor of
`Int.natCast_*`.
2025-04-25 16:16:58 +00:00
Marc Huisinga
8195f70502 chore: revert "fix: trace nodes collapsing while file is elaborating (#8056)" (#8095)
This PR reverts #8056 because the implementation there has a bug that is
best fixed with a different approach, and which we should preferably
only merge next release cycle.
2025-04-25 09:59:41 +00:00
Joachim Breitner
3fe195a4a9 fix: FunInd with nested well-founded recurison and late fixed parameters (#8094)
This PR fixes the generation of functional induction principles for
functions with nested nested well-founded recursion and late fixed
parameters. This is a follow-up for #7166. Fixes #8093.
2025-04-25 09:20:27 +00:00
Rob23oba
416e07a68e fix: handle surrogate pairs correctly in Json.parse (#8080)
This PR fixes `Json.parse` to handle surrogate pairs correctly.

Closes #5445
2025-04-24 19:07:46 +00:00
Henrik Böving
406bda8807 feat: implement a Selector for async TCP (#8078)
This PR is a follow up to #8055 and implements a `Selector` for async
TCP in order to allow IO multiplexing using TCP sockets.

As we must not commit to actually fetching data from the socket buffer
this cannot be implemented by just racing on `recv?`. Instead we perform
a call to `uv_read_start` and pass an `alloc_cb` that allocates no
memory at all. According to the docs of
[`uv_alloc_cb`](https://docs.libuv.org/en/v1.x/handle.html#c.uv_alloc_cb)
this is guaranteed to give us a `UV_ENOBUFS` in the relevant callback.
Thus we can first run this "zero read" and then go into one of three
cases:
1. We get cancelled before the zero read completes, in this case just
cancel the zero read and give up.
2. The zero read completes and we loose the race for completing the
`select`, in this case just don't do anything anymore
3. The zero read completes and we win the race for completing the
`select`, in this case we perform the actual read on the socket. As we
know that data is available already (since the read callback of the zero
read is only triggered if data actually is available) we know that the
subsequent actual read should complete right away.

In this way we avoid any data loss if we loose the race.
2025-04-24 16:05:35 +00:00
Luisa Cicolini
bc032eec8d feat: add BitVec.sdivOverflow definition and lemmas for overflow in signed and unsigned division (#7671)
This PR contains the theorem proving that signed division x.toInt /
y.toInt only overflows when `x = intMin w` and `y = allOnes w` (for `0 <
w`).
To show that this is the *only* case in which overflow happens, we refer
to overflow for negation
(`BitVec.sdivOverflow_eq_negOverflow_of_neg_one`): in fact,
`x.toInt/(allOnes w).toInt = - x.toInt`, i.e., the overflow conditions
are the same as `negOverflow` for `x`, and then reason about the signs
of the operands with the respective theorems.
These BitVec theorems themselves rely on numerous `Int.ediv_*` theorems,
that carefully set the bounds of signed division for integers.

co-authored by @bollu, @tobiasgrosser
2025-04-24 15:27:18 +00:00
Rob23oba
e2b3daf1dd fix: simp?! and variants to do auto-unfolding (#8076)
This PR fixes `simp?!`, `simp_all?!` and `dsimp?!` to do auto-unfolding.

Closes #7927
2025-04-24 14:04:39 +00:00
Lean stage0 autoupdater
7344bcffd8 chore: update stage0 2025-04-24 14:21:10 +00:00
Markus Himmel
68d9d14d44 chore: do not use the coercion α → Option α in Init and Std (#8085)
This PR moves the coercion `α → Option α` to the new file
`Init.Data.Option.Coe`. This file may not be imported anywhere in `Init`
or `Std`.
2025-04-24 13:35:01 +00:00
Joachim Breitner
9fbdf847bd fix: FunInd: properly split mutual structural recursion with extra parameters (#8086)
This PR makes sure that the functional induction priciples for mutually
recursive structural functions with extra parameters are split deeply,
as expected.
2025-04-24 13:32:53 +00:00
Sebastian Ullrich
66c00d33d4 feat: environment constant data can be split into .olean.private (#8079)
This PR lays the `Environment` groundwork for not exporting (parts of)
declarations.
2025-04-24 13:04:31 +00:00
Sebastian Ullrich
96cda3f498 chore: CI: revert accidentally disabling Lake cache 2025-04-24 15:01:09 +02:00
Joachim Breitner
d38d9400d8 fix: avoid panic in functional induction principle for structural recursion (#8083)
This PR fixes #8081.
2025-04-24 11:58:29 +00:00
Markus Himmel
781c94f2cf chore: test that there are no orphaned modules (#8082)
This PR adds a test that makes sure that there are no orphaned modules.
2025-04-24 11:55:07 +00:00
Lean stage0 autoupdater
e00a2f63ec chore: update stage0 2025-04-24 10:54:10 +00:00
Paul Reichert
be66157583 fix: import all raw tree map modules into Std.Data (#8044)
This PR introduces the modules `Std.Data.DTreeMap.Raw`,
`Std.Data.TreeMap.Raw` and `Std.Data.TreeSet.Raw` and imports them into
`Std.Data`. All modules related to the raw tree maps are imported into
these new modules so that they are now a transitive dependency of `Std`.
2025-04-24 10:06:32 +00:00
Joachim Breitner
b2ed6ac939 refactor: WF: add eq_def theorem for ._unary (#8063)
This PR adds an `foo._unary.eq_def` theorem, so that unfolding
`foo._unary` works as expected. This will help with #8019.
2025-04-24 09:59:08 +00:00
Sebastian Ullrich
51defe5935 chore: disable nondeterministic test 2025-04-24 11:30:26 +02:00
Sebastian Ullrich
c8cdb57c4b feat: move non-essential metadata into .olean.server (#8068)
This PR ensures that for modules opted into the experimental module
system, we do not import module docstrings or declaration ranges.

Excluding declaration docstrings as well would require some more work to
make `[inherit_doc]` leave a mere reference to the other declaration
instead of copying its docstring eagerly.
2025-04-24 08:12:26 +00:00
Henrik Böving
58c7e5da94 feat: async IO multiplexing framework + implementation for timers (#8055)
This PR adds an implementation of an async IO multiplexing framework as
well as an implementation of it for the `Timer` API in order to
demonstrate it.

The main motivation is to have fair and data loss free multiplexing of
event sources.
To illustrate two situations where just naively racing two tasks that
read from an event source might be the wrong thing:
1. Suppose we are waiting on two channel reads that are continuously
being filled up. As the first channel will always be ready when we start
its receive function it will instantly resolve the race before the
second one can even try. Thus the path where we receive data from the
second channel gets starved. For this reason we want to try in random
order (for fairness) if the event sources already have data available
for us.
2. Suppose we are waiting on two socket reads and both happen to finish
at the same time. As we are now only going to select one of them to
execute further, we are going to loose data on the second one (unless
there is a user written buffering mechanism involved) as we are going to
disregard the buffer it received and do a new receive next time. For
this reason it is important to wait for an event source to be available
without committing to actually fetching some data until we know that
this particular event source is going to win the select race.

The implementation is inspired by the Oslo framework written by
@haesbaert as well as Go's
[`select`](https://go.dev/src/runtime/select.go) implementation. Given a
list of event sources to select one from it is going to:
1. Randomly shuffle them
2. Attempt to fetch data from them (in their new random order) without
blocking (for fairness). If any of them succeeds return right away.
3. If none has data available right away set all of them up to resolve a
promise. They will then race to win the right to resolve that promise.
Only the data source that wins the race is allowed to then actually
fetch data, ensuring that no other event source actually fetches data
and then fails to deliver it to the consumer.


Follow up PRs are going to add implementations of `Selector` for
`Std.Channel` as well as TCP and UDP sockets.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-04-24 07:55:39 +00:00
Sebastian Ullrich
d5494a306c chore: CI: extend list of virtual merge checkout files 2025-04-24 08:43:42 +02:00
Kyle Miller
42ab5dfab0 fix: have runTermElabM reset local context when types of autobound implicits contain metavariables (#7952)
This PR makes two improvements to the local context when there are
autobound implicits in `variable`s. First, the local context no longer
has two copies of every variable (the local context is rebuilt if the
types of autobound implicits have metavariables). Second, these
metavariables get names using the same algorithm used by binders that
appear in declarations (with `mkForallFVars'` instead of
`mkForallFVars`).

This removes the last use of `Term.addAutoBoundImplicits'`, which
inherently has this variable duplication issue.
2025-04-24 03:29:10 +00:00
Max Carr
3d31b1f608 doc: fix typo in MetavarDecl docstring (#8069)
This PR fixes a typo (metavarible -> metavariable) in the docstring for
`MetavarDecl.type`
2025-04-23 22:00:13 +00:00
Leonardo de Moura
146df5ac74 feat: EqCnstr.mkNullCertExt (#8071)
This PR implements `EqCnstr.mkNullCertExt`. Given an implied polynomial
equation `p = 0`, it generates the certificate:
```
q₁ * h₁ + … + qₙ * hₙ
```  
for `d * p = 0`, where each `qᵢ`s are polynomials and each `hᵢ` is an
equational hypothesis of the form `lhsᵢ = rhsᵢ`. `d` is a numeral.
2025-04-23 19:41:46 +00:00
Sebastian Ullrich
7feb583b9e feat: enable experimental module system in Init (#8047) 2025-04-23 17:21:33 +00:00
Kim Morrison
50d18cdd75 chore: adding failing grind test (#8064)
This PR adds a failing `grind` test, showing a bug where grind is trying
to assign a metavariable incorrectly.
2025-04-23 14:47:38 +00:00
Lean stage0 autoupdater
92927cb4df chore: update stage0 2025-04-23 14:54:30 +00:00
Paul Reichert
57915af218 fix: reducing Nat.pow, kernel interprets constant as Nat literal (#8060)
This PR fixes a bug in the Lean kernel. During reduction of `Nat.pow`,
the kernel did not validate that the WHNF of the first argument is a
`Nat` literal before interpreting it as an `mpz` number. This PR adds
the missing check.

### Explanation

In `type_checker::reduce_pow`, an expression was interpreted as a `Nat`
literal without previously validating that it actually was a `Nat`
literal.

We (@TwoFX and me) noticed this while fuzzing the Lean kernel with GMP
and Mimalloc disabled. Until now, the fuzzer found one crash, leading us
to this issue.

What are the consequences? If GMP is disabled, the Lean kernel will
crash on some inputs after the memory allocator returns `null`. (MPZ
tries to clone the `.const` expression in disguise of a `Nat` literal
which accidentally has a size field indicating that the number has 88
trillion `mpz` digits. This is too much for every allocator.) If GMP is
enabled, it is possible to [prove
`False`](https://live.lean-lang.org/#codez=JYWwDg9gTgLgBAGQKYEMB2AoDATJAzOAcwDoBvAVwF84AuOABQFU1gYyq5AkwjgDkV4aAXjh5yaOAH04ggHxwIYJOIDCAGxQBnDcADGKVXGDjgBACoBPRdLgWrMABZK4ABjhJVGpC6wBaH3AApcg14AHcoViNCOAADPjZIULgACnjiRLgARlcADgBKOABWZwAmGLhQiHJVbDh9DQgK6ABrAG5YtIzU/nSIJOy4fKLnAGZyyursNAByGBx8G1pefi4GKAVaYVFxAA9pORM4PeFXBycAMXqvd08bKHIkX39TRzhmpCg0dzgoJGxyHRIDRwBzAYEwRooOBocggABGHzgCJgoSQTmyAD0cqMSnU0LVMdiACw5eYEegAeQA6gBCTbLBJ9FIkUjOaiAC/JAJfkBUyWHcKDhcAARABiIwAKyQOhgEjhKGwEjA6wgeCFSx0EBAIHQtVkcGwEAwcDgqiQ8FwOgMdGQ6GIABEpeooPxgBBxEI4MRcHg0A7LXBSEbjcG0CgQF4PTEQOYHCAADRB4NwexGGDAj3EX6EaooKAuBNJ40aFB4M3menEYulguFmCWCPCZLEFBgMApYgatAhWKmOAAbQAugUm53uzFKbT+0O8jWk6aAG7uei5sPp4SD2fB+f6B70kduseme5IYip9ZTvKJuCUIM2tDEACi6jhxGUmu1+OIqhMMDfvwAsikd7Ntg2B+gYFqqJeGB+HAyjOhojjAocADi/70IYwLYGCAqmtgGBimgkrSrK8qKsqeBYGc0BICARASEgACOEgAF4fI0dAshw4gnPScLmEGYh4BANREEGGhgN+8AADytHIUB4KoVGODRdGIX0Eh4FcSyXB4DZIgJxo6PY6CEF4vbdIySSuJkl7GlASR9oACYT0UxrHsQOQZIDsKDSnA0axhgQA)
because the kernel doesn't crash on a memory allocation and instead just
happily interprets the `.const` expression as a GMP number.

Importantly, this is _not_ a flaw in Lean's type theory. It is an
implementation bug in the built-in kernel, related to the efficient
reduction of `Nat.pow`, that will be fixed with this PR; see the test
file. Because Lean's kernel is relatively small, there are third-party
kernel implementations such as `lean4lean` and `nanoda`. `lean4lean`
catches the bogus proof, and looking at its code `nanoda` will, too, but
I haven't tried it yet.
2025-04-23 13:55:20 +00:00
Sebastian Ullrich
521a37f796 chore: rework module system import (#8062)
Ensure .server and .private are inspected only for `module`s and that
`module`s and non-`module`s interact correctly
2025-04-23 13:48:53 +00:00
Rob23oba
70bf2db056 fix: use one-field structures for Array.Perm and Vector.Perm (#7999)
This PR replaces `Array.Perm` and `Vector.Perm` with one-field
structures. This avoids dot notation for `List` to work like e.g.
`h.cons 3` where `h` is an `Array.Perm`.
2025-04-23 13:32:30 +00:00
Sebastian Ullrich
5c7a7d6406 chore: CI: reset reldebug check level 2025-04-23 14:31:59 +02:00
Marc Huisinga
55cb65c0fe fix: trace nodes collapsing while file is elaborating (#8056)
This PR fixes a bug where the trace nodes in the InfoView would close
while the file was still being elaborated.

Closes #8053.

The cause of this bug was that we didn't memorize interactive
diagnostics correctly, so the server would generate new RPC pointers in
every single `getInteractiveDiagnostics` RPC request, which lead to the
client resetting the UI.
2025-04-23 10:08:48 +00:00
Sebastian Ullrich
fbbf42e82f chore: fix reldebug preset (#8051)
Build with assertions, but without debug info
2025-04-23 10:05:11 +00:00
Sebastian Ullrich
8c8df274cf chore: fix enabling module system (#8057) 2025-04-23 09:31:08 +00:00
Sebastian Ullrich
2e42013555 chore: clarify m_cs_sz use with mimalloc (#8058)
We didn't feed correct data to `mi_free_size`, but it turns out it
discards it anyway.
2025-04-23 07:39:01 +00:00
Leonardo de Moura
dfa72d6c04 feat: infrastructure for computing Nullstellensatz certificates (#8059)
This PR adds infrastructure for computing Nullstellensatz certificates
in the comm ring procedure in `grind`.
2025-04-23 04:25:38 +00:00
Sebastian Ullrich
ad3ac150bc chore: remove lakefile copy in root and tests/ (#8054)
As we use a different Lean in these directories, using those copies
always results in a full rebuild
2025-04-22 16:03:12 +00:00
David Thrane Christiansen
5d82927d10 chore: move Lake DSL syntax into dedicated module (#8048)
This PR moves the Lake DSL syntax into a dedicated module with minimal
imports.

This allows modules outside of Lake/Lean to import Lake.DSL.Syntax
without crashing, because it reduces the transitive closure of these
modules' imports. This is needed for the reference manual to be able to
document the DSL syntax.

Additionally, the imports of `Lake.Build.Fetch` are decreased, which
reduces its import closure sufficiently to include docs for `FetchM` in
the reference manual.
2025-04-22 14:35:54 +00:00
Henrik Böving
8e1b9abb7a fix: missing wakeup in bounded channel try receive (#8052)
This PR fixes a small oversight in the wakeup mechanism of blocked
bounded channel senders that occurs when calling `tryRecv`.

Marked as `changelog-no` as this isn't released yet.
2025-04-22 14:32:59 +00:00
Sebastian Ullrich
be117c4738 fix: missing traces from realizeConst (#8050)
This PR fixes missing trace messages when produced inside `realizeConst`

Fixes #8049
2025-04-22 12:23:54 +00:00
Lean stage0 autoupdater
46526cc8fb chore: update stage0 2025-04-22 11:08:24 +00:00
Sebastian Ullrich
3ae41cb181 feat: allow use of experimental module system in Init (#7919) 2025-04-22 09:09:27 +00:00
Kim Morrison
2c6d634127 fix: make IntCast a field of Grind.CommRing (#8042)
This PR makes `IntCast` a field of `Lean.Grind.CommRing`, along with
additional axioms relating it to negation of `OfNat`. This allows use to
use existing instances which are not definitionally equal to the
previously given construction.

---------

Co-authored-by: Leonardo de Moura <leomoura@amazon.com>
2025-04-22 02:43:27 +00:00
Leonardo de Moura
ff336fb63c feat: Nullstellensatz certificates for the comm ring procedure in grind (#8043)
This PR adds `NullCert` type for representing Nullstellensatz
certificates that will be produced by the new commutative ring procedure
in `grind`.
2025-04-22 00:40:11 +00:00
Leonardo de Moura
9bdd11465c feat: improve denoteNum (#8040)
This PR modifies `denoteNum` to avoid `intCast`. It is too verbose in
pretty printing messages.
2025-04-21 18:29:23 +00:00
Sebastian Ullrich
791bba0091 feat: LLVM 15 -> 19 (#6063)
This PR updates the version of LLVM and clang used by and shipped with
Lean to 19.1.2

Fixes #5649
2025-04-21 17:18:18 +00:00
Sebastian Ullrich
d6c30a8a0a chore: disable build of old manual 2025-04-21 18:40:11 +02:00
Sebastian Ullrich
f86b192ec2 chore: fix Nix build 2025-04-21 18:40:11 +02:00
Sebastian Ullrich
e6771d7524 chore: update stage0 2025-04-21 18:40:11 +02:00
Sebastian Ullrich
da82cbd3d1 feat: module header keyword for enabling module system 2025-04-21 18:40:11 +02:00
Joachim Breitner
2386a3d7c7 chore: add RISC-V ast benchmark (#8035)
This PR adds a realistic large-inductive benchmark, taken from
https://github.com/opencompl/sail-riscv-lean
2025-04-21 15:46:38 +00:00
Henrik Böving
39f7380663 perf: fix linearity issue in bv_decide (#8036)
This PR fixes a linearity issue in `bv_decide`'s bitblaster, caused by
the fact that the higher order combinators `AIG.RefVec.zip` and
`AIG.RefVec.fold` were not being properly specialised.

Example benchmark `QF_BV/sage/app1/bench_1967.smt2`:
- before: https://share.firefox.dev/4cE86It
- after: https://share.firefox.dev/42L9chd
2025-04-21 13:51:21 +00:00
Kyle Miller
517899da7b feat: extract_lets and lift_lets tactics (#6432)
This PR implements tactics called `extract_lets` and `lift_lets` that
manipulate `let`/`let_fun` expressions. The `extract_lets` tactic
creates new local declarations extracted from any `let` and `let_fun`
expressions in the main goal. For top-level lets in the target, it is
like the `intros` tactic, but in general it can extract lets from deeper
subexpressions as well. The `lift_lets` tactic moves `let` and `let_fun`
expressions as far out of an expression as possible, but it does not
extract any new local declarations. The option `extract_lets +lift`
combines these behaviors.

This is a re-implementation of `extract_lets` and `lift_lets` from
mathlib. The new `extract_lets` is like doing `lift_lets; extract_lets`,
but it does not lift unextractable lets like `lift_lets`. The
`lift_lets; extract_lets` behavior is now handled by `extract_lets
+lift`. The new `lift_lets` tactic is a frontend to `extract_lets +lift`
machinery, which rather than creating new local definitions instead
represents the accumulated local declarations as top-level lets.

There are also conv tactics for both of these. The `extract_lets` has a
limitation due to the conv architecture; it can extract lets for a given
conv goal, but the local declarations don't survive outside conv. They
get zeta reduced immediately upon leaving conv.
2025-04-21 08:57:01 +00:00
Cameron Zwarich
02f7a1dd41 fix: correctly handle duplicate projections in the IR expand_reset_reuse pass (#8023)
This PR fixes the IR expand_reset_reuse pass to correctly handle
duplicate projections from the same base/index. This does not occur (at
least easily) with the old compiler, but it occurs when bootstrapping
Lean with the new compiler.
2025-04-21 03:27:32 +00:00
Leonardo de Moura
568a1b1a81 refactor: comm ring procedure in grind (#8034)
This PR makes the following modifications to the new comm ring procedure
in `grind`
1. Adds data-structures for representing equations (and their
justifications), basis, and queue of equations to be processed.
2. Adds `RingM` helper monad.
3. Adds equation simplification main loop
2025-04-21 02:53:43 +00:00
Leonardo de Moura
63cf571553 feat: add functions for converting ring reified terms back into Expr (#8033)
This PR adds functions for converting `CommRing` reified terms back into
Lean expressions.
2025-04-20 21:49:14 +00:00
Sebastian Ullrich
11f6326102 chore: un-orphan file (#8031)
This file is used in a test, which thus fails using `-DUSE_LAKE=ON`
2025-04-20 18:16:51 +00:00
Sebastian Ullrich
b5f191724d chore: stop taking constants from kernel env in synchronous case as well (#7915)
Makes the elaborator constant map truly independent of the kernel's in
preparation for the module system where declarations in the elab env may
in fact differ from the kernel env.
2025-04-20 17:56:14 +00:00
Leonardo de Moura
a49ad77754 feat: unsat comm ring equations in grind (#8032)
This PR adds support to `grind` for detecting unsatisfiable commutative
ring equations when the ring characteristic is known. Examples:
```lean
example (x : Int) : (x + 1)*(x - 1) = x^2 → False := by
  grind +ring

example (x y : Int) : (x + 1)*(x - 1)*y + y = y*x^2 + 1 → False := by
  grind +ring

example (x : UInt8) : (x + 1)*(x - 1) = x^2 → False := by
  grind +ring

example (x y : BitVec 8) : (x + 1)*(x - 1)*y + y = y*x^2 + 1 → False := by
  grind +ring
```
2025-04-20 17:26:46 +00:00
Kim Morrison
2cd874bd30 feat: additional List.findX lemmas (#8030)
This PR adds some missing lemmas about
`List/Array/Vector.findIdx?/findFinIdx?/findSome?/idxOf?`.
2025-04-20 08:08:53 +00:00
Leonardo de Moura
de27872f3f feat: basic CommRing support in grind (#8029)
This PR implements basic support for `CommRing` in `grind`. Terms are
already being reified and normalized. We still need to process the
equations, but `grind` can already prove simple examples such as:
```lean
open Lean.Grind in
example [CommRing α] (x : α) : (x + 1)*(x - 1) = x^2 - 1 := by
  grind +ring

open Lean.Grind in
example [CommRing α] [IsCharP α 256] (x : α) : (x + 16)*(x - 16) = x^2 := by
  grind +ring

example (x : Int) : (x + 1)*(x - 1) = x^2 - 1 := by
  grind +ring

example (x : UInt8) : (x + 16)*(x - 16) = x^2 := by
  grind +ring

example (x : Int) : (x + 1)^2 - 1 = x^2 + 2*x := by
  grind +ring

example (x : BitVec 8) : (x + 16)*(x - 16) = x^2 := by
  grind +ring

example (x : BitVec 8) : (x + 1)^2 - 1 = x^2 + 2*x := by
  grind +ring
```
2025-04-20 05:12:09 +00:00
Mac Malone
72e4f699c6 fix: lake: import-related bugs (#8026)
This PR fixes bugs in #7809 and #7909 that were not caught partially
because the `badImport` test had been disabled.

**Bugs Fixed:**

* Building by path no longer drops top-level logs.
* "bad import" errors are once again printed.
* Transitively imported precompiled modules are once again loaded during
elaboration.
2025-04-19 21:02:38 +00:00
Leonardo de Moura
876680001b feat: add Poly.simp? (#8027)
This PR adds `Poly.simp?` and improves the function for computing
S-polynomials.
2025-04-19 20:10:00 +00:00
JovanGerb
87930f59c3 fix: don't reset localInstances in delaboration (#8022)
This PR fixes a bug where pretty printing is done in a context with
cleared local instances. These were cleared since the local context is
updated during a name sanitization step, but preserving local instances
is valid since the modification to the local context only affects user
names.

This showed up when writing the mathlib delaborator for `max` and `min`
(https://github.com/leanprover-community/mathlib4/pull/23558#discussion_r2050787403)
2025-04-19 15:39:16 +00:00
Leonardo de Moura
f463b62ac3 feat: S-polynomials and cleanup (#8025)
This PR simplifies the `CommRing` monomials, and adds 
1. Monomial `lcm`
2. Monomial division
3. S-polynomials
2025-04-19 04:21:04 +00:00
Cameron Zwarich
9bb1e4f277 fix: correctly handle extern functions in the IR elim_dead_branches pass (#8017)
This PR makes the IR elim_dead_branches pass correctly handle extern
functions by considering them as having a top return value. This fix is
required to bootstrap the Init/ directory with the new compiler.
2025-04-18 17:28:32 +00:00
Sebastian Ullrich
a52e0c5ba5 chore: CI: bring back Lake build job (#8020)
Thanks to recent fixes
2025-04-18 13:42:27 +00:00
Joachim Breitner
02b206af9b fix: mkAppM to typecheck at .default transparency (#7957)
This PR ensures that `mkAppM` can be used to construct terms that are
only type-correct at at default transparency, even if we are in
`withReducible` (e.g. in simp), so that simp does not stumble over
simplifying `let` expression with simplifiable type.reliable.

Here is a reproducer of the issue this solves:
```
example (a b : Nat) (h : a = b):
  (let _ : id Bool := true; a) = (let _ : Bool := true; b) := by
  simp -zeta -zetaDelta [h]
```

This fixes #7826.
2025-04-18 09:23:51 +00:00
Joachim Breitner
e6343497a7 doc: RArray is now universe-polymorphic (#8018)
This PR adjusts the RArray docstring to the new reality from #8014.
2025-04-18 09:23:05 +00:00
Leonardo de Moura
27a7a0a2bd fix: CommRing multivariate polynomials (#8016)
This PR fixes several issues in the `CommRing` multivariate polynomial
library:
1. Replaces the previous array type with the universe polymorphic
`RArray`.
2. Properly eliminates cancelled monomials.
3. Sorts monomials in decreasing order.
4. Marks the parameter `p` of the `IsCharP` class as an output
parameter.
5. Adds `LawfulBEq` instances for the types `Power`, `Mon`, and `Poly`.
2025-04-18 04:34:05 +00:00
Cameron Zwarich
f163758bcf fix: correctly handle join points with no params in the IR elim_dead_branches pass (#8015)
This PR fixes the IR elim_dead_branches pass to correctly handle join
points with no params, which currently get considered unreachable. I was
not able to find an easy repro of this with the old compiler, but it
occurs when bootstrapping Lean with the new compiler.
2025-04-18 03:52:19 +00:00
Leonardo de Moura
32fe2391b9 feat: universe polymorphic RArray (#8014)
This PR makes `RArray` universe polymorphic.
2025-04-18 02:18:10 +00:00
Lean stage0 autoupdater
3cbffee94b chore: update stage0 2025-04-18 01:52:46 +00:00
Leonardo de Moura
807182d63e chore: allow RArray to be universe polymorphic (#8013)
This PR ensures that `RArray` can be made universe polymorphic. We need
an update-stage0 before finalizing this modification.
2025-04-18 01:10:44 +00:00
Lean stage0 autoupdater
a21377b9ec chore: update stage0 2025-04-18 00:52:57 +00:00
Leonardo de Moura
96fd2f195c feat: add debug.terminalTacticsAsSorry (#8012)
This PR adds the option `debug.terminalTacticsAsSorry`. When enabled,
terminal tactics such as `grind` and `omega` are replaced with `sorry`.
Useful for debugging and fixing bootstrapping issues.
2025-04-18 00:10:59 +00:00
Leonardo de Moura
5823d03283 feat: add IsCharP support to multivariate polynomial library (#8011)
This PR adds `IsCharP` support to the multivariate‑polynomial library in
`CommRing`.
2025-04-17 23:55:21 +00:00
Cameron Zwarich
d981fa0faf fix: make implemented_by of casesOn work correctly with hash consing (#8010)
This PR fixes caseOn expressions with an implemented_by to work
correctly with hash consing, even when the elaborator produces terms
that reconstruct the discriminant rather than just reusing a variable.
2025-04-17 23:32:59 +00:00
Cameron Zwarich
7b292090ce fix: restrict lifting outside of cases expressions on Decidable (#8009)
This PR restricts lifting outside of cases expressions on values of a
Decidable type, since we can't correctly represent the dependency on the
erased proposition in the later stages of the compiler.
2025-04-17 23:01:56 +00:00
Cameron Zwarich
f0033cd15e fix: consider params to be ground variables in specialization (#8008)
This PR changes specialization in the new code generator to consider
callee params to be ground variables, which improves the specialization
of polymorphic functions.
2025-04-17 22:34:16 +00:00
Cameron Zwarich
7bbcfdf712 fix: modify eager lambda lifting heuristics to match the old compiler (#8007)
This PR changes eager lambda lifting heuristics in the new compiler to
match the old compiler, which ensures that inlining/specializing monadic
code does not accidentally create mutual tail recursion that the code
generator can't handle.
2025-04-17 21:46:51 +00:00
Cameron Zwarich
130e2d93a5 fix: change inlining heuristics to match old code generator (#8006)
This PR changes the inlining heuristics of the new code generator to
match the old one, which ensures that monadic folds get sufficiently
inlined for their tail recursion to be exposed to the code generator.
2025-04-17 20:47:40 +00:00
Mac Malone
5b16ea98f5 fix: lake: extern_lib linking (#7987)
This PR fixes a bug in #7967 that broke external library linking.

This is slipped through because the FFI example no longer uses
`extern_lib`. As such, a separate `extern_lib` test has been added.
2025-04-17 19:33:22 +00:00
Rob23oba
acfc9c50d5 feat: hash map lemmas for filter, map and filterMap (#7400)
This PR adds lemmas for the `filter`, `map` and `filterMap` functions of
the hash map.

---------

Co-authored-by: jt0202 <johannes.tantow@gmail.com>
Co-authored-by: Johannes Tantow <44068763+jt0202@users.noreply.github.com>
Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
2025-04-17 10:15:52 +00:00
Markus Himmel
5af99cc840 chore: fix typo in simp docstring (#7998)
This PR fixes a typo in the `simp` hover.
2025-04-17 08:46:41 +00:00
Joachim Breitner
85f5a81f17 feat: FunInd: consume all type annotaions (#7997)
This PR removes all type annotations (optional paramters, auto
parameters, out params, semi-out params, not just optional parameters as
before) from the type of functional induction principles.
2025-04-17 07:52:17 +00:00
Cameron Zwarich
a81169bbe4 fix: don't eliminate fun decls in CSE in the base phase (#7996)
This PR disables CSE of local function declarations in the base phase of
the new compiler. This was introducing sharing between lambdas to bind
calls w/ `do` notation, which caused them to later no longer be inlined.
2025-04-17 04:57:21 +00:00
Kim Morrison
fdc62faa0f feat: reproduce Array.Perm API for Vector.Perm (#7994)
This PR reproduces the `Array.Perm` API for `Vector`. Both are still
significantly less developed than the API for `List.Perm`.
2025-04-17 02:39:48 +00:00
Leonardo de Moura
eaf46dfab1 feat: add Expr.toPoly (#7992)
This PR add a function for converting `CommRing` expressions into
multivariate polynomials.

Co-authored-by: Leonardo de Moura <leonardodemoura@Leonardos-MacBook-Pro.local>
2025-04-17 01:48:03 +00:00
Cameron Zwarich
d52b8e3cc1 fix: use lcAny in more cases of type erasure (#7990)
This PR adopts lcAny in more cases of type erasure in the new code
generator.
2025-04-16 22:53:18 +00:00
Kim Morrison
2a5373258f chore: add grind non-determinism repro (#7978)
This PR adds a repro for a non-determinism problem in `grind`.
2025-04-16 22:36:22 +00:00
Leonardo de Moura
d71e9cb96b feat: CommRing.Poly functions and theorems (#7989)
This PR adds functions and theorems for `CommRing` multivariate
polynomials.
2025-04-16 22:09:50 +00:00
Leonardo de Moura
a3a11ffaf9 feat: revlex and grevlex monomial orders (#7986)
This PR implements reverse lexicographical and graded reverse
lexicographical orders for `CommRing` monomials.
2025-04-16 18:03:53 +00:00
Markus Himmel
9d57ed83a9 chore: upstream Int lemmas from mathlib (#7983)
This PR upstreams many of the results from `Mathlib/Data/Int/Init.lean`.

Notably, we upstream the `simp` tag on `Int.natCast_pow`. While this is
desirable as a `simp` lemma, it is non-confluent with other good `simp`
lemmas like `Int.emod_bmod_congr`, and this will need to be addressed in
the future.
2025-04-16 17:45:08 +00:00
Rob23oba
7cca594a4a chore: adjust BEq classes (#7855)
This PR moves `ReflBEq` to `Init.Core` and changes `LawfulBEq` to extend
`ReflBEq`.

**BREAKING CHANGES:**
- The `refl` field of `ReflBEq` has been renamed to `rfl` to match
`LawfulBEq`
- `LawfulBEq` extends `ReflBEq`, so in particular `LawfulBEq.rfl` is no
longer valid
2025-04-16 13:24:23 +00:00
Kim Morrison
eed8a4828b chore: updates to List API before installing grind attributes (#7982) 2025-04-16 08:06:53 +00:00
Kim Morrison
4bea52c48e chore: failing grind test (#7981)
`propagateForallPropDown` is assuming the domain is a `Prop`
2025-04-16 07:24:53 +00:00
Markus Himmel
5a34ffb9b0 chore: upstream Nat material from mathlib (#7971)
This PR upstreams much of the material from `Mathlib/Data/Nat/Init.lean`
and `Mathlib/Data/Nat/Basic.lean`.
2025-04-16 06:55:32 +00:00
Leonardo de Moura
020b8834c3 feat: monomials for CommRing (#7980)
This PR adds a simple type for representing monomials in a `CommRing`.
This is going to be used in `grind`.
2025-04-16 02:39:31 +00:00
Mac Malone
7423e570f4 chore: lake: temporarily disable tests in tests (#7979)
These tests are currently flaky in `merge-ci` and nightly releases, so
they are being temporarily disabled. Whatever the issue is will be
debugged in a separate PR.
2025-04-16 02:29:53 +00:00
Mac Malone
b51115dac5 feat: IO.Process.SpawnArgs.inheritEnv (#6081)
This PR adds an `inheritEnv` field to `IO.Process.SpawnArgs`. If
`false`, the spawned process does not inherit its parent's environment.

For example, Lake will make use of this to ensure that build processes
do not use environment variables that Lake is not properly tracking with
its traces.
2025-04-16 00:25:32 +00:00
Mac Malone
46769b64c9 chore: lake: bootstrap Lean include directory (#7967)
This PR adds a `bootstrap` option to Lake which is used to identify the
core Lean package. This enables Lake to use the current stage's include
directory rather than the Lean toolchains when compiling Lean with Lean
in core.

**Breaking change:** The Lean library directory is no longer part of
`getLeanLinkSharedFlags`. FFI users should provide this option
separately when linking to Lean (e.g.. via `s!"-L{(←
getLeanLibDir).toString}"`). See the FFI example for a demonstration.
2025-04-15 23:15:53 +00:00
Mac Malone
7d26c7c4f3 feat: lake: build by source path (#7909)
This PR adds Lake support for building modules given their source file
path. This is made use of in both the CLI and the sever.

As a target specifier, `lake build Foo/Bar.lean` will now look for a
module in the workspace whose source file is `Foo/Bar.lean` and build
it. Facets are support via `lake build Foo/Bar.lean:o`. As such, `:` is
an illegal character in such file names (which is reasonable considering
its use in search paths like `PATH` on Linux).

In the server, `lake setup-file Foo/Bar.lean` will now try to lookup a
module for the source and and build its dependencies, ignoring the
imports specified. This allows Lake to return more specific
configuration for the module requested (e.g., library-specific dynlibs
and plugins). If the path cannot be found in the workspace, Lake will
fallback to its previous behavior.

Finally, like `setup-file`, `lake lean Foo/Bar.lean` will try to lookup
a module for the source path and use its more specific configuration if
possible.

Closes #2756.
2025-04-15 23:12:36 +00:00
Kyle Miller
dd84829282 feat: allow omission of => ?_ in induction/cases tactics (#7830)
This PR modifies the syntax of `induction`, `cases`, and other tactics
that use `Lean.Parser.Tactic.inductionAlts`. If a case omits `=> ...`
then it is assumed to be `=> ?_`. Example:
```lean
example (p : Nat × Nat) : p.1 = p.1 := by
  cases p with | _ p1 p2
  /-
  case mk
  p1 p2 : Nat
  ⊢ (p1, p2).fst = (p1, p2).fst
  -/
```
This works with multiple cases as well. Example:
```lean
example (n : Nat) : n + 1 = 1 + n := by
  induction n with | zero | succ n ih
  /-
  case zero
  ⊢ 0 + 1 = 1 + 0
  
  case succ
  n : Nat
  ih : n + 1 = 1 + n
  ⊢ n + 1 + 1 = 1 + (n + 1)
  -/
```
The `induction n with | zero | succ n ih` is short for `induction n with
| zero | succ n ih => ?_`, which is short for `induction n with | zero
=> ?_ | succ n ih => ?_`. Note that a consequence of parsing is that
only the last alternative can omit `=>`. Any `=>`-free alternatives
before an alternative with `=>` will be a part of that alternative.

Rationale:
- In the future we may require `tacticSeq` to be indented. For
one-constructor types, this lets the rest of the tactic sequence not
need indentation.
- This is a semi-structured alternative to the `cases'`/`induction'`
tactics in mathlib.
2025-04-15 22:03:46 +00:00
Mac Malone
17d3daca8a feat: lake: track trace inputs & related fixes (#7906)
This PR changes Lake build traces to track their mixed inputs. The
tracked inputs are saved as part of the `.trace` file, which can
significantly assist in debugging trace issues. In addition, this PR
tweaks some existing Lake traces. Most significant, module olean traces
no longer incorporate their module's source trace.
2025-04-15 19:23:02 +00:00
Henrik Böving
712bb070f9 feat: make bv_decide work on simp normal forms of shifts (#7976)
This PR ensure that `bv_decide` can handle the simp normal form of a
shift.

Consider:
```lean
theorem test1 (b s : BitVec 5) (hb : b = 0) (hs : s ≠ 0)
  : b <<< s = 0 := by
  bv_decide
```
This works out, however:
```lean
theorem test2 (b s : BitVec 5) (hb : b = 0) (hs : s ≠ 0)
  : b <<< s = 0 := by
  simp
  bv_decide
```
this fails because the `simp` normal form adds `toNat` to the right hand
argument of the `<<<` and `bv_decide` cannot deal with shifts by
non-constant `Nat`.

Discovered by @spdskatr
2025-04-15 17:26:19 +00:00
Kim Morrison
525fd2697c fix: reduce priorities of CommRing parent projections (#7975)
This PR reduces the priority of the parent projections of
`Lean.Grind.CommRing`, to avoid these being used in typeclass inference
in Mathlib.
2025-04-15 13:45:53 +00:00
Markus Himmel
c82159e09b feat: Int.bmod lemmas (#7933)
This PR adds lemmas about `Int.bmod` to achieve parity between
`Int.bmod` and `Int.emod`/`Int.fmod`/`Int.tmod`. Furthermore, it adds
missing lemmas for `emod`/`fmod`/`tmod` and performs cleanup on names
and statements for all four operations, also with a view towards
increasing consistency with the corresponding `Nat.mod` lemmas.
2025-04-15 12:26:49 +00:00
Kim Morrison
c3996aadb8 feat: Array.count_erase lemma (#7939)
This PR adds `Array.count_erase` and specializations.
2025-04-15 04:02:29 +00:00
Eric Wieser
bb2f51a230 feat: link Lake.EStateT with EStateM (#7963)
This PR adds helper functions to convert between `Lake.EStateT` and
`EStateM`.

In the longer run the two types could just be merged.
2025-04-15 01:05:47 +00:00
Mac Malone
d5027c1a29 chore: lake: rm unused import in DSL.DeclUtil (#7964) 2025-04-15 00:01:02 +00:00
Henrik Böving
bfb02be281 fix: bv_decide default match with as many arms as constructors (#7961)
This PR fixes a bug in bv_decide where if it was presented with a match
on an enum with as many arms as constructors but the last arm being a
default match it would (wrongly) give up on the match.
2025-04-14 14:58:13 +00:00
Sebastian Ullrich
0076ba03d4 fix: race condition in IO.getTaskState (#7945)
This PR fixes a potential race between `IO.getTaskState` and the task in
question finishing, resulting in undefined behavior.

All task state must be accessed under the respective lock.
2025-04-14 14:08:36 +00:00
Henrik Böving
8e9da7a1bc feat: wait on dedicated tasks after main is finished (#7958)
This PR ensures that after `main` is finished we still wait on dedicated
tasks instead of exiting forcefully. If users wish to violently kill
their dedicated tasks at the end of main instead they can run
`IO.Process.exit` at the end of `main` instead.
2025-04-14 11:53:54 +00:00
Henrik Böving
ac738a8e81 perf: use mimalloc in compactor hashmaps (#7929)
This PR changes the compactor hashmap to use mimalloc which speeds up
olean serialization.
2025-04-14 09:11:34 +00:00
Lean stage0 autoupdater
689acab1d3 chore: update stage0 2025-04-14 07:03:16 +00:00
Kyle Miller
de25524dd6 feat: preparation for #7830 (#7955)
This PR adds the tactic implementation for #7830, before changing the
syntax after a stage0 update. It will allow optional RHSs in induction
cases.
2025-04-14 06:22:04 +00:00
Kyle Miller
48a9bfb73d doc: add docstrings to mkFreshUserName etc (#7947)
This PR adds some docstrings to clarify the functions of
`Lean.mkFreshId`, `Lean.Core.mkFreshUserName`,
`Lean.Elab.Term.mkFreshBinderName`, and
`Lean.Meta.mkFreshBinderNameForTactic`.
2025-04-14 04:17:45 +00:00
Kyle Miller
7c9519e60c fix: make sure all_goals restores state on failure (#7950)
This PR modifies `all_goals` so that in recovery mode it commits changes
to the state only for those goals for which the tactic succeeds (while
preserving the new message log state). Before, we were trusting that
failing tactics left things in a reasonable state, but now we roll back
and admit the goal. The changes also fixes a bug where we were rolling
back only the metacontext state and not the tactic state, leading to an
inconsistent state (a goal list with metavariables not in the
metacontext). Closes #7883

Alternatively we could stop on the first error, however it is helpful to
see what the tactic did to each goal while interactively writing a
tactic script. There is some non-monotonicity here though since tactics
can solve for metavariables that appear in successive goals, and
conceivably a later goal succeeds only if a previous one does. Given
that the non-monotonicity is limited to recovery mode (which is for
example the RHS and not the LHS of the `<;>` combinator), we think this
is acceptable.

Another justification for the change to roll back the state on each
failure is that we need to admit goals in the failing cases. When a
tactic throws an error, we cannot assume the goal list is meaningful.
Rolling back lets us admit just the goal the tactic started with,
without needing to try to work out which new metavariables should be
admitted in the error state, allowing the tactic to continue trying the
tactic on the next goal.
2025-04-14 04:16:28 +00:00
Leonardo de Moura
4e1dbe1ae8 chore: add [grind ext] funext (#7951)
Co-authored-by: Kim Morrison <kim@tqft.net>
2025-04-14 02:52:44 +00:00
Kim Morrison
a0b63deb04 feat: updates to List/Array.Perm API (#7953)
This PR generalizes some typeclass hypotheses in the `List.Perm` API
(away from `DecidableEq`), and reproduces `List.Perm.mem_iff` for
`Array`, and fixes a mistake in the statement of `Array.Perm.extract`.
2025-04-14 01:17:02 +00:00
Lean stage0 autoupdater
c5e20c980c chore: update stage0 2025-04-13 23:32:03 +00:00
Leonardo de Moura
cd5b495573 feat: add [grind ext] attribute (#7949)
This PR adds the attribute `[grind ext]`. It is used to select which
`[ext]` theorems should be used by `grind`. The option `grind +extAll`
instructs `grind` to use all `[ext]` theorems available in the
environment.
After update stage0, we need to add the builtin `[grind ext]`
annotations to key theorems such as `funext`.
2025-04-13 22:08:36 +00:00
Leonardo de Moura
2337b95676 feat: improve case split heuristics in grind (#7946)
This PR improves the case split heuristics in `grind`.
2025-04-13 17:57:56 +00:00
Sebastian Ullrich
973f521c46 chore: fix cmake install exclude patterns (#7941) 2025-04-13 12:32:55 +00:00
Sebastian Ullrich
069456ea9c chore: disable flaky test 2025-04-13 13:18:05 +02:00
Kim Morrison
aa2cae8801 feat: List/Array/Vector.count_replace lemmas (#7938)
This PR adds lemmas about `List/Array/Vector.countP/count` interacting
with `replace`. (Specializing to `_self` and `_ne` lemmas doesn't seem
useful, as there will still be an `if` on the RHS.)
2025-04-13 03:10:19 +00:00
Leonardo de Moura
f513c35742 feat: lookahead in grind (#7937)
This PR implements a lookahead feature to reduce the size of the search
space in `grind`. It is currently effective only for arithmetic atoms.
2025-04-13 03:01:47 +00:00
Kim Morrison
d7cc0fd754 chore: add grind annotations for Nat/Int.min/max (#7934) 2025-04-13 01:48:17 +00:00
Kim Morrison
5f8847151d chore: generalize List.Perm.take (#7936)
Thanks @b-mehta for these generalizations.

---------

Co-authored-by: Bhavik Mehta <bhavikmehta8@gmail.com>
2025-04-13 01:45:48 +00:00
Kim Morrison
8bc9c4f154 chore: cleanup 'if normalization' grind example (#7935) 2025-04-13 01:09:38 +00:00
Henrik Böving
dd7ca772d8 refactor: more complete channel implementation for Std.Channel (#7819)
This PR extends `Std.Channel` to provide a full sync and async API, as
well as unbounded, zero sized and bounded channels.

A few notes on the implementation:
- the bounded channel is inspired by [Go channels on
steroids](https://docs.google.com/document/d/1yIAYmbvL3JxOKOjuCyon7JhW4cSv1wy5hC0ApeGMV9s/pub)
though currently doesn't do any of the lock-free optimizations
- @mhuisi convinced me that having a non-closable channel may be a good
idea as this alleviates the need for error handling which is very
annoying when working with `Task`. This does complicate the API a little
bit and I'm not quite sure whether this is a choice we want users to
give. An alternative to this would be to just write `send!` that panics
on sending to a closed channel (receiving from a closed channel is not
an error), this is for example the behavior that golang goes with.
2025-04-12 21:02:24 +00:00
Lean stage0 autoupdater
85a0232e87 chore: update stage0 2025-04-12 11:07:22 +00:00
Sebastian Ullrich
8ea6465e6d chore: CI: disable Linux 32bit (#7924)
A 2GB heap is just not that much even before fragmentation
2025-04-12 09:29:13 +00:00
Leonardo de Moura
38ed4346c2 chore: improve grind.clear_aux_decls error message (#7931)
cc @kim-em
2025-04-12 02:39:51 +00:00
Leonardo de Moura
2657f4e62c chore: move test to correct directory (#7932) 2025-04-11 19:46:47 -07:00
Leonardo de Moura
d4767a08b0 chore: another grind fixed test (#7930)
cc @kim-em
2025-04-11 19:43:35 -07:00
Leonardo de Moura
f562e72e59 chore: move test (#7921)
This test is easy for `grind`, we just need to annotate `Nat.min_def`.
2025-04-12 01:40:54 +00:00
Leonardo de Moura
5a6d45817d fix: nontermination in grind (#7928)
This PR fixes a nontermination issue in `grind`.
2025-04-11 21:06:07 +00:00
Leonardo de Moura
264095be7f fix: missing propagation and split filter in grind (#7926)
This PR fixes two issues that were preventing `grind` to solve
`getElem?_eq_some_iff`.
1. Missing propagation rule for `Exists p = False`
2. Missing conditions at `isCongrToPrevSplit` a filter for discarding
unnecessary case-splits.
2025-04-11 19:26:50 +00:00
Sebastian Ullrich
0669a04704 chore: CI: limit CCACHE_SIZE to 400MB (#7922) 2025-04-11 17:09:16 +00:00
Sebastian Ullrich
5cd352588c perf: use mimalloc with important C++ hash maps (#7868)
`unordered_map`/`unordered_set` does an allocation per insert, use
mimalloc for them for important hash maps
2025-04-11 16:23:33 +00:00
Henrik Böving
e9cc776f22 perf: bv_decide DecidableEq fast path using hash comparison (#7920)
This PR introduces a fast path based on comparing the (cached) hash
value to the `DecidableEq` instance of the core expression data type in
`bv_decide`'s bitblaster.

As we use a good hash function ™️ this should allow us to short
circuit to "not equal" quicker (if appropriate) than currently as we
will often not have to traverse all the way down to the actual conflict.
This in turn should speed up traversing of bucket chains during hash
collisions.
2025-04-11 15:00:41 +00:00
Lean stage0 autoupdater
e79fef15df chore: update stage0 2025-04-11 14:12:34 +00:00
Sebastian Ullrich
c672934f11 chore: add "Init size" benchmark (#7918) 2025-04-11 13:15:27 +00:00
Sebastian Ullrich
582877d2d3 feat: environment extension data can be split into .olean.server (#7914)
This PR adds a function hook `PersistentEnvExtension.saveEntriesFn` that
can be used to store server-only metadata such as position information
and docstrings that should not affect (re)builds.
2025-04-11 13:06:19 +00:00
Marc Huisinga
39ce3d14f4 test: make test deterministic (#7916) 2025-04-11 11:16:16 +00:00
Kim Morrison
32758aa712 feat: lemmas about permutations (#7912)
This PR adds `List.Perm.take/drop`, and `Array.Perm.extract`,
restricting permutations to sublist / subarrays when they are constant
elsewhere.
2025-04-11 08:13:58 +00:00
Kim Morrison
0f6e35dc63 feat: missing List/Array/Vector lemmas about isSome_idxOf? and relatives (#7913)
This PR adds some missing `List/Array/Vector lemmas` about
`isSome_idxOf?`, `isSome_finIdxOf?`, `isSome_findFinIdx?,
`isSome_findIdx?` and the corresponding `isNone` versions.
2025-04-11 07:45:46 +00:00
Kim Morrison
2528188dde chore: add failing grind test (#7910)
Adds a currently failing test, for a `grind` improvement.
2025-04-11 03:22:56 +00:00
Leonardo de Moura
1cdadfd47a chore: cleanup grind cutsat trace messages (#7908) 2025-04-11 00:52:18 +00:00
Kyle Miller
e07c59c831 fix: eliminate panic when inductive has autoparam parameter with underdetermined type (#7905)
This PR fixes an issue introduced bug #6125 where an `inductive` or
`structure` with an autoimplicit parameter with a type that has a
metavariable would lead to a panic. Closes #7788.

This was due to switching from `Term.addAutoBoundImplicits'` to
`Term.addAutoBoundImplicits` and not properly handling metavariables in
the parameters list. To fix this, now the inductive type headers record
the abstracted type and the number of parameters, rather than record the
parameters, the type, the local context, and the local instances. A
benefit to this over `Term.addAutoBoundImplicits'` is that the type's
parameters do not appear twice in the local context.
2025-04-11 00:19:53 +00:00
Leonardo de Moura
cbd38ceadd fix: mbtc and cast issue in grind (#7907)
This PR fixes two bugs in `grind`. 
1. Model-based theory combination was creating type incorrect terms.
2. `Nat.cast` vs `NatCast.natCast` issue during normalization.
2025-04-10 22:46:56 +00:00
Kyle Miller
c46f1e941c fix: sorry in Infoview shouldn't show module name (#7813)
This PR fixes an issue where `let n : Nat := sorry` in the Infoview
pretty prints as ``n : ℕ := sorry `«Foo:17:17»``. This was caused by
top-level expressions being pretty printed with the same rules as
Infoview hovers. Closes #6715. Refactors `Lean.Widget.ppExprTagged`; now
it takes a delaborator, and downstream users should configure their own
pretty printer option overrides if necessary if they used the `explicit`
argument (see `Lean.Widget.makePopup.ppExprForPopup` for an example).
Breaking change: `ppExprTagged` does not set `pp.proofs` on the root
expression.
2025-04-10 21:47:07 +00:00
Markus Himmel
cf3b257ccd chore: Option cleanup (#7897)
This PR cleans up the `Option` development, upstreaming some results
from mathlib in the process.

Notable changes:
- the name `<op>_eq_some_iff` is preferred over `<op>_eq_some`
- the `simp` normal form for `<$>` is `Option.map`, for `>>=` is
`Option.bind` and for `<|>` is `Option.orElse` (for the former two, this
was already true before this PR). All further lemmas about these
operations are now stated only in terms of
`Option.map`/`Option.bind`/`Option.orElse`. Previously, in some cases
both versions were available, with a prime used to disambiguate (the
primed version was usually the "non-ascii-art" version). Now, there are
no lemmas about the ascii-art versions besides the ones turning them
into the non-ascii-art operations, and there is only one version of
every lemma, about the non-ascii-art operation, and named without a
prime.
2025-04-10 18:53:30 +00:00
Kyle Miller
09ab15dc6d fix: remove infinite loop in withFnRefWhenTagAppFns (#7904)
This PR fixes an oversight in `withFnRefWhenTagAppFns` that causes an
infinite loop when the expression is a constant. This affected pretty
printing of zero-field structures when `pp.tagAppFns` was true (used by
docgen and verso). Closes #7898.
2025-04-10 17:16:29 +00:00
Sebastian Ullrich
e631efd817 feat: introduce Elab.inServer option (#7902)
This PR introduces a dedicated option for checking whether elaborators
are running in the language server.
2025-04-10 14:51:37 +00:00
Sebastian Graf
d2f4ce0158 fix: Add Inhabited instance for OptionT (#7901)
This PR adds `instance [Pure f] : Inhabited (OptionT f α)`, so that
`Inhabited (OptionT Id Empty)` synthesizes.

Co-authored-by: Sebastian Graf <sg@lean-fro.org>
2025-04-10 14:49:03 +00:00
Sebastian Ullrich
69536808ca feat: read/writeModuleDataParts API for serialization with cross-file sharing (#7854)
This PR introduces fundamental API to distribute module data across
multiple files in preparation for the module system.
2025-04-10 13:32:24 +00:00
Markus Himmel
3d5dd15de4 chore: move bmod results from LemmasAux.lean to DivMod/Lemmas.lean (#7899)
This PR shuffles some results about integers around to make sure that
all material that currently exists about `Int.bmod` is located in
`DivMod/Lemmas.lean` and not downstream of that.
2025-04-10 12:07:11 +00:00
Lean stage0 autoupdater
91c245663b chore: update stage0 2025-04-10 12:26:07 +00:00
Sebastian Ullrich
1421b6145e fix: cancellation of synchronous part of previous elaboration (#7882)
This PR fixes a regression where elaboration of a previous document
version is not cancelled on changes to the document.

Done by removing the default from `SnapshotTask.cancelTk?` and
consistently passing the current thread's token for synchronous
elaboration steps.
2025-04-10 11:43:41 +00:00
Kim Morrison
bffa642ad6 feat: Lean.Grind.IsCharP (#7870)
This PR adds a mixin typeclass for `Lean.Grind.CommRing` recording the
characteristic of the ring, and constructs instances for `Int`, `IntX`,
`UIntX`, and `BitVec`.
2025-04-10 08:36:42 +00:00
Kim Morrison
deef1c2739 feat: BitVec.pow and Pow (BitVec w) Nat (#7893)
This PR adds `BitVec.pow` and `Pow (BitVec w) Nat`. The implementation
is the naive one, and should later be replaced by an `@[extern]`. This
is tracked at https://github.com/leanprover/lean4/issues/7887.
2025-04-10 05:21:30 +00:00
Kim Morrison
acf42bd30b chore: add simp lemma Int.cast x = x for x : Int (#7891)
This PR adds the rfl simp lemma `Int.cast x = x` for `x : Int`.
2025-04-10 02:35:06 +00:00
Leonardo de Moura
4947215325 feat: improve funext support in grind (#7892)
This PR improves the support for `funext` in `grind`. We will push
another PR to minimize the number of case-splits later.
2025-04-10 01:57:27 +00:00
Kim Morrison
6e7209dfa3 chore: add Int.dvd_iff_bmod_eq_zero (#7890)
This PR adds missing lemmas about `Int.bmod`, parallel to lemmas about
the other `mod` variants.
2025-04-10 01:36:42 +00:00
Kim Morrison
97a00b3881 chore: variant of Int.toNat_sub (#7889)
This PR adds `Int.toNat_sub''` a variant of `Int.toNat_sub` taking
inequality hypotheses, rather than expecting the arguments to be casts
of natural numbers. This is parallel to the existing `toNat_add` and
`toNat_mul`.
2025-04-10 01:34:48 +00:00
Kim Morrison
d758b4c862 chore: Fin.ofNat'_mul, analogous to existing add lemmas (#7888)
This PR adds `Fin.ofNat'_mul` and `Fin.mul_ofNat'`, parallel to the
existing lemmas about `add`.
2025-04-10 01:32:47 +00:00
Kim Morrison
61d7716ad8 feat: UIntX.pow and Pow UIntX Nat instances (#7886)
This PR adds `UIntX.pow` and `Pow UIntX Nat` instances, and similarly
for signed fixed-width integers. These are currently only the naive
implementation, and will need to be subsequently replaced via
`@[extern]` with fast implementations (tracked at #7887).
2025-04-10 00:27:48 +00:00
Kim Morrison
05f16ed279 feat: UIntX.ofInt (#7880)
This PR adds the functions `UIntX.ofInt`, and basic lemmas.
2025-04-09 23:50:29 +00:00
2255 changed files with 59746 additions and 19470 deletions

View File

@@ -46,7 +46,7 @@ jobs:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPRESS: true
# current cache limit
CCACHE_MAXSIZE: 600M
CCACHE_MAXSIZE: 400M
# squelch error message about missing nixpkgs channel
NIX_BUILD_SHELL: bash
LSAN_OPTIONS: max_leaks=10
@@ -82,7 +82,7 @@ jobs:
- name: CI Merge Checkout
run: |
git fetch --depth=1 origin ${{ github.sha }}
git checkout FETCH_HEAD flake.nix flake.lock
git checkout FETCH_HEAD flake.nix flake.lock script/prepare-*
if: github.event_name == 'pull_request'
# (needs to be after "Checkout" so files don't get overridden)
- name: Setup emsdk
@@ -99,7 +99,6 @@ jobs:
if: matrix.cmultilib
- name: Cache
id: restore-cache
if: matrix.name != 'Linux Lake'
uses: actions/cache/restore@v4
with:
# NOTE: must be in sync with `save` below
@@ -194,7 +193,7 @@ jobs:
run: |
ulimit -c unlimited # coredumps
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
if: (matrix.wasm || !matrix.cross) && inputs.check-level >= 1
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.name == 'Linux release')
- name: Test Summary
uses: test-summary/action@v2
with:

View File

@@ -139,20 +139,21 @@ jobs:
let large = ${{ github.repository == 'leanprover/lean4' }};
const isPr = "${{ github.event_name }}" == "pull_request";
let matrix = [
/* TODO: to be updated to new LLVM
{
"name": "Linux LLVM",
"os": "ubuntu-latest",
"release": false,
"check-level": 2,
"shell": "nix develop .#oldGlibc -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
// reverse-ffi needs to be updated to link to LLVM libraries
"CTEST_OPTIONS": "-E 'foreign|leanlaketest_reverse-ffi'",
"CMAKE_OPTIONS": "-DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config"
},
}, */
{
// portable release build: use channel with older glibc (2.26)
"name": "Linux release",
@@ -160,25 +161,23 @@ jobs:
"release": true,
"check-level": 0,
"shell": "nix develop .#oldGlibc -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'"
},
// deactivated due to bugs
/*
{
"name": "Linux Lake",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
// just a secondary PR build job for now
"check-level": isPr ? 0 : 3,
"check-level": 0,
// just a secondary build job for now until false positives can be excluded
"secondary": true,
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
// TODO: why does this fail?
"CTEST_OPTIONS": "-E 'scopedMacros'"
// TODO: importStructure is not compatible with .olean caching
// TODO: why does scopedMacros fail?
"CTEST_OPTIONS": "-E 'scopedMacros|importStructure'"
},
*/
{
"name": "Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
@@ -210,7 +209,7 @@ jobs:
"release": true,
"check-level": 2,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
@@ -221,7 +220,7 @@ jobs:
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-aarch64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar", // https://github.com/actions/runner-images/issues/2619
@@ -242,7 +241,7 @@ jobs:
"CMAKE_OPTIONS": "-G \"Unix Makefiles\"",
// for reasons unknown, interactivetests are flaky on Windows
"CTEST_OPTIONS": "--repeat until-pass:2",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-mingw.sh lean-llvm*",
"binary-check": "ldd"
},
@@ -253,20 +252,21 @@ jobs:
"release": true,
"check-level": 2,
"shell": "nix develop .#oldGlibcAArch -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/19.1.2/lean-llvm-aarch64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*"
},
{
"name": "Linux 32bit",
"os": "ubuntu-latest",
// Use 32bit on stage0 and stage1 to keep oleans compatible
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86 -DCMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DPKG_CONFIG_EXECUTABLE=/usr/bin/i386-linux-gnu-pkg-config",
"cmultilib": true,
"release": true,
"check-level": 2,
"cross": true,
"shell": "bash -euxo pipefail {0}"
}
// Started running out of memory building expensive modules, a 2GB heap is just not that much even before fragmentation
//{
// "name": "Linux 32bit",
// "os": "ubuntu-latest",
// // Use 32bit on stage0 and stage1 to keep oleans compatible
// "CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86 -DCMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DPKG_CONFIG_EXECUTABLE=/usr/bin/i386-linux-gnu-pkg-config",
// "cmultilib": true,
// "release": true,
// "check-level": 2,
// "cross": true,
// "shell": "bash -euxo pipefail {0}"
//}
// {
// "name": "Web Assembly",
// "os": "ubuntu-latest",

View File

@@ -1,142 +0,0 @@
name: Nix CI
on:
push:
branches:
- master
tags:
- '*'
pull_request:
merge_group:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# see ci.yml
configure:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.result }}
steps:
- name: Configure build matrix
id: set-matrix
uses: actions/github-script@v7
with:
script: |
let large = ${{ github.repository == 'leanprover/lean4' }};
let matrix = [
{
"name": "Nix Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x8" : "ubuntu-latest",
}
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
return matrix;
Build:
needs: [configure]
runs-on: ${{ matrix.os }}
defaults:
run:
shell: nix run .#ciShell -- bash -euxo pipefail {0}
strategy:
matrix:
include: ${{fromJson(needs.configure.outputs.matrix)}}
# complete all jobs
fail-fast: false
name: ${{ matrix.name }}
env:
NIX_BUILD_ARGS: --print-build-logs --fallback
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Set Up Nix Cache
uses: actions/cache@v4
with:
path: nix-store-cache
key: ${{ matrix.name }}-nix-store-cache-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-nix-store-cache
save-always: true
- name: Further Set Up Nix Cache
shell: bash -euxo pipefail {0}
run: |
# Nix seems to mutate the cache, so make a copy
cp -r nix-store-cache nix-store-cache-copy || true
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
with:
extra-conf: |
extra-sandbox-paths = /nix/var/cache/ccache?
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
- name: Prepare CCache Cache
run: |
sudo mkdir -m0770 -p /nix/var/cache/ccache
sudo chown -R $USER /nix/var/cache/ccache
- name: Setup CCache Cache
uses: actions/cache@v4
with:
path: /nix/var/cache/ccache
key: ${{ matrix.name }}-nix-ccache-${{ github.sha }}
# fall back to (latest) previous cache
restore-keys: |
${{ matrix.name }}-nix-ccache
save-always: true
- name: Further Set Up CCache Cache
run: |
sudo chown -R root:nixbld /nix/var/cache
sudo chmod -R 770 /nix/var/cache
- name: Build
run: |
nix build $NIX_BUILD_ARGS .#cacheRoots -o push-build
- name: Test
run: |
nix build --keep-failed $NIX_BUILD_ARGS .#test -o push-test || (ln -s /tmp/nix-build-*/build/source/src/build ./push-test; false)
- name: Test Summary
uses: test-summary/action@v2
with:
paths: push-test/test-results.xml
if: always()
continue-on-error: true
- name: Build manual
run: |
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,inked} -o push-doc
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc
# https://github.com/netlify/cli/issues/1809
cp -r --dereference ./result ./dist
if: matrix.name == 'Nix Linux'
- name: Rebuild Nix Store Cache
run: |
rm -rf nix-store-cache || true
nix copy ./push-* --to file://$PWD/nix-store-cache?compression=none
- id: deploy-info
name: Compute Deployment Metadata
run: |
set -e
python3 -c 'import base64; print("alias="+base64.urlsafe_b64encode(bytes.fromhex("${{github.sha}}")).decode("utf-8").rstrip("="))' >> "$GITHUB_OUTPUT"
echo "message=`git log -1 --pretty=format:"%s"`" >> "$GITHUB_OUTPUT"
- name: Publish manual to Netlify
uses: nwtgck/actions-netlify@v3.0
id: publish-manual
with:
publish-dir: ./dist
production-branch: master
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: |
${{ github.event_name == 'pull_request' && format('pr#{0}: {1}', github.event.number, github.event.pull_request.title) || format('ref/{0}: {1}', github.ref_name, steps.deploy-info.outputs.message) }}
alias: ${{ steps.deploy-info.outputs.alias }}
enable-commit-comment: false
enable-pull-request-comment: false
github-deployment-environment: "lean-lang.org/lean4/doc"
fails-without-credentials: false
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: "b8e805d2-7e9b-4f80-91fb-a84d72fc4a68"
- name: Fixup CCache Cache
run: |
sudo chown -R $USER /nix/var/cache

View File

@@ -5,13 +5,15 @@ option(USE_MIMALLOC "use mimalloc" ON)
# store all variables passed on the command line into CL_ARGS so we can pass them to the stage builds
# https://stackoverflow.com/a/48555098/161659
# MUST be done before call to 'project'
# Use standard release build (discarding LEAN_CXX_EXTRA_FLAGS etc.) for stage0 by default since it is assumed to be "good", but still pass through CMake platform arguments (compiler, toolchain file, ..).
# Use standard release build (discarding LEAN_EXTRA_CXX_FLAGS etc.) for stage0 by default since it is assumed to be "good", but still pass through CMake platform arguments (compiler, toolchain file, ..).
# Use `STAGE0_` prefix to pass variables to stage0 explicitly.
get_cmake_property(vars CACHE_VARIABLES)
foreach(var ${vars})
get_property(currentHelpString CACHE "${var}" PROPERTY HELPSTRING)
if("${var}" MATCHES "STAGE0_(.*)")
list(APPEND STAGE0_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
elseif("${var}" MATCHES "STAGE1_(.*)")
list(APPEND STAGE1_ARGS "-D${CMAKE_MATCH_1}=${${var}}")
elseif("${currentHelpString}" MATCHES "No help, variable specified on the command line." OR "${currentHelpString}" STREQUAL "")
list(APPEND CL_ARGS "-D${var}=${${var}}")
if("${var}" MATCHES "USE_GMP|CHECK_OLEAN_VERSION")
@@ -37,10 +39,14 @@ endif()
# Don't do anything with cadical on wasm
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# On CI Linux, we source cadical from Nix instead; see flake.nix
find_program(CADICAL cadical)
if(NOT CADICAL)
set(CADICAL_CXX c++)
if (CADICAL_USE_CUSTOM_CXX)
set(CADICAL_CXX ${CMAKE_CXX_COMPILER})
set(CADICAL_CXXFLAGS "${LEAN_EXTRA_CXX_FLAGS}")
set(CADICAL_LDFLAGS "-Wl,-rpath=\\$$ORIGIN/../lib")
endif()
find_program(CCACHE ccache)
if(CCACHE)
set(CADICAL_CXX "${CCACHE} ${CADICAL_CXX}")
@@ -55,8 +61,11 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-2.1.2
CONFIGURE_COMMAND ""
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk
CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
CXX=${CADICAL_CXX}
CXXFLAGS=${CADICAL_CXXFLAGS}
LDFLAGS=${CADICAL_LDFLAGS}
BUILD_IN_SOURCE ON
INSTALL_COMMAND "")
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
@@ -77,26 +86,29 @@ if (USE_MIMALLOC)
list(APPEND EXTRA_DEPENDS mimalloc)
endif()
ExternalProject_add(stage0
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
SOURCE_SUBDIR src
BINARY_DIR stage0
# do not rebuild stage0 when git hash changes; it's not from this commit anyway
# (however, CI will override this as we need to embed the githash into the stage 1 library built
# by stage 0)
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND "" # skip install
DEPENDS ${EXTRA_DEPENDS}
)
if (NOT STAGE1_PREV_STAGE)
ExternalProject_add(stage0
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
SOURCE_SUBDIR src
BINARY_DIR stage0
# do not rebuild stage0 when git hash changes; it's not from this commit anyway
# (however, CI will override this as we need to embed the githash into the stage 1 library built
# by stage 0)
CMAKE_ARGS -DSTAGE=0 -DUSE_GITHASH=OFF ${PLATFORM_ARGS} ${STAGE0_ARGS}
BUILD_ALWAYS ON # cmake doesn't auto-detect changes without a download method
INSTALL_COMMAND "" # skip install
DEPENDS ${EXTRA_DEPENDS}
)
list(APPEND EXTRA_DEPENDS stage0)
endif()
ExternalProject_add(stage1
SOURCE_DIR "${LEAN_SOURCE_DIR}"
SOURCE_SUBDIR src
BINARY_DIR stage1
CMAKE_ARGS -DSTAGE=1 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage0 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${STAGE0_CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS}
CMAKE_ARGS -DSTAGE=1 -DPREV_STAGE=${CMAKE_BINARY_DIR}/stage0 -DPREV_STAGE_CMAKE_EXECUTABLE_SUFFIX=${STAGE0_CMAKE_EXECUTABLE_SUFFIX} ${CL_ARGS} ${STAGE1_ARGS}
BUILD_ALWAYS ON
INSTALL_COMMAND ""
DEPENDS stage0
DEPENDS ${EXTRA_DEPENDS}
STEP_TARGETS configure
)
ExternalProject_add(stage2
@@ -108,6 +120,7 @@ ExternalProject_add(stage2
INSTALL_COMMAND ""
DEPENDS stage1
EXCLUDE_FROM_ALL ON
STEP_TARGETS configure
)
ExternalProject_add(stage3
SOURCE_DIR "${LEAN_SOURCE_DIR}"

View File

@@ -24,9 +24,9 @@
},
{
"name": "reldebug",
"displayName": "Release with debug info build config",
"displayName": "Release with assertions enabled",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
"CMAKE_BUILD_TYPE": "RelWithAssert"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/reldebug"

View File

@@ -7,8 +7,9 @@
/.github/ @kim-em
/RELEASES.md @kim-em
/src/kernel/ @leodemoura
/src/library/compiler/ @zwarich
/src/lake/ @tydeu
/src/Lean/Compiler/ @leodemoura
/src/Lean/Compiler/ @leodemoura @zwarich
/src/Lean/Data/Lsp/ @mhuisi
/src/Lean/Elab/Deriving/ @kim-em
/src/Lean/Elab/Tactic/ @kim-em

View File

@@ -2,20 +2,19 @@ This is the repository for **Lean 4**.
# About
- [Quickstart](https://lean-lang.org/lean4/doc/quickstart.html)
- [Quickstart](https://lean-lang.org/documentation/setup/)
- [Homepage](https://lean-lang.org)
- [Theorem Proving Tutorial](https://lean-lang.org/theorem_proving_in_lean4/)
- [Functional Programming in Lean](https://lean-lang.org/functional_programming_in_lean/)
- [Documentation Overview](https://lean-lang.org/lean4/doc/)
- [Documentation Overview](https://lean-lang.org/documentation/)
- [Language Reference](https://lean-lang.org/doc/reference/latest/)
- [Release notes](RELEASES.md) starting at v4.0.0-m3
- [Examples](https://lean-lang.org/lean4/doc/examples.html)
- [Examples](https://lean-lang.org/documentation/examples/)
- [External Contribution Guidelines](CONTRIBUTING.md)
- [FAQ](https://lean-lang.org/lean4/doc/faq.html)
# Installation
See [Setting Up Lean](https://lean-lang.org/lean4/doc/setup.html).
See [Setting Up Lean](https://lean-lang.org/documentation/setup/).
# Contributing
@@ -23,4 +22,4 @@ Please read our [Contribution Guidelines](CONTRIBUTING.md) first.
# Building from Source
See [Building Lean](https://lean-lang.org/lean4/doc/make/index.html) (documentation source: [doc/make/index.md](doc/make/index.md)).
See [Building Lean](doc/make/index.md).

10
doc/README.md Normal file
View File

@@ -0,0 +1,10 @@
# Developer Documentation and Examples
This directory contains documentation that describes how to work on
Lean itself, as well as examples that are included in documentation
that's hosted on the Lean website. The `make` directory contains
information on building Lean, and the `dev` directory describes how to
work on Lean.
The [documentation section](https://lean-lang.org/documentation) has
links to documentation that describes how to use Lean itself.

View File

@@ -1,46 +0,0 @@
# Summary
- [What is Lean](./whatIsLean.md)
- [Tour of Lean](./tour.md)
- [Setting Up Lean](./quickstart.md)
- [Extended Setup Notes](./setup.md)
- [Theorem Proving in Lean](./tpil.md)
- [Functional Programming in Lean](fplean.md)
- [Examples](./examples.md)
- [Palindromes](examples/palindromes.lean.md)
- [Binary Search Trees](examples/bintree.lean.md)
- [A Certified Type Checker](examples/tc.lean.md)
- [The Well-Typed Interpreter](examples/interp.lean.md)
- [Dependent de Bruijn Indices](examples/deBruijn.lean.md)
- [Parametric Higher-Order Abstract Syntax](examples/phoas.lean.md)
- [Syntax Examples](./syntax_examples.md)
- [Balanced Parentheses](./syntax_example.md)
- [Arithmetic DSL](./metaprogramming-arith.md)
# Language Manual
- [The Lean Reference Manual](./reference.md)
# Other
- [Frequently Asked Questions](./faq.md)
- [Significant Changes from Lean 3](./lean3changes.md)
- [Syntax Highlighting Lean in LaTeX](./syntax_highlight_in_latex.md)
- [User Widgets](examples/widgets.lean.md)
- [Semantic Highlighting](./semantic_highlighting.md)
# Development
- [Development Guide](./dev/index.md)
- [Building Lean](./make/index.md)
- [Ubuntu Setup](./make/ubuntu.md)
- [macOS Setup](./make/osx-10.9.md)
- [Windows MSYS2 Setup](./make/msys2.md)
- [Windows with WSL](./make/wsl.md)
- [Bootstrapping](./dev/bootstrap.md)
- [Testing](./dev/testing.md)
- [Debugging](./dev/debugging.md)
- [Commit Convention](./dev/commit_convention.md)
- [Release checklist](./dev/release_checklist.md)
- [Building This Manual](./dev/mdbook.md)
- [Foreign Function Interface](./dev/ffi.md)

View File

@@ -1,786 +0,0 @@
@charset "UTF-8";
/*
Copyright © 2019 Clément Pit-Claudel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*******************************/
/* CSS reset for .alectryon-io */
/*******************************/
.content {
/*
Use `initial` instead of `contents` to avoid a browser bug which removes
the element from the accessibility tree.
https://developer.mozilla.org/en-US/docs/Web/CSS/display#display_contents
*/
display: initial;
}
.alectryon-io blockquote {
line-height: inherit;
}
.alectryon-io blockquote:after {
display: none;
}
.alectryon-io label {
display: inline;
font-size: inherit;
margin: 0;
}
.alectryon-io a {
text-decoration: none !important;
font-style: oblique !important;
color: unset;
}
/* Undo <small> and <blockquote>, added to improve RSS rendering. */
.alectryon-io small.alectryon-output,
.alectryon-io small.alectryon-type-info {
font-size: inherit;
}
.alectryon-io blockquote.alectryon-goal,
.alectryon-io blockquote.alectryon-message {
font-weight: normal;
font-size: inherit;
}
/***************/
/* Main styles */
/***************/
.alectryon-coqdoc .doc .code,
.alectryon-coqdoc .doc .comment,
.alectryon-coqdoc .doc .inlinecode,
.alectryon-mref,
.alectryon-block, .alectryon-io,
.alectryon-toggle-label, .alectryon-banner {
font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important;
font-size: 0.875em;
font-feature-settings: "COQX" 1 /* Coq ligatures */, "XV00" 1 /* Legacy */, "calt" 1 /* Fallback */;
line-height: initial;
}
.alectryon-io, .alectryon-block, .alectryon-toggle-label, .alectryon-banner {
overflow: visible;
overflow-wrap: break-word;
position: relative;
white-space: pre-wrap;
}
/*
CoqIDE doesn't turn off the unicode bidirectional algorithm (and PG simply
respects the user's `bidi-display-reordering` setting), so don't turn it off
here either. But beware unexpected results like `Definition test_אב := 0.`
.alectryon-io span {
direction: ltr;
unicode-bidi: bidi-override;
}
In any case, make an exception for comments:
.highlight .c {
direction: embed;
unicode-bidi: initial;
}
*/
.alectryon-mref,
.alectryon-mref-marker {
align-self: center;
box-sizing: border-box;
display: inline-block;
font-size: 80%;
font-weight: bold;
line-height: 1;
box-shadow: 0 0 0 1pt black;
padding: 1pt 0.3em;
text-decoration: none;
}
.alectryon-block .alectryon-mref-marker,
.alectryon-io .alectryon-mref-marker {
user-select: none;
margin: -0.25em 0 -0.25em 0.5em;
}
.alectryon-inline .alectryon-mref-marker {
margin: -0.25em 0.15em -0.25em 0.625em; /* 625 = 0.5em / 80% */
}
.alectryon-mref {
color: inherit;
margin: -0.5em 0.25em;
}
.alectryon-goal:target .goal-separator .alectryon-mref-marker,
:target > .alectryon-mref-marker {
animation: blink 0.2s step-start 0s 3 normal none;
background-color: #fcaf3e;
position: relative;
}
@keyframes blink {
50% {
box-shadow: 0 0 0 3pt #fcaf3e, 0 0 0 4pt black;
z-index: 10;
}
}
.alectryon-toggle,
.alectryon-io .alectryon-extra-goal-toggle {
display: none;
}
.alectryon-bubble,
.alectryon-io label,
.alectryon-toggle-label {
cursor: pointer;
}
.alectryon-toggle-label {
display: block;
font-size: 0.8em;
}
.alectryon-io .alectryon-input {
padding: 0.1em 0; /* Enlarge the hitbox slightly to fill interline gaps */
}
.alectryon-io .alectryon-token {
white-space: pre-wrap;
display: inline;
}
.alectryon-io .alectryon-sentence.alectryon-target .alectryon-input {
/* FIXME if keywords were bolder we wouldn't need !important */
font-weight: bold !important; /* Use !important to avoid a * selector */
}
.alectryon-bubble:before,
.alectryon-toggle-label:before,
.alectryon-io label.alectryon-input:after,
.alectryon-io .alectryon-goal > label:before {
border: 1px solid #babdb6;
border-radius: 1em;
box-sizing: border-box;
content: '';
display: inline-block;
font-weight: bold;
height: 0.25em;
margin-bottom: 0.15em;
vertical-align: middle;
width: 0.75em;
}
.alectryon-toggle-label:before,
.alectryon-io .alectryon-goal > label:before {
margin-right: 0.25em;
}
.alectryon-io .alectryon-goal > label:before {
margin-top: 0.125em;
}
.alectryon-io label.alectryon-input {
padding-right: 1em; /* Prevent line wraps before the checkbox bubble */
}
.alectryon-io label.alectryon-input:after {
margin-left: 0.25em;
margin-right: -1em; /* Compensate for the anti-wrapping space */
}
.alectryon-failed {
/* Underlines are broken in Chrome (they reset at each element boundary)… */
/* text-decoration: red wavy underline; */
/* … but it isn't too noticeable with dots */
text-decoration: red dotted underline;
text-decoration-skip-ink: none;
/* Chrome prints background images in low resolution, yielding a blurry underline */
/* background: bottom / 0.3em auto repeat-x url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyLjY0NiAxLjg1MiIgaGVpZ2h0PSI4IiB3aWR0aD0iMTAiPjxwYXRoIGQ9Ik0wIC4yNjVjLjc5NCAwIC41MyAxLjMyMiAxLjMyMyAxLjMyMi43OTQgMCAuNTMtMS4zMjIgMS4zMjMtMS4zMjIiIGZpbGw9Im5vbmUiIHN0cm9rZT0icmVkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=); */
}
/* Wrapping :hover rules in a media query ensures that tapping a Coq sentence
doesn't trigger its :hover state (otherwise, on mobile, tapping a sentence to
hide its output causes it to remain visible (its :hover state gets triggered.
We only do it for the default style though, since other styles don't put the
output over the main text, so showing too much is not an issue. */
@media (any-hover: hover) {
.alectryon-bubble:hover:before,
.alectryon-toggle-label:hover:before,
.alectryon-io label.alectryon-input:hover:after {
background: #eeeeec;
}
.alectryon-io label.alectryon-input:hover {
text-decoration: underline dotted #babdb6;
text-shadow: 0 0 1px rgb(46, 52, 54, 0.3); /* #2e3436 + opacity */
}
.alectryon-io .alectryon-sentence:hover .alectryon-output,
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper,
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper {
z-index: 2; /* Place hovered goals above .alectryon-sentence.alectryon-target ones */
}
}
.alectryon-toggle:checked + .alectryon-toggle-label:before,
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked + label.alectryon-input:after,
.alectryon-io .alectryon-extra-goal-toggle:checked + .alectryon-goal > label:before {
background-color: #babdb6;
border-color: #babdb6;
}
/* Disable clicks on sentences when the document-wide toggle is set. */
.alectryon-toggle:checked + label + .alectryon-container label.alectryon-input {
cursor: unset;
pointer-events: none;
}
/* Hide individual checkboxes when the document-wide toggle is set. */
.alectryon-toggle:checked + label + .alectryon-container label.alectryon-input:after {
display: none;
}
/* .alectryon-output is displayed by toggles, :hover, and .alectryon-target rules */
.alectryon-io .alectryon-output {
box-sizing: border-box;
display: none;
left: 0;
right: 0;
position: absolute;
padding: 0.25em 0;
overflow: visible; /* Let box-shadows overflow */
z-index: 1; /* Default to an index lower than that used by :hover */
}
.alectryon-io .alectryon-type-info-wrapper {
position: absolute;
display: inline-block;
width: 100%;
}
.alectryon-io .alectryon-type-info-wrapper.full-width {
left: 0;
min-width: 100%;
max-width: 100%;
}
.alectryon-io .alectryon-type-info .goal-separator {
height: unset;
margin-top: 0em;
}
.alectryon-io .alectryon-type-info-wrapper .alectryon-type-info {
box-sizing: border-box;
bottom: 100%;
position: absolute;
/*padding: 0.25em 0;*/
visibility: hidden;
overflow: visible; /* Let box-shadows overflow */
z-index: 1; /* Default to an index lower than that used by :hover */
white-space: pre-wrap !important;
}
.alectryon-io .alectryon-type-info-wrapper .alectryon-type-info .alectryon-goal.alectryon-docstring {
white-space: pre-wrap !important;
}
@media (any-hover: hover) { /* See note above about this @media query */
.alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) {
display: block;
}
.alectryon-io.output-hidden .alectryon-sentence:hover .alectryon-output:not(:hover) {
display: none !important;
}
.alectryon-io.type-info-hidden .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info,
.alectryon-io.type-info-hidden .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info {
/*visibility: hidden !important;*/
}
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info,
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info {
visibility: visible;
transition-delay: 0.5s;
}
}
.alectryon-io .alectryon-sentence.alectryon-target .alectryon-output {
display: block;
}
/* Indicate active (hovered or targeted) goals with a shadow. */
.alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) .alectryon-messages,
.alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .alectryon-messages,
.alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) .alectryon-goals,
.alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .alectryon-goals,
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info {
box-shadow: 2px 2px 2px gray;
}
.alectryon-io .alectryon-extra-goals .alectryon-goal .goal-hyps {
display: none;
}
.alectryon-io .alectryon-extra-goals .alectryon-extra-goal-toggle:not(:checked) + .alectryon-goal label.goal-separator hr {
/* Dashes indicate that the hypotheses are hidden */
border-top-style: dashed;
}
/* Show just a small preview of the other goals; this is undone by the
"extra-goal" toggle and by :hover and .alectryon-target in windowed mode. */
.alectryon-io .alectryon-extra-goals .alectryon-goal .goal-conclusion {
max-height: 5.2em;
overflow-y: auto;
/* Combining overflow-y: auto with display: inline-block causes extra space
to be added below the box. vertical-align: middle gets rid of it. */
vertical-align: middle;
}
.alectryon-io .alectryon-goals,
.alectryon-io .alectryon-messages {
background: #f6f7f6;
/*border: thin solid #d3d7cf; /* Convenient when pre's background is already #EEE */
display: block;
padding: 0.25em;
}
.alectryon-message::before {
content: '';
float: right;
/* etc/svg/square-bubble-xl.svg */
background: url("data:image/svg+xml,%3Csvg width='14' height='14' viewBox='0 0 3.704 3.704' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill-rule='evenodd' stroke='%23000' stroke-width='.264'%3E%3Cpath d='M.794.934h2.115M.794 1.463h1.455M.794 1.992h1.852'/%3E%3C/g%3E%3Cpath d='M.132.14v2.646h.794v.661l.926-.661h1.72V.14z' fill='none' stroke='%23000' stroke-width='.265'/%3E%3C/svg%3E") top right no-repeat;
height: 14px;
width: 14px;
}
.alectryon-toggle:checked + label + .alectryon-container {
width: unset;
}
/* Show goals when a toggle is set */
.alectryon-toggle:checked + label + .alectryon-container label.alectryon-input + .alectryon-output,
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-output {
display: block;
position: static;
width: unset;
background: unset; /* Override the backgrounds set in floating in windowed mode */
padding: 0.25em 0; /* Re-assert so that later :hover rules don't override this padding */
}
.alectryon-toggle:checked + label + .alectryon-container label.alectryon-input + .alectryon-output .goal-hyps,
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-output .goal-hyps {
/* Overridden back in windowed style */
flex-flow: row wrap;
justify-content: flex-start;
}
.alectryon-toggle:checked + label + .alectryon-container .alectryon-sentence .alectryon-output > div,
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-output > div {
display: block;
}
.alectryon-io .alectryon-extra-goal-toggle:checked + .alectryon-goal .goal-hyps {
display: flex;
}
.alectryon-io .alectryon-extra-goal-toggle:checked + .alectryon-goal .goal-conclusion {
max-height: unset;
overflow-y: unset;
}
.alectryon-toggle:checked + label + .alectryon-container .alectryon-sentence > .alectryon-toggle ~ .alectryon-wsp,
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-wsp {
display: none;
}
.alectryon-io .alectryon-messages,
.alectryon-io .alectryon-message,
.alectryon-io .alectryon-goals,
.alectryon-io .alectryon-goal,
.alectryon-io .goal-hyps > span,
.alectryon-io .goal-conclusion {
border-radius: 0.15em;
}
.alectryon-io .alectryon-goal,
.alectryon-io .alectryon-message {
align-items: center;
background: #f6f7f6;
border: 0em;
display: block;
flex-direction: column;
margin: 0.25em;
padding: 0.5em;
position: relative;
}
.alectryon-io .goal-hyps {
align-content: space-around;
align-items: baseline;
display: flex;
flex-flow: column nowrap; /* re-stated in windowed mode */
justify-content: space-around;
/* LATER use a gap property instead of margins once supported */
margin: -0.15em -0.25em; /* -0.15em to cancel the item spacing */
padding-bottom: 0.35em; /* 0.5em-0.15em to cancel the 0.5em of .goal-separator */
}
.alectryon-io .goal-hyps > br {
display: none; /* Only for RSS readers */
}
.alectryon-io .goal-hyps > span,
.alectryon-io .goal-conclusion {
/*background: #eeeeec;*/
display: inline-block;
padding: 0.15em 0.35em;
}
.alectryon-io .goal-hyps > span {
align-items: baseline;
display: inline-flex;
margin: 0.15em 0.25em;
}
.alectryon-block var,
.alectryon-inline var,
.alectryon-io .goal-hyps > span > var {
font-weight: 600;
font-style: unset;
}
.alectryon-io .goal-hyps > span > var {
/* Shrink the list of names, but let it grow as long as space is available. */
flex-basis: min-content;
flex-grow: 1;
}
.alectryon-io .goal-hyps > span b {
font-weight: 600;
margin: 0 0 0 0.5em;
white-space: pre;
}
.alectryon-io .hyp-body,
.alectryon-io .hyp-type {
display: flex;
align-items: baseline;
}
.alectryon-io .goal-separator {
align-items: center;
display: flex;
flex-direction: row;
height: 1em; /* Fixed height to ignore goal name and markers */
margin-top: -0.5em; /* Compensated in .goal-hyps when shown */
}
.alectryon-io .goal-separator hr {
border: none;
border-top: thin solid #555753;
display: block;
flex-grow: 1;
margin: 0;
}
.alectryon-io .goal-separator .goal-name {
font-size: 0.75em;
margin-left: 0.5em;
}
/**********/
/* Banner */
/**********/
.alectryon-banner {
background: #eeeeec;
border: 1px solid #babcbd;
font-size: 0.75em;
padding: 0.25em;
text-align: center;
margin: 1em 0;
}
.alectryon-banner a {
cursor: pointer;
text-decoration: underline;
}
.alectryon-banner kbd {
background: #d3d7cf;
border-radius: 0.15em;
border: 1px solid #babdb6;
box-sizing: border-box;
display: inline-block;
font-family: inherit;
font-size: 0.9em;
height: 1.3em;
line-height: 1.2em;
margin: -0.25em 0;
padding: 0 0.25em;
vertical-align: middle;
}
/**********/
/* Toggle */
/**********/
.alectryon-toggle-label {
margin: 1rem 0;
}
/******************/
/* Floating style */
/******************/
/* If there's space, display goals to the right of the code, not below it. */
@media (min-width: 80rem) {
/* Unlike the windowed case, we don't want to move output blocks to the side
when they are both :checked and -targeted, since it gets confusing as
things jump around; hence the commented-output part of the selector,
which would otherwise increase specificity */
.alectryon-floating .alectryon-sentence.alectryon-target /* > .alectryon-toggle ~ */ .alectryon-output,
.alectryon-floating .alectryon-sentence:hover .alectryon-output {
top: 0;
left: 100%;
right: -100%;
padding: 0 0.5em;
position: absolute;
}
.alectryon-floating .alectryon-output {
min-height: 100%;
}
.alectryon-floating .alectryon-sentence:hover .alectryon-output {
background: white; /* Ensure that short goals hide long ones */
}
/* This odd margin-bottom property prevents the sticky div from bumping
against the bottom of its container (.alectryon-output). The alternative
would be enlarging .alectryon-output, but that would cause overflows,
enlarging scrollbars and yielding scrolling towards the bottom of the
page. Doing things this way instead makes it possible to restrict
.alectryon-output to a reasonable size (100%, through top = bottom = 0).
See also https://stackoverflow.com/questions/43909940/. */
/* See note on specificity above */
.alectryon-floating .alectryon-sentence.alectryon-target /* > .alectryon-toggle ~ */ .alectryon-output > div,
.alectryon-floating .alectryon-sentence:hover .alectryon-output > div {
margin-bottom: -200%;
position: sticky;
top: 0;
}
.alectryon-floating .alectryon-toggle:checked + label + .alectryon-container .alectryon-sentence .alectryon-output > div,
.alectryon-floating .alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-output > div {
margin-bottom: unset; /* Undo the margin */
}
/* Float underneath the current fragment
@media (max-width: 80rem) {
.alectryon-floating .alectryon-output {
top: 100%;
}
} */
}
/********************/
/* Multi-pane style */
/********************/
.alectryon-windowed {
border: 0 solid #2e3436;
box-sizing: border-box;
}
.alectryon-windowed .alectryon-sentence:hover .alectryon-output {
background: white; /* Ensure that short goals hide long ones */
}
.alectryon-windowed .alectryon-output {
position: fixed; /* Overwritten by the :checked rules */
}
/* See note about specificity below */
.alectryon-windowed .alectryon-sentence:hover .alectryon-output,
.alectryon-windowed .alectryon-sentence.alectryon-target > .alectryon-toggle ~ .alectryon-output {
padding: 0.5em;
overflow-y: auto; /* Windowed contents may need to scroll */
}
.alectryon-windowed .alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) .alectryon-messages,
.alectryon-windowed .alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .alectryon-messages,
.alectryon-windowed .alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) .alectryon-goals,
.alectryon-windowed .alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .alectryon-goals {
box-shadow: none; /* A shadow is unnecessary here and incompatible with overflow-y set to auto */
}
.alectryon-windowed .alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .goal-hyps {
/* Restated to override the :checked style */
flex-flow: column nowrap;
justify-content: space-around;
}
.alectryon-windowed .alectryon-sentence.alectryon-target .alectryon-extra-goals .alectryon-goal .goal-conclusion
/* Like .alectryon-io .alectryon-extra-goal-toggle:checked + .alectryon-goal .goal-conclusion */ {
max-height: unset;
overflow-y: unset;
}
.alectryon-windowed .alectryon-output > div {
display: flex; /* Put messages after goals */
flex-direction: column-reverse;
}
/*********************/
/* Standalone styles */
/*********************/
.alectryon-standalone {
font-family: 'IBM Plex Serif', 'PT Serif', 'Merriweather', 'DejaVu Serif', serif;
line-height: 1.5;
}
@media screen and (min-width: 50rem) {
html.alectryon-standalone {
/* Prevent flickering when hovering a block causes scrollbars to appear. */
margin-left: calc(100vw - 100%);
margin-right: 0;
}
}
/* Coqdoc */
.alectryon-coqdoc .doc .code,
.alectryon-coqdoc .doc .inlinecode,
.alectryon-coqdoc .doc .comment {
display: inline;
}
.alectryon-coqdoc .doc .comment {
color: #eeeeec;
}
.alectryon-coqdoc .doc .paragraph {
height: 0.75em;
}
/* Centered, Floating */
.alectryon-standalone .alectryon-centered,
.alectryon-standalone .alectryon-floating {
max-width: 50rem;
margin: auto;
}
@media (min-width: 80rem) {
.alectryon-standalone .alectryon-floating {
max-width: 80rem;
}
.alectryon-standalone .alectryon-floating > * {
width: 50%;
margin-left: 0;
}
}
/* Windowed */
.alectryon-standalone .alectryon-windowed {
display: block;
margin: 0;
overflow-y: auto;
position: absolute;
padding: 0 1em;
}
.alectryon-standalone .alectryon-windowed > * {
/* Override properties of docutils_basic.css */
margin-left: 0;
max-width: unset;
}
.alectryon-standalone .alectryon-windowed .alectryon-io {
box-sizing: border-box;
width: 100%;
}
/* No need to predicate the :hover rules below on :not(:checked), since left,
right, top, and bottom will be inactived by the :checked rules setting
position to static */
/* Specificity: We want the output to stay inline when hovered while unfolded
(:checked), but we want it to move when it's targeted (i.e. when the user
is browsing goals one by one using the keyboard, in which case we want to
goals to appear in consistent locations). The selectors below ensure
that :hover < :checked < -targeted in terms of specificity. */
/* LATER: Reimplement this stuff with CSS variables */
.alectryon-windowed .alectryon-sentence.alectryon-target > .alectryon-toggle ~ .alectryon-output {
position: fixed;
}
@media screen and (min-width: 60rem) {
.alectryon-standalone .alectryon-windowed {
border-right-width: thin;
bottom: 0;
left: 0;
right: 50%;
top: 0;
}
.alectryon-standalone .alectryon-windowed .alectryon-sentence:hover .alectryon-output,
.alectryon-standalone .alectryon-windowed .alectryon-sentence.alectryon-target .alectryon-output {
bottom: 0;
left: 50%;
right: 0;
top: 0;
}
}
@media screen and (max-width: 60rem) {
.alectryon-standalone .alectryon-windowed {
border-bottom-width: 1px;
bottom: 40%;
left: 0;
right: 0;
top: 0;
}
.alectryon-standalone .alectryon-windowed .alectryon-sentence:hover .alectryon-output,
.alectryon-standalone .alectryon-windowed .alectryon-sentence.alectryon-target .alectryon-output {
bottom: 0;
left: 0;
right: 0;
top: 60%;
}
}

View File

@@ -1,190 +0,0 @@
var Alectryon;
(function(Alectryon) {
(function (slideshow) {
function anchor(sentence) { return "#" + sentence.id; }
function current_sentence() { return slideshow.sentences[slideshow.pos]; }
function unhighlight() {
var sentence = current_sentence();
if (sentence) sentence.classList.remove("alectryon-target");
slideshow.pos = -1;
}
function highlight(sentence) {
sentence.classList.add("alectryon-target");
}
function scroll(sentence) {
// Put the top of the current fragment close to the top of the
// screen, but scroll it out of view if showing it requires pushing
// the sentence past half of the screen. If sentence is already in
// a reasonable position, don't move.
var parent = sentence.parentElement;
/* We want to scroll the whole document, so start at root… */
while (parent && !parent.classList.contains("alectryon-root"))
parent = parent.parentElement;
/* … and work up from there to find a scrollable element.
parent.scrollHeight can be greater than parent.clientHeight
without showing scrollbars, so we add a 10px buffer. */
while (parent && parent.scrollHeight <= parent.clientHeight + 10)
parent = parent.parentElement;
/* <body> and <html> elements can have their client rect overflow
* the window if their height is unset, so scroll the window
* instead */
if (parent && (parent.nodeName == "BODY" || parent.nodeName == "HTML"))
parent = null;
var rect = function(e) { return e.getBoundingClientRect(); };
var parent_box = parent ? rect(parent) : { y: 0, height: window.innerHeight },
sentence_y = rect(sentence).y - parent_box.y,
fragment_y = rect(sentence.parentElement).y - parent_box.y;
// The assertion below sometimes fails for the first element in a block.
// console.assert(sentence_y >= fragment_y);
if (sentence_y < 0.1 * parent_box.height ||
sentence_y > 0.7 * parent_box.height) {
(parent || window).scrollBy(
0, Math.max(sentence_y - 0.5 * parent_box.height,
fragment_y - 0.1 * parent_box.height));
}
}
function highlighted(pos) {
return slideshow.pos == pos;
}
function navigate(pos, inhibitScroll) {
unhighlight();
slideshow.pos = Math.min(Math.max(pos, 0), slideshow.sentences.length - 1);
var sentence = current_sentence();
highlight(sentence);
if (!inhibitScroll)
scroll(sentence);
}
var keys = {
PAGE_UP: 33,
PAGE_DOWN: 34,
ARROW_UP: 38,
ARROW_DOWN: 40,
h: 72, l: 76, p: 80, n: 78
};
function onkeydown(e) {
e = e || window.event;
if (e.ctrlKey || e.metaKey) {
if (e.keyCode == keys.ARROW_UP)
slideshow.previous();
else if (e.keyCode == keys.ARROW_DOWN)
slideshow.next();
else
return;
} else {
// if (e.keyCode == keys.PAGE_UP || e.keyCode == keys.p || e.keyCode == keys.h)
// slideshow.previous();
// else if (e.keyCode == keys.PAGE_DOWN || e.keyCode == keys.n || e.keyCode == keys.l)
// slideshow.next();
// else
return;
}
e.preventDefault();
}
function start() {
slideshow.navigate(0);
}
function toggleHighlight(idx) {
if (highlighted(idx))
unhighlight();
else
navigate(idx, true);
}
function handleClick(evt) {
if (evt.ctrlKey || evt.metaKey) {
var sentence = evt.currentTarget;
// Ensure that the goal is shown on the side, not inline
var checkbox = sentence.getElementsByClassName("alectryon-toggle")[0];
if (checkbox)
checkbox.checked = false;
toggleHighlight(sentence.alectryon_index);
evt.preventDefault();
}
}
function init() {
document.onkeydown = onkeydown;
slideshow.pos = -1;
slideshow.sentences = Array.from(document.getElementsByClassName("alectryon-sentence"));
slideshow.sentences.forEach(function (s, idx) {
s.addEventListener('click', handleClick, false);
s.alectryon_index = idx;
});
}
slideshow.start = start;
slideshow.end = unhighlight;
slideshow.navigate = navigate;
slideshow.next = function() { navigate(slideshow.pos + 1); };
slideshow.previous = function() { navigate(slideshow.pos + -1); };
window.addEventListener('DOMContentLoaded', init);
})(Alectryon.slideshow || (Alectryon.slideshow = {}));
(function (styles) {
var styleNames = ["centered", "floating", "windowed"];
function className(style) {
return "alectryon-" + style;
}
function setStyle(style) {
var root = document.getElementsByClassName("alectryon-root")[0];
styleNames.forEach(function (s) {
root.classList.remove(className(s)); });
root.classList.add(className(style));
}
function init() {
var banner = document.getElementsByClassName("alectryon-banner")[0];
if (banner) {
banner.append(" Style: ");
styleNames.forEach(function (styleName, idx) {
var s = styleName;
var a = document.createElement("a");
a.onclick = function() { setStyle(s); };
a.append(styleName);
if (idx > 0) banner.append("; ");
banner.appendChild(a);
});
banner.append(".");
}
}
window.addEventListener('DOMContentLoaded', init);
styles.setStyle = setStyle;
})(Alectryon.styles || (Alectryon.styles = {}));
})(Alectryon || (Alectryon = {}));
function setHidden(elements, isVisible, token) {
for (let i = 0; i < elements.length; i++) {
if (isVisible) {
elements[i].classList.remove(token)
} else {
elements[i].classList.add(token)
}
}
}
function toggleShowTypes(checkbox) {
setHidden(document.getElementsByClassName("alectryon-io"), checkbox.checked, "type-info-hidden")
}
function toggleShowGoals(checkbox) {
setHidden(document.getElementsByClassName("alectryon-io"), checkbox.checked, "output-hidden")
}

View File

@@ -1,14 +0,0 @@
Lean binary distribution
------------------------
The binary distribution package contains:
- Lean executable (located in the sub-directory bin)
- Standard library (located in the sub-directory lib/lean/library)
Assuming you are in the same directory this file is located,
the following command executes a simple set of examples
% bin/lean examples/ex.lean
For more information on Lean and supported editors, please see https://lean-lang.org/documentation/.

View File

@@ -1,21 +0,0 @@
[book]
authors = ["Leonardo de Moura", "Sebastian Ullrich"]
language = "en"
multilingual = false
src = "."
title = "Lean Documentation Overview"
[build]
build-dir = "out"
[output.html]
git-repository-url = "https://github.com/leanprover/lean4"
additional-css = ["alectryon.css", "pygments.css"]
additional-js = ["alectryon.js"]
[output.html.fold]
enable = true
level = 0
[output.html.playground.boring-prefixes]
lean = "# "

View File

@@ -1 +0,0 @@
# Booleans

View File

@@ -1,885 +0,0 @@
# Declarations
-- TODO (fix)
Declaration Names
=================
A declaration name is a hierarchical [identifier](lexical_structure.md#identifiers) that is interpreted relative to the current namespace as well as (during lookup) to the set of open namespaces.
```lean
namespace A
opaque B.c : Nat
#print B.c -- opaque A.B.c : Nat
end A
#print A.B.c -- opaque A.B.c : Nat
open A
#print B.c -- opaque A.B.c : Nat
```
Declaration names starting with an underscore are reserved for internal use. Names starting with the special atomic name ``_root_`` are interpreted as absolute names.
```lean
opaque a : Nat
namespace A
opaque a : Int
#print _root_.a -- opaque a : Nat
#print A.a -- opaque A.a : Int
end A
```
Contexts and Telescopes
=======================
When processing user input, Lean first parses text to a raw expression format. It then uses background information and type constants to disambiguate overloaded symbols and infer implicit arguments, resulting in a fully-formed expression. This process is known as *elaboration*.
As hinted in [Expression Syntax](expressions.md#expression_syntax),
expressions are parsed and elaborated with respect to an *environment*
and a *local context*. Roughly speaking, an environment represents the
state of Lean at the point where an expression is parsed, including
previously declared axioms, constants, definitions, and theorems. In a
given environment, a *local context* consists of a sequence ``(a₁ :
α₁) (a₂ : α₂) ... (aₙ : αₙ)`` where each ``aᵢ`` is a name denoting a
local constant and each ``αᵢ`` is an expression of type ``Sort u`` for
some ``u`` which can involve elements of the environment and the local
constants ``aⱼ`` for ``j < i``.
Intuitively, a local context is a list of variables that are held constant while an expression is being elaborated. Consider the following
```lean
def f (a b : Nat) : Nat → Nat := fun c => a + (b + c)
```
Here the expression ``fun c => a + (b + c)`` is elaborated in the context ``(a : Nat) (b : Nat)`` and the expression ``a + (b + c)`` is elaborated in the context ``(a : Nat) (b : Nat) (c : Nat)``. If you replace the expression ``a + (b + c)`` with an underscore, the error message from Lean will include the current *goal*:
```
a b c : Nat
⊢ Nat
```
Here ``a b c : Nat`` indicates the local context, and the second ``Nat`` indicates the expected type of the result.
A *context* is sometimes called a *telescope*, but the latter is used more generally to include a sequence of declarations occurring relative to a given context. For example, relative to the context ``(a₁ : α₁) (a₂ : α₂) ... (aₙ : αₙ)``, the types ``βᵢ`` in a telescope ``(b₁ : β₁) (b₂ : β₂) ... (bₙ : βₙ)`` can refer to ``a₁, ..., aₙ``. Thus a context can be viewed as a telescope relative to the empty context.
Telescopes are often used to describe a list of arguments, or parameters, to a declaration. In such cases, it is often notationally convenient to let ``(a : α)`` stand for a telescope rather than just a single argument. In general, the annotations described in [Implicit Arguments](expressions.md#implicit_arguments) can be used to mark arguments as implicit.
.. _basic_declarations:
Basic Declarations
==================
Lean provides ways of adding new objects to the environment. The following provide straightforward ways of declaring new objects:
* ``axiom c : α`` : declare a constant named ``c`` of type ``α``, it is postulating that `α` is not an empty type.
* ``def c : α := v`` : defines ``c`` to denote ``v``, which should have type ``α``.
* ``theorem c : p := v`` : similar to ``def``, but intended to be used when ``p`` is a proposition.
* ``opaque c : α (:= v)?`` : declares a opaque constant named ``c`` of type ``α``, the optional value `v` is must have type `α`
and can be viewed as a certificate that ``α`` is not an empty type. If the value is not provided, Lean tries to find one
using a procedure based on type class resolution. The value `v` is hidden from the type checker. You can assume that
Lean "forgets" `v` after type checking this kind of declaration.
It is sometimes useful to be able to simulate a definition or theorem without naming it or adding it to the environment.
* ``example : α := t`` : elaborates ``t`` and checks that it has sort ``α`` (often a proposition), without adding it to the environment.
In ``def``, the type (``α`` or ``p``, respectively) can be omitted when it can be inferred by Lean. Constants declared with ``theorem`` are marked as ``irreducible``.
Any of ``def``, ``theorem``, ``axiom``, or ``example`` can take a list of arguments (that is, a context) before the colon. If ``(a : α)`` is a context, the definition ``def foo (a : α) : β := t``
is interpreted as ``def foo : (a : α) → β := fun a : α => t``. Similarly, a theorem ``theorem foo (a : α) : p := t`` is interpreted as ``theorem foo : ∀ a : α, p := fun a : α => t``.
```lean
opaque c : Nat
opaque d : Nat
axiom cd_eq : c = d
def foo : Nat := 5
def bar := 6
def baz (x y : Nat) (s : List Nat) := [x, y] ++ s
theorem foo_eq_five : foo = 5 := rfl
theorem baz_theorem (x y : Nat) : baz x y [] = [x, y] := rfl
example (x y : Nat) : baz x y [] = [x, y] := rfl
```
Inductive Types
===============
Lean's axiomatic foundation allows users to declare arbitrary
inductive families, following the pattern described by [Dybjer]_. To
make the presentation more manageable, we first describe inductive
*types*, and then describe the generalization to inductive *families*
in the next section. The declaration of an inductive type has the
following form:
```
inductive Foo (a : α) where
| constructor₁ : (b : β₁) → Foo a
| constructor₂ : (b : β₂) → Foo a
...
| constructorₙ : (b : βₙ) → Foo a
```
Here ``(a : α)`` is a context and each ``(b : βᵢ)`` is a telescope in the context ``(a : α)`` together with ``Foo``, subject to the following constraints.
Suppose the telescope ``(b : βᵢ)`` is ``(b₁ : βᵢ₁) ... (bᵤ : βᵢᵤ)``. Each argument in the telescope is either *nonrecursive* or *recursive*.
- An argument ``(bⱼ : βᵢⱼ)`` is *nonrecursive* if ``βᵢⱼ`` does not refer to ``foo,`` the inductive type being defined. In that case, ``βᵢⱼ`` can be any type, so long as it does not refer to any nonrecursive arguments.
- An argument ``(bⱼ : βᵢⱼ)`` is *recursive* if it ``βᵢⱼ`` of the form ``Π (d : δ), foo`` where ``(d : δ)`` is a telescope which does not refer to ``foo`` or any nonrecursive arguments.
The inductive type ``foo`` represents a type that is freely generated by the constructors. Each constructor can take arbitrary data and facts as arguments (the nonrecursive arguments), as well as indexed sequences of elements of ``foo`` that have been previously constructed (the recursive arguments). In set theoretic models, such sets can be represented by well-founded trees labeled by the constructor data, or they can defined using other transfinite or impredicative means.
The declaration of the type ``foo`` as above results in the addition of the following constants to the environment:
- the *type former* ``foo : Π (a : α), Sort u``
- for each ``i``, the *constructor* ``foo.constructorᵢ : Π (a : α) (b : βᵢ), foo a``
- the *eliminator* ``foo.rec``, which takes arguments
+ ``(a : α)`` (the parameters)
+ ``{C : foo a → Type u}`` (the *motive* of the elimination)
+ for each ``i``, the *minor premise* corresponding to ``constructorᵢ``
+ ``(x : foo)`` (the *major premise*)
and returns an element of ``C x``. Here, The ith minor premise is a function which takes
+ ``(b : βᵢ)`` (the arguments to the constructor)
+ an argument of type ``Π (d : δ), C (bⱼ d)`` corresponding to each recursive argument ``(bⱼ : βᵢⱼ)``, where ``βᵢⱼ`` is of the form ``Π (d : δ), foo`` (the recursive values of the function being defined)
and returns an element of ``C (constructorᵢ a b)``, the intended value of the function at ``constructorᵢ a b``.
The eliminator represents a principle of recursion: to construct an element of ``C x`` where ``x : foo a``, it suffices to consider each of the cases where ``x`` is of the form ``constructorᵢ a b`` and to provide an auxiliary construction in each case. In the case where some of the arguments to ``constructorᵢ`` are recursive, we can assume that we have already constructed values of ``C y`` for each value ``y`` constructed at an earlier stage.
Under the propositions-as-type correspondence, when ``C x`` is an element of ``Prop``, the eliminator represents a principle of induction. In order to show ``∀ x, C x``, it suffices to show that ``C`` holds for each constructor, under the inductive hypothesis that it holds for all recursive inputs to the constructor.
The eliminator and constructors satisfy the following identities, in which all the arguments are shown explicitly. Suppose we set ``F := foo.rec a C f₁ ... fₙ``. Then for each constructor, we have the definitional reduction:
```
F (constructorᵢ a b) = fᵢ b ... (fun d : δᵢⱼ => F (bⱼ d)) ...
```
where the ellipses include one entry for each recursive argument.
Below are some common examples of inductive types, many of which are defined in the core library.
```lean
namespace Hide
universe u v
-- BEGIN
inductive Empty : Type
inductive Unit : Type
| unit : Unit
inductive Bool : Type
| false : Bool
| true : Bool
inductive Prod (α : Type u) (β : Type v) : Type (max u v)
| mk : α → β → Prod α β
inductive Sum (α : Type u) (β : Type v)
| inl : α → Sum α β
| inr : β → Sum α β
inductive Sigma (α : Type u) (β : α → Type v)
| mk : (a : α) → β a → Sigma α β
inductive false : Prop
inductive True : Prop
| trivial : True
inductive And (p q : Prop) : Prop
| intro : p → q → And p q
inductive Or (p q : Prop) : Prop
| inl : p → Or p q
| inr : q → Or p q
inductive Exists (α : Type u) (p : α → Prop) : Prop
| intro : ∀ x : α, p x → Exists α p
inductive Subtype (α : Type u) (p : α → Prop) : Type u
| intro : ∀ x : α, p x → Subtype α p
inductive Nat : Type
| zero : Nat
| succ : Nat → Nat
inductive List (α : Type u)
| nil : List α
| cons : α → List α → List α
-- full binary tree with nodes and leaves labeled from α
inductive BinTree (α : Type u)
| leaf : α → BinTree α
| node : BinTree αα → BinTree α → BinTree α
-- every internal node has subtrees indexed by Nat
inductive CBT (α : Type u)
| leaf : α → CBT α
| node : (Nat → CBT α) → CBT α
-- END
end Hide
```
Note that in the syntax of the inductive definition ``Foo``, the context ``(a : α)`` is left implicit. In other words, constructors and recursive arguments are written as though they have return type ``Foo`` rather than ``Foo a``.
Elements of the context ``(a : α)`` can be marked implicit as described in [Implicit Arguments](#implicit.md#implicit_arguments). These annotations bear only on the type former, ``Foo``. Lean uses a heuristic to determine which arguments to the constructors should be marked implicit, namely, an argument is marked implicit if it can be inferred from the type of a subsequent argument. If the annotation ``{}`` appears after the constructor, a argument is marked implicit if it can be inferred from the type of a subsequent argument *or the return type*. For example, it is useful to let ``nil`` denote the empty list of any type, since the type can usually be inferred in the context in which it appears. These heuristics are imperfect, and you may sometimes wish to define your own constructors in terms of the default ones. In that case, use the ``[match_pattern]`` [attribute](TODO: missing link) to ensure that these will be used appropriately by the [Equation Compiler](#the-equation-compiler).
There are restrictions on the universe ``u`` in the return type ``Sort u`` of the type former. There are also restrictions on the universe ``u`` in the return type ``Sort u`` of the motive of the eliminator. These will be discussed in the next section in the more general setting of inductive families.
Lean allows some additional syntactic conveniences. You can omit the return type of the type former, ``Sort u``, in which case Lean will infer the minimal possible nonzero value for ``u``. As with function definitions, you can list arguments to the constructors before the colon. In an enumerated type (that is, one where the constructors have no arguments), you can also leave out the return type of the constructors.
```lean
namespace Hide
universe u
-- BEGIN
inductive Weekday
| sunday | monday | tuesday | wednesday
| thursday | friday | saturday
inductive Nat
| zero
| succ (n : Nat) : Nat
inductive List (α : Type u)
| nil : List α
| cons (a : α) (l : List α) : List α
@[match_pattern]
def List.nil' (α : Type u) : List α := List.nil
def length {α : Type u} : List α → Nat
| (List.nil' _) => 0
| (List.cons a l) => 1 + length l
-- END
end Hide
```
The type former, constructors, and eliminator are all part of Lean's axiomatic foundation, which is to say, they are part of the trusted kernel. In addition to these axiomatically declared constants, Lean automatically defines some additional objects in terms of these, and adds them to the environment. These include the following:
- ``Foo.recOn`` : a variant of the eliminator, in which the major premise comes first
- ``Foo.casesOn`` : a restricted version of the eliminator which omits any recursive calls
- ``Foo.noConfusionType``, ``Foo.noConfusion`` : functions which witness the fact that the inductive type is freely generated, i.e. that the constructors are injective and that distinct constructors produce distinct objects
- ``Foo.below``, ``Foo.ibelow`` : functions used by the equation compiler to implement structural recursion
- ``instance : SizeOf Foo`` : a measure which can be used for well-founded recursion
Note that it is common to put definitions and theorems related to a datatype ``foo`` in a namespace of the same name. This makes it possible to use projection notation described in [Structures](struct.md#structures) and [Namespaces](namespaces.md#namespaces).
```lean
namespace Hide
universe u
-- BEGIN
inductive Nat
| zero
| succ (n : Nat) : Nat
#check Nat
#check @Nat.rec
#check Nat.zero
#check Nat.succ
#check @Nat.recOn
#check @Nat.casesOn
#check @Nat.noConfusionType
#check @Nat.noConfusion
#check @Nat.brecOn
#check Nat.below
#check Nat.ibelow
#check Nat._sizeOf_1
-- END
end Hide
```
.. _inductive_families:
Inductive Families
==================
In fact, Lean implements a slight generalization of the inductive types described in the previous section, namely, inductive *families*. The declaration of an inductive family in Lean has the following form:
```
inductive Foo (a : α) : Π (c : γ), Sort u
| constructor₁ : Π (b : β₁), Foo t₁
| constructor₂ : Π (b : β₂), Foo t₂
...
| constructorₙ : Π (b : βₙ), Foo tₙ
```
Here ``(a : α)`` is a context, ``(c : γ)`` is a telescope in context ``(a : α)``, each ``(b : βᵢ)`` is a telescope in the context ``(a : α)`` together with ``(Foo : Π (c : γ), Sort u)`` subject to the constraints below, and each ``tᵢ`` is a tuple of terms in the context ``(a : α) (b : βᵢ)`` having the types ``γ``. Instead of defining a single inductive type ``Foo a``, we are now defining a family of types ``Foo a c`` indexed by elements ``c : γ``. Each constructor, ``constructorᵢ``, places its result in the type ``Foo a tᵢ``, the member of the family with index ``tᵢ``.
The modifications to the scheme in the previous section are straightforward. Suppose the telescope ``(b : βᵢ)`` is ``(b₁ : βᵢ₁) ... (bᵤ : βᵢᵤ)``.
- As before, an argument ``(bⱼ : βᵢⱼ)`` is *nonrecursive* if ``βᵢⱼ`` does not refer to ``Foo,`` the inductive type being defined. In that case, ``βᵢⱼ`` can be any type, so long as it does not refer to any nonrecursive arguments.
- An argument ``(bⱼ : βᵢⱼ)`` is *recursive* if ``βᵢⱼ`` is of the form ``Π (d : δ), Foo s`` where ``(d : δ)`` is a telescope which does not refer to ``Foo`` or any nonrecursive arguments and ``s`` is a tuple of terms in context ``(a : α)`` and the previous nonrecursive ``bⱼ``'s with types ``γ``.
The declaration of the type ``Foo`` as above results in the addition of the following constants to the environment:
- the *type former* ``Foo : Π (a : α) (c : γ), Sort u``
- for each ``i``, the *constructor* ``Foo.constructorᵢ : Π (a : α) (b : βᵢ), Foo a tᵢ``
- the *eliminator* ``Foo.rec``, which takes arguments
+ ``(a : α)`` (the parameters)
+ ``{C : Π (c : γ), Foo a c → Type u}`` (the motive of the elimination)
+ for each ``i``, the minor premise corresponding to ``constructorᵢ``
+ ``(x : Foo a)`` (the major premise)
and returns an element of ``C x``. Here, The ith minor premise is a function which takes
+ ``(b : βᵢ)`` (the arguments to the constructor)
+ an argument of type ``Π (d : δ), C s (bⱼ d)`` corresponding to each recursive argument ``(bⱼ : βᵢⱼ)``, where ``βᵢⱼ`` is of the form ``Π (d : δ), Foo s``
and returns an element of ``C tᵢ (constructorᵢ a b)``.
Suppose we set ``F := Foo.rec a C f₁ ... fₙ``. Then for each constructor, we have the definitional reduction, as before:
```
F (constructorᵢ a b) = fᵢ b ... (fun d : δᵢⱼ => F (bⱼ d)) ...
```
where the ellipses include one entry for each recursive argument.
The following are examples of inductive families.
```lean
namespace Hide
universe u
-- BEGIN
inductive Vector (α : Type u) : Nat → Type u
| nil : Vector 0
| succ : Π n, Vector n → Vector (n + 1)
-- 'IsProd s n' means n is a product of elements of s
inductive IsProd (s : Set Nat) : Nat → Prop
| base : ∀ n ∈ s, IsProd n
| step : ∀ m n, IsProd m → IsProd n → IsProd (m * n)
inductive Eq {α : Sort u} (a : α) : α → Prop
| refl : Eq a
-- END
end Hide
```
We can now describe the constraints on the return type of the type former, ``Sort u``. We can always take ``u`` to be ``0``, in which case we are defining an inductive family of propositions. If ``u`` is nonzero, however, it must satisfy the following constraint: for each type ``βᵢⱼ : Sort v`` occurring in the constructors, we must have ``u ≥ v``. In the set-theoretic interpretation, this ensures that the universe in which the resulting type resides is large enough to contain the inductively generated family, given the number of distinctly-labeled constructors. The restriction does not hold for inductively defined propositions, since these contain no data.
Putting an inductive family in ``Prop``, however, does impose a restriction on the eliminator. Generally speaking, for an inductive family in ``Prop``, the motive in the eliminator is required to be in ``Prop``. But there is an exception to this rule: you are allowed to eliminate from an inductively defined ``Prop`` to an arbitrary ``Sort`` when there is only one constructor, and each argument to that constructor is either in ``Prop`` or an index. The intuition is that in this case the elimination does not make use of any information that is not already given by the mere fact that the type of argument is inhabited. This special case is known as *singleton elimination*.
.. _mutual_and_nested_inductive_definitions:
Mutual and Nested Inductive Definitions
=======================================
Lean supports two generalizations of the inductive families described above, namely, *mutual* and *nested* inductive definitions. These are *not* implemented natively in the kernel. Rather, the definitions are compiled down to the primitive inductive types and families.
The first generalization allows for multiple inductive types to be defined simultaneously.
```
mutual
inductive Foo (a : α) : Π (c : γ₁), Sort u
| constructor₁₁ : Π (b : β₁₁), Foo a t₁₁
| constructor₁₂ : Π (b : β₁₂), Foo a t₁₂
...
| constructor₁ₙ : Π (b : β₁ₙ), Foo a t₁ₙ
inductive Bar (a : α) : Π (c : γ₂), Sort u
| constructor₂₁ : Π (b : β₂₁), Bar a t₂₁
| constructor₂₂ : Π (b : β₂₂), Bar a t₂₂
...
| constructor₂ₘ : Π (b : β₂ₘ), Bar a t₂ₘ
end
```
Here the syntax is shown for defining two inductive families, ``Foo`` and ``Bar``, but any number is allowed. The restrictions are almost the same as for ordinary inductive families. For example, each ``(b : βᵢⱼ)`` is a telescope relative to the context ``(a : α)``. The difference is that the constructors can now have recursive arguments whose return types are any of the inductive families currently being defined, in this case ``Foo`` and ``Bar``. Note that all of the inductive definitions share the same parameters ``(a : α)``, though they may have different indices.
A mutual inductive definition is compiled down to an ordinary inductive definition using an extra finite-valued index to distinguish the components. The details of the internal construction are meant to be hidden from most users. Lean defines the expected type formers ``Foo`` and ``Bar`` and constructors ``constructorᵢⱼ`` from the internal inductive definition. There is no straightforward elimination principle, however. Instead, Lean defines an appropriate ``sizeOf`` measure, meant for use with well-founded recursion, with the property that the recursive arguments to a constructor are smaller than the constructed value.
The second generalization relaxes the restriction that in the recursive definition of ``Foo``, ``Foo`` can only occur strictly positively in the type of any of its recursive arguments. Specifically, in a nested inductive definition, ``Foo`` can appear as an argument to another inductive type constructor, so long as the corresponding parameter occurs strictly positively in the constructors for *that* inductive type. This process can be iterated, so that additional type constructors can be applied to those, and so on.
A nested inductive definition is compiled down to an ordinary inductive definition using a mutual inductive definition to define copies of all the nested types simultaneously. Lean then constructs isomorphisms between the mutually defined nested types and their independently defined counterparts. Once again, the internal details are not meant to be manipulated by users. Rather, the type former and constructors are made available and work as expected, while an appropriate ``sizeOf`` measure is generated for use with well-founded recursion.
```lean
universe u
-- BEGIN
mutual
inductive Even : Nat → Prop
| even_zero : Even 0
| even_succ : ∀ n, Odd n → Even (n + 1)
inductive Odd : Nat → Prop
| odd_succ : ∀ n, Even n → Odd (n + 1)
end
inductive Tree (α : Type u)
| mk : α → List (Tree α) → Tree α
inductive DoubleTree (α : Type u)
| mk : α → List (DoubleTree α) × List (DoubleTree α) → DoubleTree α
-- END
```
.. _the_equation_compiler:
The Equation Compiler
=====================
The equation compiler takes an equational description of a function or proof and tries to define an object meeting that specification. It expects input with the following syntax:
```
def foo (a : α) : Π (b : β), γ
| [patterns₁] => t₁
...
| [patternsₙ] => tₙ
```
Here ``(a : α)`` is a telescope, ``(b : β)`` is a telescope in the context ``(a : α)``, and ``γ`` is an expression in the context ``(a : α) (b : β)`` denoting a ``Type`` or a ``Prop``.
Each ``patternsᵢ`` is a sequence of patterns of the same length as ``(b : β)``. A pattern is either:
- a variable, denoting an arbitrary value of the relevant type,
- an underscore, denoting a *wildcard* or *anonymous variable*,
- an inaccessible term (see below), or
- a constructor for the inductive type of the corresponding argument, applied to a sequence of patterns.
In the last case, the pattern must be enclosed in parentheses.
Each term ``tᵢ`` is an expression in the context ``(a : α)`` together with the variables introduced on the left-hand side of the token ``=>``. The term ``tᵢ`` can also include recursive calls to ``foo``, as described below. The equation compiler does case splitting on the variables ``(b : β)`` as necessary to match the patterns, and defines ``foo`` so that it has the value ``tᵢ`` in each of the cases. In ideal circumstances (see below), the equations hold definitionally. Whether they hold definitionally or only propositionally, the equation compiler proves the relevant equations and assigns them internal names. They are accessible by the ``rewrite`` and ``simp`` tactics under the name ``foo`` (see [Rewrite](tactics.md#rewrite) and _[TODO: where is simplifier tactic documented?]_. If some of the patterns overlap, the equation compiler interprets the definition so that the first matching pattern applies in each case. Thus, if the last pattern is a variable, it covers all the remaining cases. If the patterns that are presented do not cover all possible cases, the equation compiler raises an error.
When identifiers are marked with the ``[match_pattern]`` attribute, the equation compiler unfolds them in the hopes of exposing a constructor. For example, this makes it possible to write ``n+1`` and ``0`` instead of ``Nat.succ n`` and ``Nat.zero`` in patterns.
For a nonrecursive definition involving case splits, the defining equations will hold definitionally. With inductive types like ``Char``, ``String``, and ``Fin n``, a case split would produce definitions with an inordinate number of cases. To avoid this, the equation compiler uses ``if ... then ... else`` instead of ``casesOn`` when defining the function. In this case, the defining equations hold definitionally as well.
```lean
open Nat
def sub2 : Nat → Nat
| zero => 0
| succ zero => 0
| succ (succ a) => a
def bar : Nat → List Nat → Bool → Nat
| 0, _, false => 0
| 0, b :: _, _ => b
| 0, [], true => 7
| a+1, [], false => a
| a+1, [], true => a + 1
| a+1, b :: _, _ => a + b
def baz : Char → Nat
| 'A' => 1
| 'B' => 2
| _ => 3
```
The case where patterns are matched against an argument whose type is an inductive family is known as *dependent pattern matching*. This is more complicated, because the type of the function being defined can impose constraints on the patterns that are matched. In this case, the equation compiler will detect inconsistent cases and rule them out.
```lean
universe u
inductive Vector (α : Type u) : Nat → Type u
| nil : Vector α 0
| cons : α → Vector α n → Vector α (n+1)
namespace Vector
def head : Vector α (n+1) → α
| cons h t => h
def tail : Vector α (n+1) → Vector α n
| cons h t => t
def map (f : α → β → γ) : Vector α n → Vector β n → Vector γ n
| nil, nil => nil
| cons a va, cons b vb => cons (f a b) (map f va vb)
end Vector
```
.. _recursive_functions:
Recursive functions
===================
Lean must ensure that a recursive function terminates, for which there are two strategies: _structural recursion_, in which all recursive calls are made on smaller parts of the input data, and _well-founded recursion_, in which recursive calls are justified by showing that arguments to recursive calls are smaller according to some other measure.
Structural recursion
--------------------
If the definition of a function contains recursive calls, Lean first tries to interpret the definition as a structural recursion. In order for that to succeed, the recursive arguments must be subterms of the corresponding arguments on the left-hand side.
The function is then defined using a *course of values* recursion, using automatically generated functions ``below`` and ``brec`` in the namespace corresponding to the inductive type of the recursive argument. In this case the defining equations hold definitionally, possibly with additional case splits.
```lean
namespace Hide
-- BEGIN
def fib : Nat → Nat
| 0 => 1
| 1 => 1
| (n+2) => fib (n+1) + fib n
def append {α : Type} : List α → List α → List α
| [], l => l
| h::t, l => h :: append t l
example : append [(1 : Nat), 2, 3] [4, 5] = [1, 2, 3, 4, 5] => rfl
-- END
end Hide
```
Well-founded recursion
---------------------
If structural recursion fails, the equation compiler falls back on well-founded recursion. It tries to infer an instance of ``SizeOf`` for the type of each argument, and then tries to find a permutation of the arguments such that each recursive call is decreasing under the lexicographic order with respect to ``sizeOf`` measures. Lean uses information in the local context, so you can often provide the relevant proof manually using ``have`` in the body of the definition.
In the case of well-founded recursion, the equation used to declare the function holds only propositionally, but not definitionally, and can be accessed using ``unfold``, ``simp`` and ``rewrite`` with the function name (for example ``unfold foo`` or ``simp [foo]``, where ``foo`` is the function defined with well-founded recursion).
```lean
namespace Hide
open Nat
-- BEGIN
def div : Nat → Nat → Nat
| x, y =>
if h : 0 < y ∧ y ≤ x then
have : x - y < x :=
sub_lt (Nat.lt_of_lt_of_le h.left h.right) h.left
div (x - y) y + 1
else
0
example (x y : Nat) :
div x y = if 0 < y ∧ y ≤ x then div (x - y) y + 1 else 0 :=
by rw [div]; rfl
-- END
end Hide
```
If Lean cannot find a permutation of the arguments for which all recursive calls are decreasing, it will print a table that contains, for every recursive call, which arguments Lean could prove to be decreasing. For example, a function with three recursive calls and four parameters might cause the following message to be printed
```
example.lean:37:0-43:31: error: Could not find a decreasing measure.
The arguments relate at each recursive call as follows:
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
x1 x2 x3 x4
1) 39:6-27 = = _ =
2) 40:6-25 = ? _ <
3) 41:6-25 < _ _ _
Please use `termination_by` to specify a decreasing measure.
```
This table should be read as follows:
* In the first recursive call, in line 39, arguments 1, 2 and 4 are equal to the function's parameters.
* The second recursive call, in line 40, has an equal first argument, a smaller fourth argument, and nothing could be inferred for the second argument.
* The third recursive call, in line 41, has a decreasing first argument.
* No other proofs were attempted, either because the parameter has a type without a non-trivial ``WellFounded`` instance (parameter 3), or because it is already clear that no decreasing measure can be found.
Lean will print the termination measure it found if ``set_option showInferredTerminationBy true`` is set.
If Lean does not find the termination measure, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
```
termination_by e
```
where ``e`` is an expression that depends on the parameters of the function and should be decreasing at each recursive call. The type of `e` should be an instance of the class ``WellFoundedRelation``, which determines how to compare two values of that type.
If ``f`` has parameters “after the ``:``” (for example when defining functions via patterns using `|`), then these can be brought into scope using the syntax
```
termination_by a₁ … aₙ => e
```
By default, Lean uses the tactic ``decreasing_tactic`` when proving that an argument is decreasing; see its documentation for how to globally extend it. You can also choose to use a different tactic for a given function definition with the clause
```
decreasing_by <tac>
```
which should come after ``termination_by`, if present.
Note that recursive definitions can in general require nested recursions, that is, recursion on different arguments of ``foo`` in the template above. The equation compiler handles this by abstracting later arguments, and recursively defining higher-order functions to meet the specification.
Mutual recursion
----------------
The equation compiler also allows mutual recursive definitions, with a syntax similar to that of [Mutual and Nested Inductive Definitions](#mutual-and-nested-inductive-definitions). Mutual definitions are always compiled using well-founded recursion, and so once again the defining equations hold only propositionally.
```lean
mutual
def even : Nat → Bool
| 0 => true
| a+1 => odd a
def odd : Nat → Bool
| 0 => false
| a+1 => even a
end
example (a : Nat) : even (a + 1) = odd a :=
by simp [even]
example (a : Nat) : odd (a + 1) = even a :=
by simp [odd]
```
Well-founded recursion is especially useful with [Mutual and Nested Inductive Definitions](#mutual-and-nested-inductive-definitions), since it provides the canonical way of defining functions on these types.
```lean
mutual
inductive Even : Nat → Prop
| even_zero : Even 0
| even_succ : ∀ n, Odd n → Even (n + 1)
inductive Odd : Nat → Prop
| odd_succ : ∀ n, Even n → Odd (n + 1)
end
open Even Odd
theorem not_odd_zero : ¬ Odd 0 := fun x => nomatch x
mutual
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
| _, odd_succ n h => h
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
| _, even_succ n h => h
end
inductive Term
| const : String → Term
| app : String → List Term → Term
open Term
mutual
def num_consts : Term → Nat
| .const n => 1
| .app n ts => num_consts_lst ts
def num_consts_lst : List Term → Nat
| [] => 0
| t::ts => num_consts t + num_consts_lst ts
end
```
In a set of mutually recursive function, either all or no functions must have an explicit termination measure (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
```
mutual
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
| _, odd_succ n h => h
termination_by n h => h
decreasing_by decreasing_tactic
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
| _, even_succ n h => h
termination_by n h => h
end
```
Another way to express mutual recursion is using local function definitions in ``where`` or ``let rec`` clauses: these can be mutually recursive with each other and their containing function:
```
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
| _, odd_succ n h => h
termination_by n h => h
where
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
| _, even_succ n h => h
termination_by n h => h
```
.. _match_expressions:
Match Expressions
=================
Lean supports a ``match ... with ...`` construct similar to ones found in most functional programming languages. The syntax is as follows:
```
match t₁, ..., tₙ with
| p₁₁, ..., p₁ₙ => s₁
...
| pₘ₁, ..., pₘₙ => sₘ
```
Here ``t₁, ..., tₙ`` are any terms in the context in which the expression appears, the expressions ``pᵢⱼ`` are patterns, and the terms ``sᵢ`` are expressions in the local context together with variables introduced by the patterns on the left-hand side. Each ``sᵢ`` should have the expected type of the entire ``match`` expression.
Any ``match`` expression is interpreted using the equation compiler, which generalizes ``t₁, ..., tₙ``, defines an internal function meeting the specification, and then applies it to ``t₁, ..., tₙ``. In contrast to the definitions in [The Equation Compiler](declarations.md#the-equation-compiler), the terms ``tᵢ`` are arbitrary terms rather than just variables, and the expression can occur anywhere within a Lean expression, not just at the top level of a definition. Note that the syntax here is somewhat different: both the terms ``tᵢ`` and the patterns ``pᵢⱼ`` are separated by commas.
```lean
def foo (n : Nat) (b c : Bool) :=
5 + match n - 5, b && c with
| 0, true => 0
| m+1, true => m + 7
| 0, false => 5
| m+1, false => m + 3
```
When a ``match`` has only one line, Lean provides alternative syntax with a destructuring ``let``, as well as a destructuring lambda abstraction. Thus the following definitions all have the same net effect.
```lean
def bar₁ : Nat × Nat → Nat
| (m, n) => m + n
def bar₂ (p : Nat × Nat) : Nat :=
match p with | (m, n) => m + n
def bar₃ : Nat × Nat → Nat :=
fun ⟨m, n⟩ => m + n
def bar₄ (p : Nat × Nat) : Nat :=
let ⟨m, n⟩ := p; m + n
```
Information about the term being matched can be preserved in each branch using the syntax `match h : t with`. For example, a user may want to match a term `ns ++ ms : List Nat`, while tracking the hypothesis `ns ++ ms = []` or `ns ++ ms= h :: t` in the respective match arm:
```lean
def foo (ns ms : List Nat) (h1 : ns ++ ms ≠ []) (k : Nat -> Char) : Char :=
match h2 : ns ++ ms with
-- in this arm, we have the hypothesis `h2 : ns ++ ms = []`
| [] => absurd h2 h1
-- in this arm, we have the hypothesis `h2 : ns ++ ms = h :: t`
| h :: t => k h
-- '7'
#eval foo [7, 8, 9] [] (by decide) Nat.digitChar
```
.. _structures_and_records:
Structures and Records
======================
The ``structure`` command in Lean is used to define an inductive data type with a single constructor and to define its projections at the same time. The syntax is as follows:
```
structure Foo (a : α) : Sort u extends Bar, Baz :=
constructor :: (field₁ : β₁) ... (fieldₙ : βₙ)
```
Here ``(a : α)`` is a telescope, that is, the parameters to the inductive definition. The name ``constructor`` followed by the double colon is optional; if it is not present, the name ``mk`` is used by default. The keyword ``extends`` followed by a list of previously defined structures is also optional; if it is present, an instance of each of these structures is included among the fields to ``Foo``, and the types ``βᵢ`` can refer to their fields as well. The output type, ``Sort u``, can be omitted, in which case Lean infers to smallest non-``Prop`` sort possible (unless all the fields are ``Prop``, in which case it infers ``Prop``).
Finally, ``(field₁ : β₁) ... (fieldₙ : βₙ)`` is a telescope relative to ``(a : α)`` and the fields in ``bar`` and ``baz``.
The declaration above is syntactic sugar for an inductive type declaration, and so results in the addition of the following constants to the environment:
- the type former : ``Foo : Π (a : α), Sort u``
- the single constructor :
```
Foo.constructor : Π (a : α) (toBar : Bar) (toBaz : Baz)
(field₁ : β₁) ... (fieldₙ : βₙ), Foo a
```
- the eliminator ``Foo.rec`` for the inductive type with that constructor
In addition, Lean defines
- the projections : ``fieldᵢ : Π (a : α) (c : Foo) : βᵢ`` for each ``i``
where any other fields mentioned in ``βᵢ`` are replaced by the relevant projections from ``c``.
Given ``c : Foo``, Lean offers the following convenient syntax for the projection ``Foo.fieldᵢ c``:
- *anonymous projections* : ``c.fieldᵢ``
- *numbered projections* : ``c.i``
These can be used in any situation where Lean can infer that the type of ``c`` is of the form ``Foo a``. The convention for anonymous projections is extended to any function ``f`` defined in the namespace ``Foo``, as described in [Namespaces](namespaces.md).
Similarly, Lean offers the following convenient syntax for constructing elements of ``Foo``. They are equivalent to ``Foo.constructor b₁ b₂ f₁ f₁ ... fₙ``, where ``b₁ : Bar``, ``b₂ : Baz``, and each ``fᵢ : βᵢ`` :
- *anonymous constructor*: ``⟨ b₁, b₂, f₁, ..., fₙ ⟩``
- *record notation*:
```
{ toBar := b₁, toBaz := b₂, field₁ := f₁, ...,
fieldₙ := fₙ : Foo a }
```
The anonymous constructor can be used in any context where Lean can infer that the expression should have a type of the form ``Foo a``. The unicode brackets are entered as ``\<`` and ``\>`` respectively.
When using record notation, you can omit the annotation ``: Foo a`` when Lean can infer that the expression should have a type of the form ``Foo a``. You can replace either ``toBar`` or ``toBaz`` by assignments to *their* fields as well, essentially acting as though the fields of ``Bar`` and ``Baz`` are simply imported into ``Foo``. Finally, record notation also supports
- *record updates*: ``{ t with ... fieldᵢ := fᵢ ...}``
Here ``t`` is a term of type ``Foo a`` for some ``a``. The notation instructs Lean to take values from ``t`` for any field assignment that is omitted from the list.
Lean also allows you to specify a default value for any field in a structure by writing ``(fieldᵢ : βᵢ := t)``. Here ``t`` specifies the value to use when the field ``fieldᵢ`` is left unspecified in an instance of record notation.
```lean
universe u v
structure Vec (α : Type u) (n : Nat) :=
(l : List α) (h : l.length = n)
structure Foo (α : Type u) (β : Nat → Type v) : Type (max u v) :=
(a : α) (n : Nat) (b : β n)
structure Bar :=
(c : Nat := 8) (d : Nat)
structure Baz extends Foo Nat (Vec Nat), Bar :=
(v : Vec Nat n)
#check Foo
#check @Foo.mk
#check @Foo.rec
#check Foo.a
#check Foo.n
#check Foo.b
#check Baz
#check @Baz.mk
#check @Baz.rec
#check Baz.toFoo
#check Baz.toBar
#check Baz.v
def bzz := Vec.mk [1, 2, 3] rfl
#check Vec.l bzz
#check Vec.h bzz
#check bzz.l
#check bzz.h
#check bzz.1
#check bzz.2
example : Vec Nat 3 := Vec.mk [1, 2, 3] rfl
example : Vec Nat 3 := ⟨[1, 2, 3], rfl⟩
example : Vec Nat 3 := { l := [1, 2, 3], h := rfl : Vec Nat 3 }
example : Vec Nat 3 := { l := [1, 2, 3], h := rfl }
example : Foo Nat (Vec Nat) := ⟨1, 3, bzz⟩
example : Baz := ⟨⟨1, 3, bzz⟩, ⟨5, 7⟩, bzz⟩
example : Baz := { a := 1, n := 3, b := bzz, c := 5, d := 7, v := bzz}
def fzz : Foo Nat (Vec Nat) := {a := 1, n := 3, b := bzz}
example : Foo Nat (Vec Nat) := { fzz with a := 7 }
example : Baz := { fzz with c := 5, d := 7, v := bzz }
example : Bar := { c := 8, d := 9 }
example : Bar := { d := 9 } -- uses the default value for c
```
.. _type_classes:
Type Classes
============
(Classes and instances. Anonymous instances. Local instances.)
.. [Dybjer] Dybjer, Peter, *Inductive Families*. Formal Aspects of Computing 6, 1994, pages 440-465.

View File

@@ -1 +0,0 @@
# Definitions

View File

@@ -1,66 +0,0 @@
## What makes dependent type theory dependent?
The short explanation is that what makes dependent type theory dependent is that types can depend on parameters.
You have already seen a nice example of this: the type ``List α`` depends on the argument ``α``, and
this dependence is what distinguishes ``List Nat`` and ``List Bool``.
For another example, consider the type ``Vector α n``, the type of vectors of elements of ``α`` of length ``n``.
This type depends on *two* parameters: the type ``α : Type`` of the elements in the vector and the length ``n : Nat``.
Suppose we wish to write a function ``cons`` which inserts a new element at the head of a list.
What type should ``cons`` have? Such a function is *polymorphic*: we expect the ``cons`` function for ``Nat``, ``Bool``,
or an arbitrary type ``α`` to behave the same way.
So it makes sense to take the type to be the first argument to ``cons``, so that for any type, ``α``, ``cons α``
is the insertion function for lists of type ``α``. In other words, for every ``α``, ``cons α`` is the function that takes an element ``a : α``
and a list ``as : List α``, and returns a new list, so we have ``cons α a as : list α``.
It is clear that ``cons α`` should have type ``α → List α → List α``. But what type should ``cons`` have?
A first guess might be ``Type → α → list α → list α``, but, on reflection, this does not make sense:
the ``α`` in this expression does not refer to anything, whereas it should refer to the argument of type ``Type``.
In other words, *assuming* ``α : Type`` is the first argument to the function, the type of the next two elements are ``α`` and ``List α``.
These types vary depending on the first argument, ``α``.
This is an instance of a *dependent function type*, or *dependent arrow type*. Given ``α : Type`` and ``β : α → Type``,
think of ``β`` as a family of types over ``α``, that is, a type ``β a`` for each ``a : α``.
In that case, the type ``(a : α) → β a`` denotes the type of functions ``f`` with the property that,
for each ``a : α``, ``f a`` is an element of ``β a``. In other words, the type of the value returned by ``f`` depends on its input.
Notice that ``(a : α) → β`` makes sense for any expression ``β : Type``. When the value of ``β`` depends on ``a``
(as does, for example, the expression ``β a`` in the previous paragraph), ``(a : α) → β`` denotes a dependent function type.
When ``β`` doesn't depend on ``a``, ``(a : α) → β`` is no different from the type ``α → β``.
Indeed, in dependent type theory (and in Lean), ``α → β`` is just notation for ``(a : α) → β`` when ``β`` does not depend on ``a``.
Returning to the example of lists, we can use the command `#check` to inspect the type of the following `List` functions
We will explain the ``@`` symbol and the difference between the round and curly braces momentarily.
```lean
#check @List.cons -- {α : Type u_1} → α → List α → List α
#check @List.nil -- {α : Type u_1} → List α
#check @List.length -- {α : Type u_1} → List α → Nat
#check @List.append -- {α : Type u_1} → List α → List α → List α
```
Just as dependent function types ``(a : α) → β a`` generalize the notion of a function type ``α → β`` by allowing ``β`` to depend on ``α``,
dependent Cartesian product types ``(a : α) × β a`` generalize the Cartesian product ``α × β`` in the same way. Dependent products are also
called *sigma* types, and you can also write them as `Σ a : α, β a`. You can use `⟨a, b⟩` or `Sigma.mk a b` to create a dependent pair.
```lean
universe u v
def f (α : Type u) (β : α → Type v) (a : α) (b : β a) : (a : α) × β a :=
⟨a, b⟩
def g (α : Type u) (β : α → Type v) (a : α) (b : β a) : Σ a : α, β a :=
Sigma.mk a b
#reduce f
#reduce g
#reduce f Type (fun α => α) Nat 10
#reduce g Type (fun α => α) Nat 10
#reduce (f Type (fun α => α) Nat 10).1 -- Nat
#reduce (g Type (fun α => α) Nat 10).1 -- Nat
#reduce (f Type (fun α => α) Nat 10).2 -- 10
#reduce (g Type (fun α => α) Nat 10).2 -- 10
```
The function `f` and `g` above denote the same function.

View File

@@ -1,3 +0,0 @@
# Dependent Types
In this section, we introduce simple type theory, types as objects, definitions, and explain what makes dependent type theory *dependent*.

View File

@@ -1,109 +0,0 @@
# Documentation
The Lean `doc` folder contains the [Lean Manual](https://lean-lang.org/lean4/doc/) and is
authored in a combination of markdown (`*.md`) files and literate Lean files. The .lean files are
preprocessed using a tool called [LeanInk](https://github.com/leanprover/leanink) and
[Alectryon](https://github.com/Kha/alectryon) which produces a generated markdown file. We then run
`mdbook` on the result to generate the html pages.
## Settings
We are using the following settings while editing the markdown docs.
```json
{
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"[markdown]": {
"rewrap.wrappingColumn": 70
}
}
```
## Build
### Using Nix
Building the manual using Nix (which is what the CI does) is as easy as
```bash
$ nix build --update-input lean ./doc
```
You can also open a shell with `mdbook` for running the commands mentioned below with
`nix develop ./doc#book`. Otherwise, read on.
### Manually
To build and test the book you have to preprocess the .lean files with Alectryon then use our own
fork of the Rust tool named [mdbook](https://github.com/leanprover/mdbook). We have our own fork of
mdBook with the following additional features:
* Add support for hiding lines in other languages
[#1339](https://github.com/rust-lang/mdBook/pull/1339)
* Make `mdbook test` call the `lean` compiler to test the snippets.
* Ability to test a single chapter at a time which is handy when you
are working on that chapter. See the `--chapter` option.
So you need to setup these tools before you can run `mdBook`.
1. install [Rust](https://www.rust-lang.org/tools/install)
which provides you with the `cargo` tool for building rust packages.
Then run the following:
```bash
cargo install --git https://github.com/leanprover/mdBook mdbook
```
1. Clone https://github.com/leanprover/LeanInk.git and run `lake build` then make the resulting
binary available to Alectryon using e.g.
```bash
# make `leanInk` available in the current shell
export PATH=$PWD/build/bin:$PATH
```
1. Create a Python 3.10 environment.
1. Install Alectryon:
```
python3 -m pip install git+https://github.com/Kha/alectryon.git@typeid
```
1. Now you are ready to process the `*.lean` files using Alectryon as follows:
```
cd lean4/doc
alectryon --frontend lean4+markup examples/palindromes.lean --backend webpage -o palindromes.lean.md
```
Repeat this for the other .lean files you care about or write a script to process them all.
1. Now you can build the book using:
```
cd lean4/doc
mdbook build
```
This will put the HTML in a `out` folder so you can load `out/index.html` in your web browser and
it should look like https://lean-lang.org/lean4/doc/.
1. It is also handy to use e.g. [`mdbook watch`](https://rust-lang.github.io/mdBook/cli/watch.html)
in the `doc/` folder so that it keeps the html up to date while you are editing.
```bash
mdbook watch --open # opens the output in `out/` in your default browser
```
## Testing Lean Snippets
You can run the following in the `doc/` folder to test all the lean code snippets.
```bash
mdbook test
```
and you can use the `--chapter` option to test a specific chapter that you are working on:
```bash
mdbook test --chapter Array
```
Use chapter name `?` to get a list of all the chapter names.

View File

@@ -69,7 +69,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
- `repl`:
There are two copies of `lean-toolchain`/`lakefile.lean`:
in the root, and in `test/Mathlib/`. Edit both, and run `lake update` in both directories.
- An awkward situtation that sometimes occurs (e.g. with Verso) is that the `master`/`main` branch has already been moved
- An awkward situation that sometimes occurs (e.g. with Verso) is that the `master`/`main` branch has already been moved
to a nightly toolchain that comes *after* the stable toolchain we are
targeting. In this case it is necessary to create a branch `releases/v4.6.0` from the last commit which was on
an earlier toolchain, move that branch to the stable toolchain, and create the toolchain tag from that branch.
@@ -144,6 +144,10 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
- Run `script/release_steps.py v4.7.0-rc1 <repo>` (e.g. replacing `<repo>` with `batteries`), which will walk you through the following steps:
- Create a new branch off `master`/`main` (as specified in the `branch` field), called `bump_to_v4.7.0-rc1`.
- Merge `origin/bump/v4.7.0` if relevant (i.e. `bump-branch: true` appears in `release_repos.yml`).
- Otherwise, you *may* need to merge `origin/nightly-testing`.
- Note that for `verso` and `reference-manual` development happens on `nightly-testing`, so
we will merge that branch into `bump_to_v4.7.0-rc1`, but it is essential in the GitHub interface that we do a rebase merge,
in order to preserve the history.
- Update the contents of `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`.
- In the `lakefile.toml` or `lakefile.lean`, if there are dependencies on `nightly-testing`, `bump/v4.7.0`, or specific version tags, update them to the new tag.
If they depend on `main` or `master`, don't change this; you've just updated the dependency, so `lake update` will take care of modifying the manifest.
@@ -151,7 +155,7 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
- Run `lake build && if lake check-test; then lake test; fi` to check things are working.
- Commit the changes as `chore: bump toolchain to v4.7.0-rc1` and push.
- Create a PR with title "chore: bump toolchain to v4.7.0-rc1".
- Merge the PR once CI completes.
- Merge the PR once CI completes. (Recall: for `verso` and `reference-manual` you will need to do a rebase merge.)
- Re-running `script/release_checklist.py` will then create the tag `v4.7.0-rc1` from `master`/`main` and push it (unless `toolchain-tag: false` in the `release_repos.yml` file)
- We do this for the same list of repositories as for stable releases, see above for notes about special cases.
As above, there are dependencies between these, and so the process above is iterative.

View File

@@ -1,9 +0,0 @@
Examples
========
- [Palindromes](examples/palindromes.lean.md)
- [Binary Search Trees](examples/bintree.lean.md)
- [A Certified Type Checker](examples/tc.lean.md)
- [The Well-Typed Interpreter](examples/interp.lean.md)
- [Dependent de Bruijn Indices](examples/deBruijn.lean.md)
- [Parametric Higher-Order Abstract Syntax](examples/phoas.lean.md)

4
doc/examples/README.md Normal file
View File

@@ -0,0 +1,4 @@
These examples are checked in Lean's CI to ensure that they continue
to work. They are included in the documentation section of the Lean
website via a script that copies the latest version, in order to
ensure that the website tracks Lean releases rather than `master`.

View File

@@ -1,5 +0,0 @@
(this example is rendered by Alectryon in the CI)
```lean
{{#include bintree.lean}}
```

View File

@@ -1,5 +0,0 @@
(this example is rendered by Alectryon in the CI)
```lean
{{#include deBruijn.lean}}
```

View File

@@ -1,5 +0,0 @@
(this example is rendered by Alectryon in the CI)
```lean
{{#include interp.lean}}
```

View File

@@ -1,5 +0,0 @@
(this example is rendered by Alectryon in the CI)
```lean
{{#include palindromes.lean}}
```

View File

@@ -1,5 +0,0 @@
(this example is rendered by Alectryon in the CI)
```lean
{{#include phoas.lean}}
```

View File

@@ -1,5 +0,0 @@
(this example is rendered by Alectryon in the CI)
```lean
{{#include tc.lean}}
```

View File

@@ -1,5 +0,0 @@
(this chapter is rendered by Alectryon in the CI)
```lean
{{#include widgets.lean}}
```

View File

@@ -1,550 +0,0 @@
Expressions
===========
Every expression in Lean has a [Type](types.md). Every type is also an
expression of type `Sort u` for some universe level u. See [Type
Universes](types.md#type_universes).
Expression Syntax
=================
The set of expressions in Lean is defined inductively as follows:
* ``Sort u`` : the universe of types at universe level ``u``
* ``c`` : where ``c`` is an identifier denoting a declared constant or a defined object
* ``x`` : where ``x`` is a variable in the local context in which the expression is interpreted
* `m?` : where `m?` is a metavariable in the metavariable context in which the expression is interpreted,
you can view metavariable as a "hole" that still needs to be synthesized
* ``(x : α) → β`` : the type of functions taking an element ``x`` of ``α`` to an element of ``β``,
where ``β`` is an expression whose type is a ``Sort``
* ``s t`` : the result of applying ``s`` to ``t``, where ``s`` and ``t`` are expressions
* ``fun x : α => t`` or `λ x : α => t`: the function mapping any value ``x`` of type ``α`` to ``t``, where ``t`` is an expression
* ``let x := t; s`` : a local definition, denotes the value of ``s`` when ``x`` is replaced by ``t``
* `s.i` : a projection, denotes the value of the `i`-th field of `s`
* `lit` : a natural number or string literal
* `mdata k s` : the expression `s` decorated with metadata `k`, where is a key-value map
Every well formed term in Lean has a *type*, which itself is an expression of type ``Sort u`` for some ``u``. The fact that a term ``t`` has type ``α`` is written ``t : α``.
For an expression to be well formed, its components have to satisfy certain typing constraints. These, in turn, determine the type of the resulting term, as follows:
* ``Sort u : Sort (u + 1)``
* ``c : α``, where ``α`` is the type that ``c`` has been declared or defined to have
* ``x : α``, where ``α`` is the type that ``x`` has been assigned in the local context where it is interpreted
* ``?m : α``, where ``α`` is the type that ``?m`` has been declared in the metavariable context where it is interpreted
* ``(x : α) → β : Sort (imax u v)`` where ``α : Sort u``, and ``β : Sort v`` assuming ``x : α``
* ``s t : β[t/x]`` where ``s`` has type ``(x : α) → β`` and ``t`` has type ``α``
* ``(fun x : α => t) : (x : α) → β`` if ``t`` has type ``β`` whenever ``x`` has type ``α``
* ``(let x := t; s) : β[t/x]`` where ``t`` has type ``α`` and ``s`` has type ``β`` assuming ``x : α``
* `lit : Nat` if `lit` is a numeral
* `lit : String` if `lit` is a string literal
* `mdata k s : α` if `s : α`
* `s.i : α` if `s : β` and `β` is an inductive datatype with only one constructor, and `i`-th field has type `α`
``Prop`` abbreviates ``Sort 0``, ``Type`` abbreviates ``Sort 1``, and
``Type u`` abbreviates ``Sort (u + 1)`` when ``u`` is a universe
variable. We say "``α`` is a type" to express ``α : Type u`` for some
``u``, and we say "``p`` is a proposition" to express
``p : Prop``. Using the *propositions as types* correspondence, given
``p : Prop``, we refer to an expression ``t : p`` as a *proof* of ``p``. In
contrast, given ``α : Type u`` for some ``u`` and ``t : α``, we
sometimes refer to ``t`` as *data*.
When the expression ``β`` in ``(x : α) → β`` does not depend on ``x``,
it can be written ``α → β``. As usual, the variable ``x`` is bound in
``(x : α) → β``, ``fun x : α => t``, and ``let x := t; s``. The
expression ``∀ x : α, β`` is alternative syntax for ``(x : α) → β``,
and is intended to be used when ``β`` is a proposition. An underscore
can be used to generate an internal variable in a binder, as in
``fun _ : α => t``.
*Metavariables*, that is, temporary placeholders, are used in the
process of constructing terms. Terms that are added to the
environment contain neither metavariable nor variables, which is to
say, they are fully elaborated and make sense in the empty context.
Axioms can be declared using the ``axiom`` keyword.
Similarly, objects can be defined in various ways, such as using ``def`` and ``theorem`` keywords.
See [Chapter Declarations](./declarations.md) for more information.
Writing an expression ``(t : α)`` forces Lean to elaborate ``t`` so that it has type ``α`` or report an error if it fails.
Lean supports anonymous constructor notation, anonymous projections,
and various forms of match syntax, including destructuring ``fun`` and
``let``. These, as well as notation for common data types (like pairs,
lists, and so on) are discussed in [Chapter Declarations](./declarations.md)
in connection with inductive types.
```lean
universe u
#check Sort 0
#check Prop
#check Sort 1
#check Type
#check Sort u
#check Sort (u+1)
#check Nat → Bool
#check (α : Type u) → List α
#check (α : Type u) → (β : Type u) → Sum α β
#check fun x : Nat => x
#check fun (α : Type u) (x : α) => x
#check let x := 5; x * 2
#check "hello"
#check (fun x => x) true
```
Implicit Arguments
==================
When declaring arguments to defined objects in Lean (for example, with
``def``, ``theorem``, ``axiom``, ``constant``, ``inductive``, or
``structure``; see [Chapter Declarations](./declarations.md) or when
declaring variables in sections (see [Other Commands](./other_commands.md)),
arguments can be annotated as *explicit* or *implicit*.
This determines how expressions containing the object are interpreted.
* ``(x : α)`` : an explicit argument of type ``α``
* ``{x : α}`` : an implicit argument, eagerly inserted
* ``⦃x : α⦄`` or ``{{x : α}}`` : an implicit argument, weakly inserted
* ``[x : α]`` : an implicit argument that should be inferred by type class resolution
* ``(x : α := v)`` : an optional argument, with default value ``v``
* ``(x : α := by tac)`` : an implicit argument, to be synthesized by tactic ``tac``
The name of the variable can be omitted from a class resolution
argument, in which case an internal name is generated.
When a function has an explicit argument, you can nonetheless ask
Lean's elaborator to infer the argument automatically, by entering it
as an underscore (``_``). Conversely, writing ``@foo`` indicates that
all of the arguments to be ``foo`` are to be given explicitly,
independent of how ``foo`` was declared. You can also provide a value
for an implicit parameter using named arguments. Named arguments
enable you to specify an argument for a parameter by matching the
argument with its name rather than with its position in the parameter
list. If you don't remember the order of the parameters but know
their names, you can send the arguments in any order. You may also
provide the value for an implicit parameter whenLean failed to infer
it. Named arguments also improve the readability of your code by
identifying what each argument represents.
```lean
def add (x y : Nat) : Nat :=
x + y
#check add 2 3 -- Nat
#eval add 2 3 -- 5
def id1 (α : Type u) (x : α) : α := x
#check id1 Nat 3
#check id1 _ 3
def id2 {α : Type u} (x : α) : α := x
#check id2 3
#check @id2 Nat 3
#check id2 (α := Nat) 3
#check id2
#check id2 (α := Nat)
def id3 {{α : Type u}} (x : α) : α := x
#check id3 3
#check @id3 Nat 3
#check (id3 : (α : Type) → αα)
class Cls where
val : Nat
instance Cls_five : Cls where
val := 5
def ex2 [c : Cls] : Nat := c.val
example : ex2 = 5 := rfl
def ex2a [Cls] : Nat := ex2
example : ex2a = 5 := rfl
def ex3 (x : Nat := 5) := x
#check ex3 2
#check ex3
example : ex3 = 5 := rfl
def ex4 (x : Nat) (y : Nat := x) : Nat :=
x * y
example : ex4 x = x * x :=
rfl
```
Basic Data Types and Assertions
===============================
The core library contains a number of basic data types, such as the
natural numbers (`Nat`), the integers (`Int`), the
booleans (``Bool``), and common operations on these, as well as the
usual logical quantifiers and connectives. Some example are given
below. A list of common notations and their precedences can be found
in a [file](https://github.com/leanprover/lean4/blob/master/src/Init/Notation.lean)
in the core library. The core library also contains a number of basic
data type constructors. Definitions can also be found the
[Data](https://github.com/leanprover/lean4/blob/master/src/Init/Data)
directory of the core library. For more information, see also [Chapter libraries](./libraries.md).
```
/- numbers -/
def f1 (a b c : Nat) : Nat :=
a^2 + b^2 + c^2
def p1 (a b c d : Nat) : Prop :=
(a + b)^c ≤ d
def p2 (i j k : Int) : Prop :=
i % (j * k) = 0
/- booleans -/
def f2 (a b c : Bool) : Bool :=
a && (b || c)
/- pairs -/
#eval (1, 2)
def p : Nat × Bool := (1, false)
section
variable (a b c : Nat) (p : Nat × bool)
#check (1, 2)
#check p.1 * 2
#check p.2 && tt
#check ((1, 2, 3) : Nat × Nat × Nat)
end
/- lists -/
section
variable x y z : Nat
variable xs ys zs : list Nat
open list
#check (1 :: xs) ++ (y :: zs) ++ [1,2,3]
#check append (cons 1 xs) (cons y zs)
#check map (λ x, x^2) [1, 2, 3]
end
/- sets -/
section
variable s t u : set Nat
#check ({1, 2, 3} ∩ s) ({x | x < 7} ∩ t)
end
/- strings and characters -/
#check "hello world"
#check 'a'
/- assertions -/
#check ∀ a b c n : Nat,
a ≠ 0 ∧ b ≠ 0 ∧ c ≠ 0 ∧ n > 2 → a^n + b^n ≠ c^n
def unbounded (f : Nat → Nat) : Prop := ∀ M, ∃ n, f n ≥ M
```
.. _constructors_projections_and_matching:
Constructors, Projections, and Matching
=======================================
Lean's foundation, the *Calculus of Inductive Constructions*, supports the declaration of *inductive types*. Such types can have any number of *constructors*, and an associated *eliminator* (or *recursor*). Inductive types with one constructor, known as *structures*, have *projections*. The full syntax of inductive types is described in [Declarations](declarations.md), but here we describe some syntactic elements that facilitate their use in expressions.
When Lean can infer the type of an expression and it is an inductive type with one constructor, then one can write ``⟨a1, a2, ..., an⟩`` to apply the constructor without naming it. For example, ``⟨a, b⟩`` denotes ``prod.mk a b`` in a context where the expression can be inferred to be a pair, and ``⟨h₁, h₂⟩`` denotes ``and.intro h₁ h₂`` in a context when the expression can be inferred to be a conjunction. The notation will nest constructions automatically, so ``⟨a1, a2, a3⟩`` is interpreted as ``prod.mk a1 (prod.mk a2 a3)`` when the expression is expected to have a type of the form ``α1 × α2 × α3``. (The latter is interpreted as ``α1 × (α2 × α3)``, since the product associates to the right.)
Similarly, one can use "dot notation" for projections: one can write ``p.fst`` and ``p.snd`` for ``prod.fst p`` and ``prod.snd p`` when Lean can infer that ``p`` is an element of a product, and ``h.left`` and ``h.right`` for ``and.left h`` and ``and.right h`` when ``h`` is a conjunction.
The anonymous projector notation can used more generally for any objects defined in a *namespace* (see [Other Commands](other_commands.md)). For example, if ``l`` has type ``list α`` then ``l.map f`` abbreviates ``list.map f l``, in which ``l`` has been placed at the first argument position where ``list.map`` expects a ``list``.
Finally, for data types with one constructor, one destruct an element by pattern matching using the ``let`` and ``assume`` constructs, as in the examples below. Internally, these are interpreted using the ``match`` construct, which is in turn compiled down for the eliminator for the inductive type, as described in [Declarations](declarations.md).
.. code-block:: lean
universes u v
variable {α : Type u} {β : Type v}
def p : Nat × := ⟨1, 2⟩
#check p.fst
#check p.snd
def p' : Nat × × bool := ⟨1, 2, tt⟩
#check p'.fst
#check p'.snd.fst
#check p'.snd.snd
def swap_pair (p : α × β) : β × α :=
⟨p.snd, p.fst⟩
theorem swap_conj {a b : Prop} (h : a ∧ b) : b ∧ a :=
⟨h.right, h.left⟩
#check [1, 2, 3].append [2, 3, 4]
#check [1, 2, 3].map (λ x, x^2)
example (p q : Prop) : p ∧ q → q ∧ p :=
λ h, ⟨h.right, h.left⟩
def swap_pair' (p : α × β) : β × α :=
let (x, y) := p in (y, x)
theorem swap_conj' {a b : Prop} (h : a ∧ b) : b ∧ a :=
let ⟨ha, hb⟩ := h in ⟨hb, ha⟩
def swap_pair'' : α × β → β × α :=
λ ⟨x, y⟩, (y, x)
theorem swap_conj'' {a b : Prop} : a ∧ b → b ∧ a :=
assume ⟨ha, hb⟩, ⟨hb, ha⟩
Structured Proofs
=================
Syntactic sugar is provided for writing structured proof terms:
* ``have h : p := s; t`` is sugar for ``(fun h : p => t) s``
* ``suffices h : p from s; t`` is sugar for ``(λ h : p => s) t``
* ``suffices h : p by s; t`` is sugar for ``(suffixes h : p from by s; t)``
* ``show p from t`` is sugar for ``(have this : p := t; this)``
* ``show p by tac`` is sugar for ``(show p from by tac)``
Types can be omitted when they can be inferred by Lean. Lean also
allows ``have : p := t; s``, which gives the assumption the
name ``this`` in the local context. Similarly, Lean recognizes the
variant ``suffices p from s; t``, which use the name ``this`` for the new hypothesis.
The notation ``p`` is notation for ``(by assumption : p)``, and can
therefore be used to apply hypotheses in the local context.
As noted in [Constructors, Projections and Matching](#constructors_projections_and_matching),
anonymous constructors and projections and match syntax can be used in proofs just as in expressions that denote data.
.. code-block:: lean
example (p q r : Prop) : p → (q ∧ r) → p ∧ q :=
assume h₁ : p,
assume h₂ : q ∧ r,
have h₃ : q, from and.left h₂,
show p ∧ q, from and.intro h₁ h₃
example (p q r : Prop) : p → (q ∧ r) → p ∧ q :=
assume : p,
assume : q ∧ r,
have q, from and.left this,
show p ∧ q, from and.intro p this
example (p q r : Prop) : p → (q ∧ r) → p ∧ q :=
assume h₁ : p,
assume h₂ : q ∧ r,
suffices h₃ : q, from and.intro h₁ h₃,
show q, from and.left h₂
Lean also supports a calculational environment, which is introduced with the keyword ``calc``. The syntax is as follows:
.. code-block:: text
calc
<expr>_0 'op_1' <expr>_1 ':' <proof>_1
'...' 'op_2' <expr>_2 ':' <proof>_2
...
'...' 'op_n' <expr>_n ':' <proof>_n
Each ``<proof>_i`` is a proof for ``<expr>_{i-1} op_i <expr>_i``.
Here is an example:
.. code-block:: lean
variable (a b c d e : Nat)
variable h1 : a = b
variable h2 : b = c + 1
variable h3 : c = d
variable h4 : e = 1 + d
theorem T : a = e :=
calc
a = b : h1
... = c + 1 : h2
... = d + 1 : congr_arg _ h3
... = 1 + d : add_comm d (1 : Nat)
... = e : eq.symm h4
The style of writing proofs is most effective when it is used in conjunction with the ``simp`` and ``rewrite`` tactics.
.. _computation:
Computation
===========
Two expressions that differ up to a renaming of their bound variables are said to be *α-equivalent*, and are treated as syntactically equivalent by Lean.
Every expression in Lean has a natural computational interpretation, unless it involves classical elements that block computation, as described in the next section. The system recognizes the following notions of *reduction*:
* *β-reduction* : An expression ``(λ x, t) s`` β-reduces to ``t[s/x]``, that is, the result of replacing ``x`` by ``s`` in ``t``.
* *ζ-reduction* : An expression ``let x := s in t`` ζ-reduces to ``t[s/x]``.
* *δ-reduction* : If ``c`` is a defined constant with definition ``t``, then ``c`` δ-reduces to ``t``.
* *ι-reduction* : When a function defined by recursion on an inductive type is applied to an element given by an explicit constructor, the result ι-reduces to the specified function value, as described in [Inductive Types](inductive.md).
The reduction relation is transitive, which is to say, is ``s`` reduces to ``s'`` and ``t`` reduces to ``t'``, then ``s t`` reduces to ``s' t'``, ``λ x, s`` reduces to ``λ x, s'``, and so on. If ``s`` and ``t`` reduce to a common term, they are said to be *definitionally equal*. Definitional equality is defined to be the smallest equivalence relation that satisfies all these properties and also includes α-equivalence and the following two relations:
* *η-equivalence* : An expression ``(λx, t x)`` is η-equivalent to ``t``, assuming ``x`` does not occur in ``t``.
* *proof irrelevance* : If ``p : Prop``, ``s : p``, and ``t : p``, then ``s`` and ``t`` are considered to be equivalent.
This last fact reflects the intuition that once we have proved a proposition ``p``, we only care that is has been proved; the proof does nothing more than witness the fact that ``p`` is true.
Definitional equality is a strong notion of equality of values. Lean's logical foundations sanction treating definitionally equal terms as being the same when checking that a term is well-typed and/or that it has a given type.
The reduction relation is believed to be strongly normalizing, which is to say, every sequence of reductions applied to a term will eventually terminate. The property guarantees that Lean's type-checking algorithm terminates, at least in principle. The consistency of Lean and its soundness with respect to set-theoretic semantics do not depend on either of these properties.
Lean provides two commands to compute with expressions:
* ``#reduce t`` : use the kernel type-checking procedures to carry out reductions on ``t`` until no more reductions are possible, and show the result
* ``#eval t`` : evaluate ``t`` using a fast bytecode evaluator, and show the result
Every computable definition in Lean is compiled to bytecode at definition time. Bytecode evaluation is more liberal than kernel evaluation: types and all propositional information are erased, and functions are evaluated using a stack-based virtual machine. As a result, ``#eval`` is more efficient than ``#reduce,`` and can be used to execute complex programs. In contrast, ``#reduce`` is designed to be small and reliable, and to produce type-correct terms at each step. Bytecode is never used in type checking, so as far as soundness and consistency are concerned, only kernel reduction is part of the trusted computing base.
.. code-block:: lean
#reduce (fun x => x + 3) 5
#eval (fun x => x + 3) 5
#reduce let x := 5; x + 3
#eval let x := 5; x + 3
def f x := x + 3
#reduce f 5
#eval f 5
#reduce @Nat.rec (λ n => Nat) (0 : Nat)
(λ n recval : Nat => recval + n + 1) (5 : Nat)
def g : Nat → Nat
| 0 => 0
| (n+1) => g n + n + 1
#reduce g 5
#eval g 5
#eval g 5000
example : (fun x => x + 3) 5 = 8 := rfl
example : (fun x => f x) = f := rfl
example (p : Prop) (h₁ h₂ : p) : h₁ = h₂ := rfl
Note: the combination of proof irrelevance and singleton ``Prop`` elimination in ι-reduction renders the ideal version of definitional equality, as described above, undecidable. Lean's procedure for checking definitional equality is only an approximation to the ideal. It is not transitive, as illustrated by the example below. Once again, this does not compromise the consistency or soundness of Lean; it only means that Lean is more conservative in the terms it recognizes as well typed, and this does not cause problems in practice. Singleton elimination will be discussed in greater detail in [Inductive Types](inductive.md).
.. code-block:: lean
def R (x y : unit) := false
def accrec := @acc.rec unit R (λ_, unit) (λ _ a ih, ()) ()
example (h) : accrec h = accrec (acc.intro _ (λ y, acc.inv h)) :=
rfl
example (h) : accrec (acc.intro _ (λ y, acc.inv h)) = () := rfl
example (h) : accrec h = () := sorry -- rfl fails
Axioms
======
Lean's foundational framework consists of:
- type universes and dependent function types, as described above
- inductive definitions, as described in [Inductive Types](inductive.md) and
[Inductive Families](declarations.md#inductive-families).
In addition, the core library defines (and trusts) the following axiomatic extensions:
- propositional extensionality:
.. code-block:: lean
namespace hide
-- BEGIN
axiom propext {a b : Prop} : (a ↔ b) → a = b
-- END
end hide
- quotients:
.. code-block:: lean
namespace hide
-- BEGIN
universes u v
constant quot : Π {α : Sort u}, (αα → Prop) → Sort u
constant quot.mk : Π {α : Sort u} (r : αα → Prop),
α → quot r
axiom quot.ind : ∀ {α : Sort u} {r : αα → Prop}
{β : quot r → Prop},
(∀ a, β (quot.mk r a)) →
∀ (q : quot r), β q
constant quot.lift : Π {α : Sort u} {r : αα → Prop}
{β : Sort u} (f : α → β),
(∀ a b, r a b → f a = f b) → quot r → β
axiom quot.sound : ∀ {α : Type u} {r : αα → Prop}
{a b : α},
r a b → quot.mk r a = quot.mk r b
-- END
end hide
``quot r`` represents the quotient of ``α`` by the smallest equivalence relation containing ``r``. ``quot.mk`` and ``quot.lift`` satisfy the following computation rule:
.. code-block:: text
quot.lift f h (quot.mk r a) = f a
- choice:
.. code-block:: lean
namespace hide
universe u
-- BEGIN
axiom choice {α : Sort u} : nonempty αα
-- END
end hide
Here ``nonempty α`` is defined as follows:
.. code-block:: lean
namespace hide
universe u
-- BEGIN
class inductive nonempty (α : Sort u) : Prop
| intro : α → nonempty
-- END
end hide
It is equivalent to ``∃ x : α, true``.
The quotient construction implies function extensionality. The ``choice`` principle, in conjunction with the others, makes the axiomatic foundation classical; in particular, it implies the law of the excluded middle and propositional decidability. Functions that make use of ``choice`` to produce data are incompatible with a computational interpretation, and do not produce bytecode. They have to be declared ``noncomputable``.
For metaprogramming purposes, Lean also allows the definition of objects which stand outside the object language. These are denoted with the ``meta`` keyword, as described in [Metaprogramming](metaprogramming.md).

View File

@@ -1,55 +0,0 @@
Frequently Asked Questions
==========================
### What is Lean?
Lean is a new open source theorem prover being developed at Microsoft Research.
It is a research project that aims to bridge the gap between interactive and automated theorem proving.
Lean can be also used as a programming language. Actually, some Lean features are implemented in Lean itself.
### Should I use Lean?
Lean is under heavy development, and we are constantly trying new
ideas and tweaking the system. It is a research project and not a product.
Things change rapidly, and we constantly break backward compatibility.
Lean comes "as is", you should not expect we will fix bugs and/or add new features for your project.
We have our own priorities, and will not change them to accommodate your needs.
Even if you implement a new feature or fix a bug, we may not want to merge it because
it may conflict with our plans for Lean, it may not be performant, we may not want to maintain it,
we may be busy, etc. If you really need this new feature or bug fix, we suggest you create your own fork and maintain it yourself.
### Where is the documentation?
This is the Lean 4 manual. It is a work in progress, but it will eventually cover the whole language.
A public and very active chat room dedicated to Lean is open on [Zulip](https://leanprover.zulipchat.com).
It is a good place to interact with other Lean users.
### Should I use Lean to teach a course?
Lean has been used to teach courses on logic, type theory and programming languages at CMU and the University of Washington.
The lecture notes for the CMU course [Logic and Proof](https://lean-lang.org/logic_and_proof) are available online,
but they are for Lean 3.
If you decide to teach a course using Lean, we suggest you prepare all material before the beginning of the course, and
make sure that Lean attends all your needs. You should not expect we will fix bugs and/or add features needed for your course.
### Are there IDEs for Lean?
Yes, see [Setting Up Lean](./setup.md).
### Is Lean sound? How big is the kernel? Should I trust it?
Lean has a relatively small kernel.
Several independent checkers have been implemented for Lean 3. Two of them are
[tc](https://github.com/leanprover/tc) and [trepplein](https://github.com/gebner/trepplein).
We expect similar independent checkers will be built for Lean 4.
### Should I open a new issue?
We use [GitHub](https://github.com/leanprover/lean4/issues) to track bugs and new features.
Bug reports are always welcome, but nitpicking issues are not (e.g., the error message is confusing).
See also our [contribution guidelines](https://github.com/leanprover/lean4/blob/master/CONTRIBUTING.md).
### Is it Lean, LEAN, or L∃∀N?
We always use "Lean" in writing.
When specifying a major version number, we append it together with a single space: Lean 4.

151
doc/flake.lock generated
View File

@@ -1,151 +0,0 @@
{
"nodes": {
"alectryon": {
"flake": false,
"locked": {
"lastModified": 1654613606,
"narHash": "sha256-IGCn1PzTyw8rrwmyWUiw3Jo/dyZVGkMslnHYW7YB8yk=",
"owner": "Kha",
"repo": "alectryon",
"rev": "c3b16f650665745e1da4ddfcc048d3bd639f71d5",
"type": "github"
},
"original": {
"owner": "Kha",
"ref": "typeid",
"repo": "alectryon",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"lean": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"nixpkgs-old": "nixpkgs-old"
},
"locked": {
"lastModified": 0,
"narHash": "sha256-saRAtQ6VautVXKDw1XH35qwP0KEBKTKZbg/TRa4N9Vw=",
"path": "../.",
"type": "path"
},
"original": {
"path": "../.",
"type": "path"
}
},
"leanInk": {
"flake": false,
"locked": {
"lastModified": 1704976501,
"narHash": "sha256-FSBUsbX0HxakSnYRYzRBDN2YKmH9EkA0q9p7TSPEJTI=",
"owner": "leanprover",
"repo": "LeanInk",
"rev": "51821e3c2c032c88e4b2956483899d373ec090c4",
"type": "github"
},
"original": {
"owner": "leanprover",
"ref": "refs/pull/57/merge",
"repo": "LeanInk",
"type": "github"
}
},
"mdBook": {
"flake": false,
"locked": {
"lastModified": 1660074464,
"narHash": "sha256-W30G7AeWBjdJE/CQZJU5vJjaDGZtpmxEKNMEvaYtuF8=",
"owner": "leanprover",
"repo": "mdBook",
"rev": "9321c10c502cd59eea8afc4325a84eab3ddf9391",
"type": "github"
},
"original": {
"owner": "leanprover",
"repo": "mdBook",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1710889954,
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-old": {
"flake": false,
"locked": {
"lastModified": 1581379743,
"narHash": "sha256-i1XCn9rKuLjvCdu2UeXKzGLF6IuQePQKFt4hEKRU5oc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-19.03",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"alectryon": "alectryon",
"flake-utils": [
"lean",
"flake-utils"
],
"lean": "lean",
"leanInk": "leanInk",
"mdBook": "mdBook"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,93 +0,0 @@
{
description = "Lean documentation";
inputs.lean.url = path:../.;
inputs.flake-utils.follows = "lean/flake-utils";
inputs.mdBook = {
url = "github:leanprover/mdBook";
flake = false;
};
inputs.alectryon = {
url = "github:Kha/alectryon/typeid";
flake = false;
};
inputs.leanInk = {
url = "github:leanprover/LeanInk/refs/pull/57/merge";
flake = false;
};
outputs = inputs@{ self, ... }: inputs.flake-utils.lib.eachDefaultSystem (system:
with inputs.lean.packages.${system}.deprecated; with nixpkgs;
let
doc-src = lib.sourceByRegex ../. ["doc.*" "tests(/lean(/beginEndAsMacro.lean)?)?"];
in {
packages = rec {
lean-mdbook = mdbook.overrideAttrs (drv: rec {
name = "lean-${mdbook.name}";
src = inputs.mdBook;
cargoDeps = drv.cargoDeps.overrideAttrs (_: {
inherit src;
outputHash = "sha256-CO3A9Kpp4sIvkT9X3p+GTidazk7Fn4jf0AP2PINN44A=";
});
doCheck = false;
});
book = stdenv.mkDerivation {
name ="lean-doc";
src = doc-src;
buildInputs = [ lean-mdbook ];
buildCommand = ''
mkdir $out
# necessary for `additional-css`...?
cp -r --no-preserve=mode $src/doc/* .
# overwrite stub .lean.md files
cp -r ${inked}/* .
mdbook build -d $out
'';
};
leanInk = (buildLeanPackage {
name = "Main";
src = inputs.leanInk;
deps = [ (buildLeanPackage {
name = "LeanInk";
src = inputs.leanInk;
}) ];
executableName = "leanInk";
linkFlags = ["-rdynamic"];
}).executable;
alectryon = python3Packages.buildPythonApplication {
name = "alectryon";
src = inputs.alectryon;
propagatedBuildInputs =
[ leanInk lean-all ] ++
# https://github.com/cpitclaudel/alectryon/blob/master/setup.cfg
(with python3Packages; [ pygments dominate beautifulsoup4 docutils ]);
doCheck = false;
};
renderLeanMod = mod: mod.overrideAttrs (final: prev: {
name = "${prev.name}.md";
buildInputs = prev.buildInputs ++ [ alectryon ];
outputs = [ "out" ];
buildCommand = ''
dir=$(dirname $relpath)
mkdir -p $dir out/$dir
if [ -d $src ]; then cp -r $src/. $dir/; else cp $src $leanPath; fi
alectryon --frontend lean4+markup $leanPath --backend webpage -o $out/$leanPath.md
'';
});
renderPackage = pkg: symlinkJoin {
name = "${pkg.name}-mds";
paths = map renderLeanMod (lib.attrValues pkg.mods);
};
literate = buildLeanPackage {
name = "literate";
src = ./.;
roots = [
{ mod = "examples"; glob = "submodules"; }
];
};
inked = renderPackage literate;
doc = book;
};
defaultPackage = self.packages.${system}.doc;
});
}

View File

@@ -1,7 +0,0 @@
Functional Programming in Lean
=======================
The goal of [this book](https://lean-lang.org/functional_programming_in_lean/) is to be an accessible introduction to using Lean 4 as a programming language.
It should be useful both to people who want to use Lean as a general-purpose programming language and to mathematicians who want to develop larger-scale proof automation but do not have a background in functional programming.
It does not assume any background with functional programming, though it's probably not a good first book on programming in general.
New content will be added once per month until it's done.

View File

@@ -1,145 +0,0 @@
## Function Abstraction and Evaluation
We have seen that if we have ``m n : Nat``, then we have ``(m, n) : Nat × Nat``.
This gives us a way of creating pairs of natural numbers.
Conversely, if we have ``p : Nat × Nat``, then
we have ``p.1 : Nat`` and ``p.2 : Nat``.
This gives us a way of "using" a pair, by extracting its two components.
We already know how to "use" a function ``f : α → β``, namely,
we can apply it to an element ``a : α`` to obtain ``f a : β``.
But how do we create a function from another expression?
The companion to application is a process known as "lambda abstraction."
Suppose that giving a variable ``x : α`` we can construct an expression ``t : β``.
Then the expression ``fun (x : α) => t``, or, equivalently, ``λ (x : α) => t``, is an object of type ``α → β``.
Think of this as the function from ``α`` to ``β`` which maps any value ``x`` to the value ``t``,
which may depend on ``x``.
```lean
#check fun (x : Nat) => x + 5
#check λ (x : Nat) => x + 5
#check fun x : Nat => x + 5
#check λ x : Nat => x + 5
```
Here are some more examples:
```lean
constant f : Nat → Nat
constant h : Nat → Bool → Nat
#check fun x : Nat => fun y : Bool => h (f x) y -- Nat → Bool → Nat
#check fun (x : Nat) (y : Bool) => h (f x) y -- Nat → Bool → Nat
#check fun x y => h (f x) y -- Nat → Bool → Nat
```
Lean interprets the final three examples as the same expression; in the last expression,
Lean infers the type of ``x`` and ``y`` from the types of ``f`` and ``h``.
Some mathematically common examples of operations of functions can be described in terms of lambda abstraction:
```lean
constant f : Nat → String
constant g : String → Bool
constant b : Bool
#check fun x : Nat => x -- Nat → Nat
#check fun x : Nat => b -- Nat → Bool
#check fun x : Nat => g (f x) -- Nat → Bool
#check fun x => g (f x) -- Nat → Bool
```
Think about what these expressions mean. The expression ``fun x : Nat => x`` denotes the identity function on ``Nat``,
the expression ``fun x : α => b`` denotes the constant function that always returns ``b``,
and ``fun x : Nat => g (f x)``, denotes the composition of ``f`` and ``g``.
We can, in general, leave off the type annotation on a variable and let Lean infer it for us.
So, for example, we can write ``fun x => g (f x)`` instead of ``fun x : Nat => g (f x)``.
We can abstract over the constants `f` and `g` in the previous definitions:
```lean
#check fun (g : String → Bool) (f : Nat → String) (x : Nat) => g (f x)
-- (String → Bool) → (Nat → String) → Nat → Bool
```
We can also abstract over types:
```lean
#check fun (α β γ : Type) (g : β → γ) (f : α → β) (x : α) => g (f x)
```
The last expression, for example, denotes the function that takes three types, ``α``, ``β``, and ``γ``, and two functions, ``g : β → γ`` and ``f : α → β``, and returns the composition of ``g`` and ``f``. (Making sense of the type of this function requires an understanding of dependent products, which we will explain below.) Within a lambda expression ``fun x : α => t``, the variable ``x`` is a "bound variable": it is really a placeholder, whose "scope" does not extend beyond ``t``.
For example, the variable ``b`` in the expression ``fun (b : β) (x : α) => b`` has nothing to do with the constant ``b`` declared earlier.
In fact, the expression denotes the same function as ``fun (u : β) (z : α), u``. Formally, the expressions that are the same up to a renaming of bound variables are called *alpha equivalent*, and are considered "the same." Lean recognizes this equivalence.
Notice that applying a term ``t : α → β`` to a term ``s : α`` yields an expression ``t s : β``.
Returning to the previous example and renaming bound variables for clarity, notice the types of the following expressions:
```lean
#check (fun x : Nat => x) 1 -- Nat
#check (fun x : Nat => true) 1 -- Bool
constant f : Nat → String
constant g : String → Bool
#check
(fun (α β γ : Type) (g : β → γ) (f : α → β) (x : α) => g (f x)) Nat String Bool g f 0
-- Bool
```
As expected, the expression ``(fun x : Nat => x) 1`` has type ``Nat``.
In fact, more should be true: applying the expression ``(fun x : Nat => x)`` to ``1`` should "return" the value ``1``. And, indeed, it does:
```lean
#reduce (fun x : Nat => x) 1 -- 1
#reduce (fun x : Nat => true) 1 -- true
constant f : Nat → String
constant g : String → Bool
#reduce
(fun (α β γ : Type) (g : β → γ) (f : α → β) (x : α) => g (f x)) Nat String Bool g f 0
-- g (f 0)
```
The command ``#reduce`` tells Lean to evaluate an expression by *reducing* it to its normal form,
which is to say, carrying out all the computational reductions that are sanctioned by its kernel.
The process of simplifying an expression ``(fun x => t) s`` to ``t[s/x]`` -- that is, ``t`` with ``s`` substituted for the variable ``x`` --
is known as *beta reduction*, and two terms that beta reduce to a common term are called *beta equivalent*.
But the ``#reduce`` command carries out other forms of reduction as well:
```lean
constant m : Nat
constant n : Nat
constant b : Bool
#reduce (m, n).1 -- m
#reduce (m, n).2 -- n
#reduce true && false -- false
#reduce false && b -- false
#reduce b && false -- Bool.rec false false b
#reduce n + 0 -- n
#reduce n + 2 -- Nat.succ (Nat.succ n)
#reduce 2 + 3 -- 5
```
We explain later how these terms are evaluated.
For now, we only wish to emphasize that this is an important feature of dependent type theory:
every term has a computational behavior, and supports a notion of reduction, or *normalization*.
In principle, two terms that reduce to the same value are called *definitionally equal*.
They are considered "the same" by Lean's type checker, and Lean does its best to recognize and support these identifications.
The `#reduce` command is mainly useful to understand why two terms are considered the same.
Lean is also a programming language. It has a compiler to native code and an interpreter.
You can use the command `#eval` to execute expressions, and it is the preferred way of testing your functions.
Note that `#eval` and `#reduce` are *not* equivalent. The command `#eval` first compiles Lean expressions
into an intermediate representation (IR) and then uses an interpreter to execute the generated IR.
Some builtin types (e.g., `Nat`, `String`, `Array`) have a more efficient representation in the IR.
The IR has support for using foreign functions that are opaque to Lean.
In contrast, the ``#reduce`` command relies on a reduction engine similar to the one used in Lean's trusted kernel,
the part of Lean that is responsible for checking and verifying the correctness of expressions and proofs.
It is less efficient than ``#eval``, and treats all foreign functions as opaque constants.
We later discuss other differences between the two commands.

File diff suppressed because one or more lines are too long

View File

@@ -1,66 +0,0 @@
## Introducing Definitions
The ``def`` command provides one important way of defining new objects.
```lean
def foo : (Nat → Nat) → Nat :=
fun f => f 0
#check foo -- (Nat → Nat) → Nat
#print foo
```
We can omit the type when Lean has enough information to infer it:
```lean
def foo :=
fun (f : Nat → Nat) => f 0
```
The general form of a definition is ``def foo : α := bar``. Lean can usually infer the type ``α``, but it is often a good idea to write it explicitly.
This clarifies your intention, and Lean will flag an error if the right-hand side of the definition does not have the right type.
Lean also allows us to use an alternative format that puts the abstracted variables before the colon and omits the lambda:
```lean
def double (x : Nat) : Nat :=
x + x
#print double
#check double 3
#reduce double 3 -- 6
#eval double 3 -- 6
def square (x : Nat) :=
x * x
#print square
#check square 3
#reduce square 3 -- 9
#eval square 3 -- 9
def doTwice (f : Nat → Nat) (x : Nat) : Nat :=
f (f x)
#eval doTwice double 2 -- 8
```
These definitions are equivalent to the following:
```lean
def double : Nat → Nat :=
fun x => x + x
def square : Nat → Nat :=
fun x => x * x
def doTwice : (Nat → Nat) → Nat → Nat :=
fun f x => f (f x)
```
We can even use this approach to specify arguments that are types:
```lean
def compose (α β γ : Type) (g : β → γ) (f : α → β) (x : α) : γ :=
g (f x)
```

View File

@@ -1,369 +0,0 @@
# Significant changes from Lean 3
Lean 4 is not backward compatible with Lean 3.
We have rewritten most of the system, and took the opportunity to cleanup the syntax,
metaprogramming framework, and elaborator. In this section, we go over the most significant
changes.
## Lambda expressions
We do not use `,` anymore to separate the binders from the lambda expression body.
The Lean 3 syntax for lambda expressions was unconventional, and `,` has been overused in Lean 3.
For example, we believe a list of lambda expressions is quite confusing in Lean 3, since `,` is used
to separate the elements of a list, and in the lambda expression itself. We now use `=>` as the separator,
as an example, `fun x => x` is the identity function. One may still use the symbol `λ` as a shorthand for `fun`.
The lambda expression notation has many new features that are not supported in Lean 3.
## Pattern matching
In Lean 4, one can easily create new notation that abbreviates commonly used idioms. One of them is a
`fun` followed by a `match`. In the following examples, we define a few functions using `fun`+`match` notation.
```lean
# namespace ex1
def Prod.str : Nat × Nat String :=
fun (a, b) => "(" ++ toString a ++ ", " ++ toString b ++ ")"
structure Point where
x : Nat
y : Nat
z : Nat
def Point.addX : Point Point Nat :=
fun { x := a, .. } { x := b, .. } => a+b
def Sum.str : Option Nat String :=
fun
| some a => "some " ++ toString a
| none => "none"
# end ex1
```
## Implicit lambdas
In Lean 3 stdlib, we find many [instances](https://github.com/leanprover/lean/blob/master/library/init/category/reader.lean#L39) of the dreadful `@`+`_` idiom.
It is often used when the expected type is a function type with implicit arguments,
and we have a constant (`reader_t.pure` in the example) which also takes implicit arguments. In Lean 4, the elaborator automatically introduces lambdas
for consuming implicit arguments. We are still exploring this feature and analyzing its impact, but the experience so far has been very positive. As an example,
here is the example in the link above using Lean 4 implicit lambdas.
```lean
# variable (ρ : Type) (m : Type Type) [Monad m]
instance : Monad (ReaderT ρ m) where
pure := ReaderT.pure
bind := ReaderT.bind
```
Users can disable the implicit lambda feature by using `@` or writing a lambda expression with `{}` or `[]` binder annotations.
Here are few examples
```lean
# namespace ex2
def id1 : {α : Type} α α :=
fun x => x
def listId : List ({α : Type} α α) :=
(fun x => x) :: []
-- In this example, implicit lambda introduction has been disabled because
-- we use `@` before `fun`
def id2 : {α : Type} α α :=
@fun α (x : α) => id1 x
def id3 : {α : Type} α α :=
@fun α x => id1 x
def id4 : {α : Type} α α :=
fun x => id1 x
-- In this example, implicit lambda introduction has been disabled
-- because we used the binder annotation `{...}`
def id5 : {α : Type} α α :=
fun {α} x => id1 x
# end ex2
```
## Sugar for simple functions
In Lean 3, we can create simple functions from infix operators by using parentheses. For example, `(+1)` is sugar for `fun x, x + 1`. In Lean 4, we generalize this notation using `·` as a placeholder. Here are a few examples:
```lean
# namespace ex3
#check (· + 1)
-- fun a => a + 1
#check (2 - ·)
-- fun a => 2 - a
#eval [1, 2, 3, 4, 5].foldl (·*·) 1
-- 120
def f (x y z : Nat) :=
x + y + z
#check (f · 1 ·)
-- fun a b => f a 1 b
#eval [(1, 2), (3, 4), (5, 6)].map (·.1)
-- [1, 3, 5]
# end ex3
```
As in Lean 3, the notation is activated using parentheses, and the lambda abstraction is created by collecting the nested `·`s.
The collection is interrupted by nested parentheses. In the following example, two different lambda expressions are created.
```lean
#check (Prod.mk · (· + 1))
-- fun a => (a, fun b => b + 1)
```
## Function applications
In Lean 4, we have support for named arguments.
Named arguments enable you to specify an argument for a parameter by matching the argument with
its name rather than with its position in the parameter list.
If you don't remember the order of the parameters but know their names,
you can send the arguments in any order. You may also provide the value for an implicit parameter when
Lean failed to infer it. Named arguments also improve the readability of your code by identifying what
each argument represents.
```lean
def sum (xs : List Nat) :=
xs.foldl (init := 0) (·+·)
#eval sum [1, 2, 3, 4]
-- 10
example {a b : Nat} {p : Nat Nat Nat Prop} (h₁ : p a b b) (h₂ : b = a)
: p a a b :=
Eq.subst (motive := fun x => p a x b) h₂ h₁
```
In the following examples, we illustrate the interaction between named and default arguments.
```lean
def f (x : Nat) (y : Nat := 1) (w : Nat := 2) (z : Nat) :=
x + y + w - z
example (x z : Nat) : f (z := z) x = x + 1 + 2 - z := rfl
example (x z : Nat) : f x (z := z) = x + 1 + 2 - z := rfl
example (x y : Nat) : f x y = fun z => x + y + 2 - z := rfl
example : f = (fun x z => x + 1 + 2 - z) := rfl
example (x : Nat) : f x = fun z => x + 1 + 2 - z := rfl
example (y : Nat) : f (y := 5) = fun x z => x + 5 + 2 - z := rfl
def g {α} [Add α] (a : α) (b? : Option α := none) (c : α) : α :=
match b? with
| none => a + c
| some b => a + b + c
variable {α} [Add α]
example : g = fun (a c : α) => a + c := rfl
example (x : α) : g (c := x) = fun (a : α) => a + x := rfl
example (x : α) : g (b? := some x) = fun (a c : α) => a + x + c := rfl
example (x : α) : g x = fun (c : α) => x + c := rfl
example (x y : α) : g x y = fun (c : α) => x + y + c := rfl
```
In Lean 4, we can use `..` to provide missing explicit arguments as `_`.
This feature combined with named arguments is useful for writing patterns. Here is an example:
```lean
inductive Term where
| var (name : String)
| num (val : Nat)
| add (fn : Term) (arg : Term)
| lambda (name : String) (type : Term) (body : Term)
def getBinderName : Term Option String
| Term.lambda (name := n) .. => some n
| _ => none
def getBinderType : Term Option Term
| Term.lambda (type := t) .. => some t
| _ => none
```
Ellipsis are also useful when explicit argument can be automatically inferred by Lean, and we want
to avoid a sequence of `_`s.
```lean
example (f : Nat Nat) (a b c : Nat) : f (a + b + c) = f (a + (b + c)) :=
congrArg f (Nat.add_assoc ..)
```
In Lean 4, writing `f(x)` in place of `f x` is no longer allowed, you must use whitespace between the function and its arguments (e.g., `f (x)`).
## Dependent function types
Given `α : Type` and `β : α → Type`, `(x : α) → β x` denotes the type of functions `f` with the property that,
for each `a : α`, `f a` is an element of `β a`. In other words, the type of the value returned by `f` depends on its input.
We say `(x : α) → β x` is a dependent function type. In Lean 3, we write the dependent function type `(x : α) → β x` using
one of the following three equivalent notations:
`forall x : α, β x` or `∀ x : α, β x` or `Π x : α, β x`.
The first two were intended to be used for writing propositions, and the latter for writing code.
Although the notation `Π x : α, β x` has historical significance, we have removed it from Lean 4 because
it is awkward to use and often confuses new users. We can still write `forall x : α, β x` and `∀ x : α, β x`.
```lean
#check forall (α : Type), α α
#check (α : Type), α α
#check α : Type, α α
#check α, α α
#check (α : Type) α α
#check {α : Type} (a : Array α) (i : Nat) i < a.size α
#check {α : Type} [ToString α] α String
#check forall {α : Type} (a : Array α) (i : Nat), i < a.size α
#check {α β : Type} α β α × β
```
## The `meta` keyword
In Lean 3, the keyword `meta` is used to mark definitions that can use primitives implemented in C/C++.
These metadefinitions can also call themselves recursively, relaxing the termination
restriction imposed by ordinary type theory. Metadefinitions may also use unsafe primitives such as
`eval_expr (α : Type u) [reflected α] : expr → tactic α`, or primitives that break referential transparency
`tactic.unsafe_run_io`.
The keyword `meta` has been currently removed from Lean 4. However, we may re-introduce it in the future,
but with a much more limited purpose: marking meta code that should not be included in the executables produced by Lean.
The keyword `constant` has been deleted in Lean 4, and `axiom` should be used instead. In Lean 4, the new command `opaque` is used to define an opaque definition. Here are two simple examples:
```lean
# namespace meta1
opaque x : Nat := 1
-- The following example will not type check since `x` is opaque
-- example : x = 1 := rfl
-- We can evaluate `x`
#eval x
-- 1
-- When no value is provided, the elaborator tries to build one automatically for us
-- using the `Inhabited` type class
opaque y : Nat
# end meta1
```
We can instruct Lean to use a foreign function as the implementation for any definition
using the attribute `@[extern "foreign_function"]`. It is the user's responsibility to ensure the
foreign implementation is correct.
However, a user mistake here will only impact the code generated by Lean, and
it will **not** compromise the logical soundness of the system.
That is, you cannot prove `False` using the `@[extern]` attribute.
We use `@[extern]` with definitions when we want to provide a reference implementation in Lean
that can be used for reasoning. When we write a definition such as
```lean
@[extern "lean_nat_add"]
def add : Nat Nat Nat
| a, Nat.zero => a
| a, Nat.succ b => Nat.succ (add a b)
```
Lean assumes that the foreign function `lean_nat_add` implements the reference implementation above.
The `unsafe` keyword allows us to define functions using unsafe features such as general recursion,
and arbitrary type casting. Regular (safe) functions cannot directly use `unsafe` ones since it would
compromise the logical soundness of the system. As in regular programming languages, programs written
using unsafe features may crash at runtime. Here are a few unsafe examples:
```lean
unsafe def unsound : False :=
unsound
#check @unsafeCast
-- {α : Type _} → {β : Type _} → α → β
unsafe def nat2String (x : Nat) : String :=
unsafeCast x
-- The following definition doesn't type check because it is not marked as `unsafe`
-- def nat2StringSafe (x : Nat) : String :=
-- unsafeCast x
```
The `unsafe` keyword is particularly useful when we want to take advantage of an implementation detail of the
Lean execution runtime. For example, we cannot prove in Lean that arrays have a maximum size, but
the runtime used to execute Lean programs guarantees that an array cannot have more than 2^64 (2^32) elements
in a 64-bit (32-bit) machine. We can take advantage of this fact to provide a more efficient implementation for
array functions. However, the efficient version would not be very useful if it can only be used in
unsafe code. Thus, Lean 4 provides the attribute `@[implemented_by functionName]`. The idea is to provide
an unsafe (and potentially more efficient) version of a safe definition or constant. The function `f`
at the attribute `@[implemented_by f]` is very similar to an extern/foreign function,
the key difference is that it is implemented in Lean itself. Again, the logical soundness of the system
cannot be compromised by using the attribute `implemented_by`, but if the implementation is incorrect your
program may crash at runtime. In the following example, we define `withPtrUnsafe a k h` which
executes `k` using the memory address where `a` is stored in memory. The argument `h` is proof
that `k` is a constant function. Then, we "seal" this unsafe implementation at `withPtr`. The proof `h`
ensures the reference implementation `k 0` is correct. For more information, see the article
"Sealing Pointer-Based Optimizations Behind Pure Functions".
```lean
unsafe
def withPtrUnsafe {α β : Type} (a : α) (k : USize β) (h : u, k u = k 0) : β :=
k (ptrAddrUnsafe a)
@[implemented_by withPtrUnsafe]
def withPtr {α β : Type} (a : α) (k : USize β) (h : u, k u = k 0) : β :=
k 0
```
General recursion is very useful in practice, and it would be impossible to implement Lean 4 without it.
The keyword `partial` implements a very simple and efficient approach for supporting general recursion.
Simplicity was key here because of the bootstrapping problem. That is, we had to implement Lean in Lean before
many of its features were implemented (e.g., the tactic framework or support for wellfounded recursion).
Another requirement for us was performance. Functions tagged with `partial` should be as efficient as the ones implemented in mainstream functional programming
languages such as OCaml. When the `partial` keyword is used, Lean generates an auxiliary `unsafe` definition that
uses general recursion, and then defines an opaque constant that is implemented by this auxiliary definition.
This is very simple, efficient, and is sufficient for users that want to use Lean as a regular programming language.
A `partial` definition cannot use unsafe features such as `unsafeCast` and `ptrAddrUnsafe`, and it can only be used to
implement types we already known to be inhabited. Finally, since we "seal" the auxiliary definition using an opaque
constant, we cannot reason about `partial` definitions.
We are aware that proof assistants such as Isabelle provide a framework for defining partial functions that does not
prevent users from proving properties about them. This kind of framework can be implemented in Lean 4. Actually,
it can be implemented by users since Lean 4 is an extensible system. The developers current have no plans to implement
this kind of support for Lean 4. However, we remark that users can implement it using a function that traverses
the auxiliary unsafe definition generated by Lean, and produces a safe one using an approach similar to the one used in Isabelle.
```lean
# namespace partial1
partial def f (x : Nat) : IO Unit := do
IO.println x
if x < 100 then
f (x+1)
#eval f 98
# end partial1
```
## Library changes
These are changes to the library which may trip up Lean 3 users:
- `List` is no longer a monad.
## Style changes
Coding style changes have also been made:
- Term constants and variables are now `lowerCamelCase` rather than `snake_case`
- Type constants are now `UpperCamelCase`, eg `Nat`, `List`. Type variables are still lower case greek letters. Functors are still lower case latin `(m : Type → Type) [Monad m]`.
- When defining typeclasses, prefer not to use "has". Eg `ToString` or `Add` instead of `HasToString` or `HasAdd`.
- Prefer `return` to `pure` in monad expressions.
- Pipes `<|` are preferred to dollars `$` for function application.
- Declaration bodies should always be indented:
```lean
inductive Hello where
| foo
| bar
structure Point where
x : Nat
y : Nat
def Point.addX : Point → Point → Nat :=
fun { x := a, .. } { x := b, .. } => a + b
```
- In structures and typeclass definitions, prefer `where` to `:=` and don't surround fields with parentheses. (Shown in `Point` above)

View File

@@ -1,180 +0,0 @@
Lexical Structure
=================
This section describes the detailed lexical structure of the Lean
language.
A Lean program consists of a stream of UTF-8 tokens where each token
is one of the following:
```
token: symbol | command | ident | string | raw_string | char | numeral |
: decimal | doc_comment | mod_doc_comment | field_notation
```
Tokens can be separated by the whitespace characters space, tab, line
feed, and carriage return, as well as comments. Single-line comments
start with ``--``, whereas multi-line comments are enclosed by ``/-``
and ``-/`` and can be nested.
Symbols and Commands
====================
.. *(TODO: list built-in symbols and command tokens?)*
Symbols are static tokens that are used in term notations and
commands. They can be both keyword-like (e.g. the `have
<structured_proofs>` keyword) or use arbitrary Unicode characters.
Command tokens are static tokens that prefix any top-level declaration
or action. They are usually keyword-like, with transitory commands
like `#print <instructions>` prefixed by the ``#`` character. The set
of built-in commands is listed in [Other Commands](./other_commands.md).
Users can dynamically extend the sets of both symbols (via the
commands listed in [Quoted Symbols](#quoted-symbols) and command
tokens (via the `[user_command] <attributes>` attribute).
.. _identifiers:
Identifiers
===========
An *atomic identifier*, or *atomic name*, is (roughly) an alphanumeric
string that does not begin with a numeral. A (hierarchical)
*identifier*, or *name*, consists of one or more atomic names
separated by periods.
Parts of atomic names can be escaped by enclosing them in pairs of French double quotes ``«»``.
```lean
def Foo.«bar.baz» := 0 -- name parts ["Foo", "bar.baz"]
```
```
ident: atomic_ident | ident "." atomic_ident
atomic_ident: atomic_ident_start atomic_ident_rest*
atomic_ident_start: letterlike | "_" | escaped_ident_part
letterlike: [a-zA-Z] | greek | coptic | letterlike_symbols
greek: <[α-ωΑ-Ωἀ-] except for [λΠΣ]>
coptic: [ϊ-ϻ]
letterlike_symbols: [℀-⅏]
escaped_ident_part: "«" [^«»\r\n\t]* "»"
atomic_ident_rest: atomic_ident_start | [0-9'ⁿ] | subscript
subscript: [₀-₉ₐ-ₜᵢ-ᵪⱼ]
```
String Literals
===============
String literals are enclosed by double quotes (``"``). They may contain line breaks, which are conserved in the string value. Backslash (`\`) is a special escape character which can be used to the following
special characters:
- `\\` represents an escaped backslash, so this escape causes one backslash to be included in the string.
- `\"` puts a double quote in the string.
- `\'` puts an apostrophe in the string.
- `\n` puts a new line character in the string.
- `\t` puts a tab character in the string.
- `\xHH` puts the character represented by the 2 digit hexadecimal into the string. For example
"this \x26 that" which become "this & that". Values above 0x80 will be interpreted according to the
[Unicode table](https://unicode-table.com/en/) so "\xA9 Copyright 2021" is "© Copyright 2021".
- `\uHHHH` puts the character represented by the 4 digit hexadecimal into the string, so the following
string "\u65e5\u672c" will become "日本" which means "Japan".
- `\` followed by a newline and then any amount of whitespace is a "gap" that is equivalent to the empty string,
useful for letting a string literal span across multiple lines. Gaps spanning multiple lines can be confusing,
so the parser raises an error if the trailing whitespace contains any newlines.
So the complete syntax is:
```
string : '"' string_item '"'
string_item : string_char | char_escape | string_gap
string_char : [^"\\]
char_escape : "\" ("\" | '"' | "'" | "n" | "t" | "x" hex_char{2} | "u" hex_char{4})
hex_char : [0-9a-fA-F]
string_gap : "\" newline whitespace*
```
Raw String Literals
===================
Raw string literals are string literals without any escape character processing.
They begin with `r##...#"` (with zero or more `#` characters) and end with `"#...##` (with the same number of `#` characters).
The contents of a raw string literal may contain `"##..#` so long as the number of `#` characters
is less than the number of `#` characters used to begin the raw string literal.
```
raw_string : raw_string_aux(0) | raw_string_aux(1) | raw_string_aux(2) | ...
raw_string_aux(n) : 'r' '#'{n} '"' raw_string_item '"' '#'{n}
raw_string_item(n) : raw_string_char | raw_string_quote(n)
raw_string_char : [^"]
raw_string_quote(n) : '"' '#'{0..n-1}
```
Char Literals
=============
Char literals are enclosed by single quotes (``'``).
```
char : "'" char_item "'"
char_item : char_char | char_escape
char_char : [^'\\]
```
Numeric Literals
================
Numeric literals can be specified in various bases.
```
numeral : numeral10 | numeral2 | numeral8 | numeral16
numeral10 : [0-9]+ ("_"+ [0-9]+)*
numeral2 : "0" [bB] ("_"* [0-1]+)+
numeral8 : "0" [oO] ("_"* [0-7]+)+
numeral16 : "0" [xX] ("_"* hex_char+)+
```
Floating point literals are also possible with optional exponent:
```
float : numeral10 "." numeral10? [eE[+-]numeral10]
```
For example:
```
constant w : Int := 55
constant x : Nat := 26085
constant y : Nat := 0x65E5
constant z : Float := 2.548123e-05
constant b : Bool := 0b_11_01_10_00
```
Note: that negative numbers are created by applying the "-" negation prefix operator to the number, for example:
```
constant w : Int := -55
```
Doc Comments
============
A special form of comments, doc comments are used to document modules
and declarations.
```
doc_comment: "/--" ([^-] | "-" [^/])* "-/"
mod_doc_comment: "/-!" ([^-] | "-" [^/])* "-/"
```
Field Notation
==============
Trailing field notation tokens are used in expressions such as
``(1+1).to_string``. Note that ``a.toString`` is a single
[Identifier](#identifiers), but may be interpreted as a field
notation expression by the parser.
```
field_notation: "." ([0-9]+ | atomic_ident)
```

View File

@@ -1,134 +0,0 @@
# Arithmetic as an embedded domain-specific language
Let's parse another classic grammar, the grammar of arithmetic expressions with
addition, multiplication, integers, and variables. In the process, we'll learn
how to:
- Convert identifiers such as `x` into strings within a macro.
- add the ability to "escape" the macro context from within the macro. This is useful to interpret identifiers with their _original_ meaning (predefined values)
instead of their new meaning within a macro (treat as a symbol).
Let's begin with the simplest thing possible. We'll define an AST, and use operators `+` and `*` to denote
building an arithmetic AST.
Here's the AST that we will be parsing:
```lean,ignore
{{#include metaprogramming-arith.lean:1:5}}
```
We declare a syntax category to describe the grammar that we will be parsing.
See that we control the precedence of `+` and `*` by writing `syntax:50` for addition and `syntax:60` for multiplication,
indicating that multiplication binds tighter than addition (higher the number, tighter the binding).
This allows us to declare _precedence_ when defining new syntax.
```lean,ignore
{{#include metaprogramming-arith.lean:7:13}}
```
Further, if we look at `syntax:60 arith:60 "+" arith:61 : arith`, the
precedence declarations at `arith:60 "+" arith:61` conveys that the left
argument must have precedence at least `60` or greater, and the right argument
must have precedence at least`61` or greater. Note that this forces left
associativity. To understand this, let's compare two hypothetical parses:
```
-- syntax:60 arith:60 "+" arith:61 : arith -- Arith.add
-- a + b + c
(a:60 + b:61):60 + c
a + (b:60 + c:61):60
```
In the parse tree of `a + (b:60 + c:61):60`, we see that the right argument `(b + c)` is given the precedence `60`. However,
the rule for addition expects the right argument to have a precedence of **at least** 61, as witnessed by the `arith:61` at
the right-hand-side of `syntax:60 arith:60 "+" arith:61 : arith`. Thus, the rule `syntax:60 arith:60 "+" arith:61 : arith`
ensures that addition is left associative.
Since addition is declared arguments of precedence `60/61` and multiplication with `70/71`, this causes multiplication to bind
tighter than addition. Once again, let's compare two hypothetical parses:
```
-- syntax:60 arith:60 "+" arith:61 : arith -- Arith.add
-- syntax:70 arith:70 "*" arith:71 : arith -- Arith.mul
-- a * b + c
a * (b:60 + c:61):60
(a:70 * b:71):70 + c
```
While parsing `a * (b + c)`, `(b + c)` is assigned a precedence `60` by the addition rule. However, multiplication expects
the right argument to have precedence **at least** 71. Thus, this parse is invalid. In contrast, `(a * b) + c` assigns
a precedence of `70` to `(a * b)`. This is compatible with addition which expects the left argument to have precedence
**at least `60` ** (`70` is greater than `60`). Thus, the string `a * b + c` is parsed as `(a * b) + c`.
For more details, please look at the [Lean manual on syntax extensions](./notation.md#notations-and-precedence).
To go from strings into `Arith`, we define a macro to
translate the syntax category `arith` into an `Arith` inductive value that
lives in `term`:
```lean,ignore
{{#include metaprogramming-arith.lean:15:16}}
```
Our macro rules perform the "obvious" translation:
```lean,ignore
{{#include metaprogramming-arith.lean:18:23}}
```
And some examples:
```lean,ignore
{{#include metaprogramming-arith.lean:25:41}}
```
Writing variables as strings, such as `"x"` gets old; wouldn't it be so much
prettier if we could write `x * y`, and have the macro translate this into `Arith.mul (Arith.Symbol "x") (Arith.mul "y")`?
We can do this, and this will be our first taste of manipulating macro variables --- we'll use `x.getId` instead of directly evaluating `$x`.
We also write a macro rule for `Arith|` that translates an identifier into
a string, using `$(Lean.quote (toString x.getId))`:
```lean,ignore
{{#include metaprogramming-arith.lean:43:46}}
```
Let's test and see that we can now write expressions such as `x * y` directly instead of having to write `"x" * "y"`:
```lean,ignore
{{#include metaprogramming-arith.lean:48:51}}
```
We now show an unfortunate consequence of the above definitions. Suppose we want to build `(x + y) + z`.
Since we already have defined `xPlusY` as `x + y`, perhaps we should reuse it! Let's try:
```lean,ignore
#check `[Arith| xPlusY + z] -- Arith.add (Arith.symbol "xPlusY") (Arith.symbol "z")
```
Whoops, that didn't work! What happened? Lean treats `xPlusY` _itself_ as an identifier! So we need to add some syntax
to be able to "escape" the `Arith|` context. Let's use the syntax `<[ $e:term ]>` to mean: evaluate `$e` as a real term,
not an identifier. The macro looks like follows:
```lean,ignore
{{#include metaprogramming-arith.lean:53:56}}
```
Let's try our previous example:
```lean,ignore
{{#include metaprogramming-arith.lean:58:58}}
```
Perfect!
In this tutorial, we expanded on the previous tutorial to parse a more
realistic grammar with multiple levels of precedence, how to parse identifiers directly
within a macro, and how to provide an escape from within the macro context.
#### Full code listing
```lean
{{#include metaprogramming-arith.lean}}
```

View File

@@ -1,12 +0,0 @@
Mission
=======
Empower software developers to design, develop, and reason about programs.
Empower mathematicians and scientists to design, develop, and reason about formal models.
How
---
Lean is an efficient functional programming language based on dependent type theory.
It is under heavy development, but it already generates very efficient code.
It also has a powerful meta-programming framework, extensible parser, and IDE support based on LSP.

View File

@@ -1 +0,0 @@
# Other Commands

View File

@@ -1,83 +0,0 @@
/* Pygments stylesheet generated by Alectryon (style=None) */
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll, .code .hll { background-color: #ffffcc }
.highlight .c, .code .c { color: #555753; font-style: italic } /* Comment */
.highlight .err, .code .err { color: #a40000; border: 1px solid #cc0000 } /* Error */
.highlight .g, .code .g { color: #000000 } /* Generic */
.highlight .k, .code .k { color: #8f5902 } /* Keyword */
.highlight .l, .code .l { color: #2e3436 } /* Literal */
.highlight .n, .code .n { color: #000000 } /* Name */
.highlight .o, .code .o { color: #000000 } /* Operator */
.highlight .x, .code .x { color: #2e3436 } /* Other */
.highlight .p, .code .p { color: #000000 } /* Punctuation */
.highlight .ch, .code .ch { color: #555753; font-weight: bold; font-style: italic } /* Comment.Hashbang */
.highlight .cm, .code .cm { color: #555753; font-style: italic } /* Comment.Multiline */
.highlight .cp, .code .cp { color: #3465a4; font-style: italic } /* Comment.Preproc */
.highlight .cpf, .code .cpf { color: #555753; font-style: italic } /* Comment.PreprocFile */
.highlight .c1, .code .c1 { color: #555753; font-style: italic } /* Comment.Single */
.highlight .cs, .code .cs { color: #3465a4; font-weight: bold; font-style: italic } /* Comment.Special */
.highlight .gd, .code .gd { color: #a40000 } /* Generic.Deleted */
.highlight .ge, .code .ge { color: #000000; font-style: italic } /* Generic.Emph */
.highlight .gr, .code .gr { color: #a40000 } /* Generic.Error */
.highlight .gh, .code .gh { color: #a40000; font-weight: bold } /* Generic.Heading */
.highlight .gi, .code .gi { color: #4e9a06 } /* Generic.Inserted */
.highlight .go, .code .go { color: #000000; font-style: italic } /* Generic.Output */
.highlight .gp, .code .gp { color: #8f5902 } /* Generic.Prompt */
.highlight .gs, .code .gs { color: #000000; font-weight: bold } /* Generic.Strong */
.highlight .gu, .code .gu { color: #000000; font-weight: bold } /* Generic.Subheading */
.highlight .gt, .code .gt { color: #000000; font-style: italic } /* Generic.Traceback */
.highlight .kc, .code .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */
.highlight .kd, .code .kd { color: #4e9a06; font-weight: bold } /* Keyword.Declaration */
.highlight .kn, .code .kn { color: #4e9a06; font-weight: bold } /* Keyword.Namespace */
.highlight .kp, .code .kp { color: #204a87 } /* Keyword.Pseudo */
.highlight .kr, .code .kr { color: #8f5902 } /* Keyword.Reserved */
.highlight .kt, .code .kt { color: #204a87 } /* Keyword.Type */
.highlight .ld, .code .ld { color: #2e3436 } /* Literal.Date */
.highlight .m, .code .m { color: #2e3436 } /* Literal.Number */
.highlight .s, .code .s { color: #ad7fa8 } /* Literal.String */
.highlight .na, .code .na { color: #c4a000 } /* Name.Attribute */
.highlight .nb, .code .nb { color: #75507b } /* Name.Builtin */
.highlight .nc, .code .nc { color: #204a87 } /* Name.Class */
.highlight .no, .code .no { color: #ce5c00 } /* Name.Constant */
.highlight .nd, .code .nd { color: #3465a4; font-weight: bold } /* Name.Decorator */
.highlight .ni, .code .ni { color: #c4a000; text-decoration: underline } /* Name.Entity */
.highlight .ne, .code .ne { color: #cc0000 } /* Name.Exception */
.highlight .nf, .code .nf { color: #a40000 } /* Name.Function */
.highlight .nl, .code .nl { color: #3465a4; font-weight: bold } /* Name.Label */
.highlight .nn, .code .nn { color: #000000 } /* Name.Namespace */
.highlight .nx, .code .nx { color: #000000 } /* Name.Other */
.highlight .py, .code .py { color: #000000 } /* Name.Property */
.highlight .nt, .code .nt { color: #a40000 } /* Name.Tag */
.highlight .nv, .code .nv { color: #ce5c00 } /* Name.Variable */
.highlight .ow, .code .ow { color: #8f5902 } /* Operator.Word */
.highlight .w, .code .w { color: #d3d7cf; text-decoration: underline } /* Text.Whitespace */
.highlight .mb, .code .mb { color: #2e3436 } /* Literal.Number.Bin */
.highlight .mf, .code .mf { color: #2e3436 } /* Literal.Number.Float */
.highlight .mh, .code .mh { color: #2e3436 } /* Literal.Number.Hex */
.highlight .mi, .code .mi { color: #2e3436 } /* Literal.Number.Integer */
.highlight .mo, .code .mo { color: #2e3436 } /* Literal.Number.Oct */
.highlight .sa, .code .sa { color: #ad7fa8 } /* Literal.String.Affix */
.highlight .sb, .code .sb { color: #ad7fa8 } /* Literal.String.Backtick */
.highlight .sc, .code .sc { color: #ad7fa8; font-weight: bold } /* Literal.String.Char */
.highlight .dl, .code .dl { color: #ad7fa8 } /* Literal.String.Delimiter */
.highlight .sd, .code .sd { color: #ad7fa8 } /* Literal.String.Doc */
.highlight .s2, .code .s2 { color: #ad7fa8 } /* Literal.String.Double */
.highlight .se, .code .se { color: #ad7fa8; font-weight: bold } /* Literal.String.Escape */
.highlight .sh, .code .sh { color: #ad7fa8; text-decoration: underline } /* Literal.String.Heredoc */
.highlight .si, .code .si { color: #ce5c00 } /* Literal.String.Interpol */
.highlight .sx, .code .sx { color: #ad7fa8 } /* Literal.String.Other */
.highlight .sr, .code .sr { color: #ad7fa8 } /* Literal.String.Regex */
.highlight .s1, .code .s1 { color: #ad7fa8 } /* Literal.String.Single */
.highlight .ss, .code .ss { color: #8f5902 } /* Literal.String.Symbol */
.highlight .bp, .code .bp { color: #5c35cc } /* Name.Builtin.Pseudo */
.highlight .fm, .code .fm { color: #a40000 } /* Name.Function.Magic */
.highlight .vc, .code .vc { color: #ce5c00 } /* Name.Variable.Class */
.highlight .vg, .code .vg { color: #ce5c00; text-decoration: underline } /* Name.Variable.Global */
.highlight .vi, .code .vi { color: #ce5c00 } /* Name.Variable.Instance */
.highlight .vm, .code .vm { color: #ce5c00 } /* Name.Variable.Magic */
.highlight .il, .code .il { color: #2e3436 } /* Literal.Number.Integer.Long */
.hljs-doctag { color: green }
.hljs-comment { color: green }

View File

@@ -1,23 +0,0 @@
# Quickstart
These instructions will walk you through setting up Lean 4 together with VS Code as an editor for Lean 4.
See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
1. Install [VS Code](https://code.visualstudio.com/).
1. Launch VS Code and install the `Lean 4` extension by clicking on the 'Extensions' sidebar entry and searching for 'Lean 4'.
![installing the vscode-lean4 extension](images/code-ext.png)
1. Open the Lean 4 setup guide by creating a new text file using 'File > New Text File' (`Ctrl+N` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting 'Documentation… > Docs: Show Setup Guide'.
![show setup guide](images/show-setup-guide.png)
1. Follow the Lean 4 setup guide. It will:
- walk you through learning resources for Lean,
- teach you how to set up Lean's dependencies on your platform,
- install Lean 4 for you at the click of a button,
- help you set up your first project.
![setup guide](images/setup_guide.png)

View File

@@ -1,3 +0,0 @@
# The Lean Reference Manual
The latest version of the Lean reference manual is available [here](https://lean-lang.org/doc/reference/latest).

View File

@@ -1,23 +0,0 @@
Semantic Highlighting
---------------------
The Lean language server provides semantic highlighting information to editors. In order to benefit from this in VSCode, you may need to activate the "Editor > Semantic Highlighting" option in the preferences (this is translates to `"editor.semanticHighlighting.enabled": true,`
in `settings.json`). The default option here is to let your color theme decides whether it activates semantic highlighting (the default themes Dark+ and Light+ do activate it for instance).
However this may be insufficient if your color theme does not distinguish enough syntax categories or distinguishes them very subtly. For instance the default Light+ theme uses color `#001080` for variables. This is awfully close to `#000000` that is used as the default text color. This makes it very easy to miss an accidental use of [auto bound implicit arguments](https://lean-lang.org/lean4/doc/autobound.html). For instance in
```lean
def my_id (n : nat) := n
```
maybe `nat` is a typo and `Nat` was intended. If your color theme is good enough then you should see that `n` and `nat` have the same color since they are both marked as variables by semantic highlighting. If you rather write `(n : Nat)` then `n` keeps its variable color but `Nat` gets the default text color.
If you use such a bad theme, you can fix things by modifying the `Semantic Token Color Customizations` configuration. This cannot be done directly in the preferences dialog but you can click on "Edit in settings.json" to directly edit the settings file. Beware that you must save this file (in the same way you save any file opened in VSCode) before seeing any effect in other tabs or VSCode windows.
In the main config object, you can add something like
```
"editor.semanticTokenColorCustomizations": {
"[Default Light+]": {"rules": {"function": "#ff0000", "property": "#00ff00", "variable": "#ff00ff"}}
},
```
The colors in this example are not meant to be nice but to be easy to spot in your file when testing. Of course you need to replace `Default Light+` with the name of your theme, and you can customize several themes if you use several themes. VSCode will display small colored boxes next to the HTML color specifications. Hovering on top of a color specification opens a convenient color picker dialog.
In order to understand what `function`, `property` and `variable` mean in the above example, the easiest path is to open a Lean file and ask VSCode about its classification of various bits of your file. Open the command palette with Ctrl-shift-p (or ⌘-shift-p on a Mac) and search for "Inspect Editor Tokens and Scopes" (typing the word "tokens" should be enough to see it). You can then click on any word in your file and look if there is a "semantic token type" line in the displayed information.

View File

@@ -1,65 +0,0 @@
# Supported Platforms
### Tier 1
Platforms built & tested by our CI, available as binary releases via elan (see below)
* x86-64 Linux with glibc 2.26+
* x86-64 macOS 10.15+
* aarch64 (Apple Silicon) macOS 10.15+
* x86-64 Windows 11 (any version), Windows 10 (version 1903 or higher), Windows Server 2022, Windows Server 2025
### Tier 2
Platforms cross-compiled but not tested by our CI, available as binary releases
Releases may be silently broken due to the lack of automated testing.
Issue reports and fixes are welcome.
* aarch64 Linux with glibc 2.27+
* x86 (32-bit) Linux
* Emscripten Web Assembly
<!--
### Tier 3
Platforms that are known to work from manual testing, but do not come with CI or official releases
-->
# Setting Up Lean
See also the [quickstart](./quickstart.md) instructions for a standard setup with VS Code as the editor.
Release builds for all supported platforms are available at <https://github.com/leanprover/lean4/releases>.
Instead of downloading these and setting up the paths manually, however, it is recommended to use the Lean version manager [`elan`](https://github.com/leanprover/elan) instead:
```sh
$ elan self update # in case you haven't updated elan in a while
# download & activate latest Lean 4 stable release (https://github.com/leanprover/lean4/releases)
$ elan default leanprover/lean4:stable
```
## `lake`
Lean 4 comes with a package manager named `lake`.
Use `lake init foo` to initialize a Lean package `foo` in the current directory, and `lake build` to typecheck and build it as well as all its dependencies. Use `lake help` to learn about further commands.
The general directory structure of a package `foo` is
```sh
lakefile.lean # package configuration
lean-toolchain # specifies the lean version to use
Foo.lean # main file, import via `import Foo`
Foo/
A.lean # further files, import via e.g. `import Foo.A`
A/... # further nesting
.lake/ # `lake` build output directory
```
After running `lake build` you will see a binary named `./.lake/build/bin/foo` and when you run it you should see the output:
```
Hello, world!
```
## Editing
Lean implements the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) that can be used for interactive development in [Emacs](https://github.com/leanprover/lean4-mode), [VS Code](https://github.com/leanprover-community/vscode-lean4), and possibly other editors.
Changes must be saved to be visible in other files, which must then be invalidated using an editor command (see links above).

View File

@@ -1,91 +0,0 @@
## Simple Type Theory
"Type theory" gets its name from the fact that every expression has an associated *type*.
For example, in a given context, ``x + 0`` may denote a natural number and ``f`` may denote a function on the natural numbers.
For those that don't like math, a Lean natural number is an arbitrary-precision unsigned integer.
Here are some examples of how we can declare objects in Lean and check their types.
```lean
/- Declare some constants. -/
constant m : Nat -- m is a natural number
constant n : Nat
constant b1 : Bool -- b1 is a Boolean
constant b2 : Bool
/- Check their types. -/
#check m -- output: Nat
#check n
#check n + 0 -- Nat
#check m * (n + 0) -- Nat
#check b1 -- Bool
#check b1 && b2 -- "&&" is the Boolean and
#check b1 || b2 -- Boolean or
#check true -- Boolean "true"
```
Any text between ``/-`` and ``-/`` constitutes a comment block that is ignored by Lean.
Similarly, two dashes `--` indicate that the rest of the line contains a comment that is also ignored.
Comment blocks can be nested, making it possible to "comment out" chunks of code, just as in many programming languages.
The ``constant`` command introduce new constant symbols into the working environment.
The ``#check`` command asks Lean to report their types; in Lean, auxiliary commands that query the system for
information typically begin with the hash symbol. You should try declaring some constants and type checking
some expressions on your own. Declaring new objects in this way is a good way to experiment with the system.
What makes simple type theory powerful is that one can build new types out of others.
For example, if ``a`` and ``b`` are types, ``a -> b`` denotes the type of functions from ``a`` to ``b``,
and ``a × b`` denotes the type of pairs consisting of an element of ``a``
paired with an element of ``b``, also known as the *Cartesian product*.
Note that `×` is a Unicode symbol. We believe that judicious use of Unicode improves legibility,
and all modern editors have great support for it. In the Lean standard library, we often use
Greek letters to denote types, and the Unicode symbol `` as a more compact version of `->`.
```lean
constant m : Nat
constant n : Nat
constant f : Nat → Nat -- type the arrow as "\to" or "\r"
constant f' : Nat -> Nat -- alternative ASCII notation
constant p : Nat × Nat -- type the product as "\times"
constant q : Prod Nat Nat -- alternative notation
constant g : Nat → Nat → Nat
constant g' : Nat → (Nat → Nat) -- has the same type as g!
constant h : Nat × Nat → Nat
constant F : (Nat → Nat) → Nat -- a "functional"
#check f -- Nat → Nat
#check f n -- Nat
#check g m n -- Nat
#check g m -- Nat → Nat
#check (m, n) -- Nat × Nat
#check p.1 -- Nat
#check p.2 -- Nat
#check (m, n).1 -- Nat
#check (p.1, n) -- Nat × Nat
#check F f -- Nat
```
Once again, you should try some examples on your own.
Let us dispense with some basic syntax. You can enter the unicode arrow ``→`` by typing ``\to`` or ``\r``.
You can also use the ASCII alternative ``->``, so the expressions ``Nat -> Nat`` and ``Nat → Nat`` mean the same thing.
Both expressions denote the type of functions that take a natural number as input and return a natural number as output.
The unicode symbol ``×`` for the Cartesian product is entered as ``\times``.
We will generally use lower-case Greek letters like ``α``, ``β``, and ``γ`` to range over types.
You can enter these particular ones with ``\a``, ``\b``, and ``\g``.
There are a few more things to notice here. First, the application of a function ``f`` to a value ``x`` is denoted ``f x``.
Second, when writing type expressions, arrows associate to the *right*; for example, the type of ``g`` is ``Nat → (Nat → Nat)``.
Thus we can view ``g`` as a function that takes natural numbers and returns another function that takes a natural number and
returns a natural number.
In type theory, this is generally more convenient than writing ``g`` as a function that takes a pair of natural numbers as input
and returns a natural number as output. For example, it allows us to "partially apply" the function ``g``.
The example above shows that ``g m`` has type ``Nat → Nat``, that is, the function that "waits" for a second argument, ``n``,
and then returns ``g m n``. Taking a function ``h`` of type ``Nat × Nat → Nat`` and "redefining" it to look like ``g`` is a process
known as *currying*, something we will come back to below.
By now you may also have guessed that, in Lean, ``(m, n)`` denotes the ordered pair of ``m`` and ``n``,
and if ``p`` is a pair, ``p.1`` and ``p.2`` denote the two projections.

View File

@@ -1,9 +0,0 @@
# The Lean standard library
This directory contains development information about the Lean standard library. The user-facing documentation of the standard library
is part of the [Lean Language Reference](https://lean-lang.org/doc/reference/latest/).
Here you will find
* the [standard library vision document](./vision.md), including the call for contributions,
* the [standard library style guide](./style.md), and
* the [standard library naming conventions](./naming.md).

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -1,260 +0,0 @@
# Standard library naming conventions
The easiest way to access a result in the standard library is to correctly guess the name of the declaration (possibly with the help of identifier autocompletion). This is faster and has lower friction than more sophisticated search tools, so easily guessable names (which are still reasonably short) make Lean users more productive.
The guide that follows contains very few hard rules, many heuristics and a selection of examples. It cannot and does not present a deterministic algorithm for choosing good names in all situations. It is intended as a living document that gets clarified and expanded as situations arise during code reviews for the standard library. If applying one of the suggestions in this guide leads to nonsensical results in a certain situation, it is
probably safe to ignore the suggestion (or even better, suggest a way to improve the suggestion).
## Prelude
Identifiers use a mix of `UpperCamelCase`, `lowerCamelCase` and `snake_case`, used for types, data, and theorems, respectively.
Structure fields should be named such that the projections have the correct names.
## Naming convention for types
When defining a type, i.e., a (possibly 0-ary) function whose codomain is Sort u for some u, it should be named in UpperCamelCase. Examples include `List`, and `List.IsPrefix`.
When defining a predicate, prefix the name by `Is`, like in `List.IsPrefix`. The `Is` prefix may be omitted if
* the resulting name would be ungrammatical, or
* the predicate depends on additional data in a way where the `Is` prefix would be confusing (like `List.Pairwise`), or
* the name is an adjective (like `Std.Time.Month.Ordinal.Valid`)
## Namespaces and generalized projection notation
Almost always, definitions and theorems relating to a type should be placed in a namespace with the same name as the type. For example, operations and theorems about lists should be placed in the `List` namespace, and operations and theorems about `Std.Time.PlainDate` should be placed in the `Std.Time.PlainDate` namespace.
Declarations in the root namespace will be relatively rare. The most common type of declaration in the root namespace are declarations about data and properties exported by notation type classes, as long as they are not about a specific type implementing that type class. For example, we have
```lean
theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b a = b := sorry
```
in the root namespace, but
```lean
theorem List.cons_beq_cons [BEq α] {a b : α} {l₁ l₂ : List α} :
(a :: l₁ == b :: l₂) = (a == b && l₁ == l₂) := rfl
```
belongs in the `List` namespace.
Subtleties arise when multiple namespaces are in play. Generally, place your theorem in the most specific namespace that appears in one of the hypotheses of the theorem. The following names are both correct according to this convention:
```lean
theorem List.Sublist.reverse : l₁ <+ l₂ l₁.reverse <+ l₂.reverse := sorry
theorem List.reverse_sublist : l₁.reverse <+ l₂.reverse l₁ <+ l₂ := sorry
```
Notice that the second theorem does not have a hypothesis of type `List.Sublist l` for some `l`, so the name `List.Sublist.reverse_iff` would be incorrect.
The advantage of placing results in a namespace like `List.Sublist` is that it enables generalized projection notation, i.e., given `h : l₁ <+ l₂`,
one can write `h.reverse` to obtain a proof of `l₁.reverse <+ l₂.reverse`. Thinking about which dot notations are convenient can act as a guideline
for deciding where to place a theorem, and is, on occasion, a good reason to duplicate a theorem into multiple namespaces.
### The `Std` namespace
New types that are added will usually be placed in the `Std` namespace and in the `Std/` source directory, unless there are good reasons to place
them elsewhere.
Inside the `Std` namespace, all internal declarations should be `private` or else have a name component that clearly marks them as internal, preferably
`Internal`.
## Naming convention for data
When defining data, i.e., a (possibly 0-ary) function whose codomain is not Sort u, but has type Type u for some u, it should be named in lowerCamelCase. Examples include `List.append` and `List.isPrefixOf`.
If your data is morally fully specified by its type, then use the naming procedure for theorems described below and convert the result to lower camel case.
If your function returns an `Option`, consider adding `?` as a suffix. If your function may panic, consider adding `!` as a suffix. In many cases, there will be multiple variants of a function; one returning an option, one that may panic and possibly one that takes a proof argument.
## Naming algorithm for theorems and some definitions
There is, in principle, a general algorithm for naming a theorem. The problem with this algorithm is that it produces very long and unwieldy names which need to be shortened. So choosing a name for a declaration can be thought of as consisting of a mechanical part and a creative part.
Usually the first part is to decide which namespace the result should live in, according to the guidelines described above.
Next, consider the type of your declaration as a tree. Inner nodes of this tree are function types or function applications. Leaves of the tree are 0-ary functions or bound variables.
As an example, consider the following result from the standard library:
```lean
example {α : Type u} {β : Type v} [BEq α] [Hashable α] [EquivBEq α] [LawfulHashable α]
[Inhabited β] {m : Std.HashMap α β} {a : α} {h' : a m} : m[a]? = some (m[a]'h') :=
sorry
```
The correct namespace is clearly `Std.HashMap`. The corresponding tree looks like this:
![](naming-tree.svg)
The preferred spelling of a notation can be looked up by hovering over the notation.
Now traverse the tree and build a name according to the following rules:
* When encountering a function type, first turn the result type into a name, then all of the argument types from left to right, and join the names using `_of_`.
* When encountering a function that is neither an infix notation nor a structure projection, first put the function name and then the arguments, joined by an underscore.
* When encountering an infix notation, join the arguments using the name of the notation, separated by underscores.
* When encountering a structure projection, proceed as for normal functions, but put the name of the projection last.
* When encountering a name, put it in lower camel case.
* Skip bound variables and proofs.
* Type class arguments are also generally skipped.
When encountering namespaces names, concatenate them in lower camel case.
Applying this algorithm to our example yields the name `Std.HashMap.getElem?_eq_optionSome_getElem_of_mem`.
From there, the name should be shortened, using the following heuristics:
* The namespace of functions can be omitted if it is clear from context or if the namespace is the current one. This is almost always the case.
* For infix operators, it is possible to leave out the RHS or the name of the notation and the RHS if they are clear from context.
* Hypotheses can be left out if it is clear that they are required or if they appear in the conclusion.
Based on this, here are some possible names for our example:
1. `Std.HashMap.getElem?_eq`
2. `Std.HashMap.getElem?_eq_of_mem`
3. `Std.HashMap.getElem?_eq_some`
4. `Std.HashMap.getElem?_eq_some_of_mem`
5. `Std.HashMap.getElem?_eq_some_getElem`
6. `Std.Hashmap.getElem?_eq_some_getElem_of_mem`
Choosing a good name among these then requires considering the context of the lemma. In this case it turns out that the first four options are underspecified as there is also a lemma relating `m[a]?` and `m[a]!` which could have the same name. This leaves the last two options, the first of which is shorter, and this is how the lemma is called in the Lean standard library.
Here are some additional examples:
```lean
example {x y : List α} (h : x <+: y) (hx : x []) :
x.head hx = y.head (h.ne_nil hx) := sorry
```
Since we have an `IsPrefix` parameter, this should live in the `List.IsPrefix` namespace, and the algorithm suggests `List.IsPrefix.head_eq_head_of_ne_nil`, which is shortened to `List.IsPrefix.head`. Note here the difference between the namespace name (`IsPrefix`) and the recommended spelling of the corresponding notation (`prefix`).
```lean
example : l₁ <+: l₂ reverse l₁ <:+ reverse l₂ := sorry
```
Again, this result should be in the `List.IsPrefix` namespace; the algorithm suggests `List.IsPrefix.reverse_prefix_reverse`, which becomes `List.IsPrefix.reverse`.
The following examples show how the traversal order often matters.
```lean
theorem Nat.mul_zero (n : Nat) : n * 0 = 0 := sorry
theorem Nat.zero_mul (n : Nat) : 0 * n = 0 := sorry
```
Here we see that one name may be a prefix of another name:
```lean
theorem Int.mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 := sorry
theorem Int.mul_ne_zero_iff {a b : Int} : a * b 0 a 0 b 0 := sorry
```
It is usually a good idea to include the `iff` in a theorem name even if the name would still be unique without the name. For example,
```lean
theorem List.head?_eq_none_iff : l.head? = none l = [] := sorry
```
is a good name: if the lemma was simply called `List.head?_eq_none`, users might try to `apply` it when the goal is `l.head? = none`, leading
to confusion.
The more common you expect (or want) a theorem to be, the shorter you should try to make the name. For example, we have both
```lean
theorem Std.HashMap.getElem?_eq_none_of_contains_eq_false {a : α} : m.contains a = false m[a]? = none := sorry
theorem Std.HashMap.getElem?_eq_none {a : α} : ¬a m m[a]? = none := sorry
```
As users of the hash map are encouraged to use ∈ rather than contains, the second lemma gets the shorter name.
## Special cases
There are certain special “keywords” that may appear in identifiers.
| Keyword | Meaning | Example |
| :---- | :---- | :---- |
| `def` | Unfold a definition. Avoid this for public APIs. | `Nat.max_def` |
| `refl` | Theorems of the form `a R a`, where R is a reflexive relation and `a` is an explicit parameter | `Nat.le_refl` |
| `rfl` | Like `refl`, but with `a` implicit | `Nat.le_rfl` |
| `irrefl` | Theorems of the form `¬a R a`, where R is an irreflexive relation | `Nat.lt_irrefl` |
| `symm` | Theorems of the form `a R b → b R a`, where R is a symmetric relation (compare `comm` below) | `Eq.symm` |
| `trans` | Theorems of the form `a R b → b R c → a R c`, where R is a transitive relation (R may carry data) | `Eq.trans` |
| `antisymmm` | Theorems of the form `a R b → b R a → a = b`, where R is an antisymmetric relation | `Nat.le_antisymm` |
| `congr` | Theorems of the form `a R b → f a S f b`, where R and S are usually equivalence relations | `Std.HashMap.mem_congr` |
| `comm` | Theorems of the form `f a b = f b a` (compare `symm` above) | `Eq.comm`, `Nat.add_comm` |
| `assoc` | Theorems of the form `g (f a b) c = f a (g b c)` (note the order! In most cases, we have f = g) | `Nat.add_sub_assoc` |
| `distrib` | Theorems of the form `f (g a b) = g (f a) (f b)` | `Nat.add_left_distrib` |
| `self` | May be used if a variable appears multiple times in the conclusion | `List.mem_cons_self` |
| `inj` | Theorems of the form `f a = f b ↔ a = b`. | `Int.neg_inj`, `Nat.add_left_inj` |
| `cancel` | Theorems which have one of the forms `f a = f b → a = b` or `g (f a) = a`, where `f` and `g` usually involve a binary operator | `Nat.add_sub_cancel` |
| `cancel_iff` | Same as `inj`, but with different conventions for left and right (see below) | `Nat.add_right_cancel_iff` |
| `ext` | Theorems of the form `f a = f b → a = b`, where `f` usually involves some kind of projection | `List.ext_getElem`
| `mono` | Theorems of the form `a R b → f a R f b`, where `R` is a transitive relation | `List.countP_mono_left`
### Left and right
The keywords left and right are useful to disambiguate symmetric variants of theorems.
```lean
theorem imp_congr_left (h : a b) : (a c) (b c) := sorry
theorem imp_congr_right (h : a (b c)) : (a b) (a c) := sorry
```
It is not always obvious which version of a theorem should be “left” and which should be “right”.
Heuristically, the theorem should name the side which is “more variable”, but there are exceptions. For some of the special keywords discussed in this section, there are conventions which should be followed, as laid out in the following examples:
```lean
theorem Nat.left_distrib (n m k : Nat) : n * (m + k) = n * m + n * k := sorry
theorem Nat.right_distrib (n m k : Nat) : (n + m) * k = n * k + m * k := sorry
theorem Nat.add_left_cancel {n m k : Nat} : n + m = n + k m = k := sorry
theorem Nat.add_right_cancel {n m k : Nat} : n + m = k + m n = k := sorry
theorem Nat.add_left_cancel_iff {m k n : Nat} : n + m = n + k m = k := sorry
theorem Nat.add_right_cancel_iff {m k n : Nat} : m + n = k + n m = k := sorry
theorem Nat.add_left_inj {m k n : Nat} : m + n = k + n m = k := sorry
theorem Nat.add_right_inj {m k n : Nat} : n + m = n + k m = k := sorry
```
Note in particular that the convention is opposite for `cancel_iff` and `inj`.
```lean
theorem Nat.add_sub_self_left (a b : Nat) : (a + b) - a = b := sorry
theorem Nat.add_sub_self_right (a b : Nat) : (a + b) - b = a := sorry
theorem Nat.add_sub_cancel (n m : Nat) : (n + m) - m = n := sorry
```
## Primed names
Avoid disambiguating variants of a concept by appending the `'` character (e.g., introducing both `BitVec.sshiftRight` and `BitVec.sshiftRight'`), as it is impossible to tell the difference without looking at the type signature, the documentation or even the code, and even if you know what the two variants are there is no way to tell which is which. Prefer descriptive pairs `BitVec.sshiftRightNat`/`BitVec.sshiftRight`.
## Acronyms
For acronyms which are three letters or shorter, all letters should use the same case as dictated by the convention. For example, `IO` is a correct name for a type and the name `IO.Ref` may become `IORef` when used as part of a definition name and `ioRef` when used as part of a theorem name.
For acronyms which are at least four letters long, switch to lower case starting from the second letter. For example, `Json` is a correct name for a type, as is `JsonRPC`.
If an acronym is typically spelled using mixed case, this mixed spelling may be used in identifiers (for example `Std.Net.IPv4Addr`).
## Simp sets
Simp sets centered around a conversion function should be called `source_to_target`. For example, a simp set for the `BitVec.toNat` function, which goes from `BitVec` to
`Nat`, should be called `bitvec_to_nat`.
## Variable names
We make the following recommendations for variable names, but without insisting on them:
* Simple hypotheses should be named `h`, `h'`, or using a numerical sequence `h₁`, `h₂`, etc.
* Another common name for a simple hypothesis is `w` (for "witness").
* `List`s should be named `l`, `l'`, `l₁`, etc, or `as`, `bs`, etc.
(Use of `as`, `bs` is encouraged when the lists are of different types, e.g. `as : List α` and `bs : List β`.)
`xs`, `ys`, `zs` are allowed, but it is better if these are reserved for `Array` and `Vector`.
A list of lists may be named `L`.
* `Array`s should be named `xs`, `ys`, `zs`, although `as`, `bs` are encouraged when the arrays are of different types, e.g. `as : Array α` and `bs : Array β`.
An array of arrays may be named `xss`.
* `Vector`s should be named `xs`, `ys`, `zs`, although `as`, `bs` are encouraged when the vectors are of different types, e.g. `as : Vector α n` and `bs : Vector β n`.
A vector of vectors may be named `xss`.
* A common exception for `List` / `Array` / `Vector` is to use `acc` for an accumulator in a recursive function.
* `i`, `j`, `k` are preferred for numerical indices.
Descriptive names such as `start`, `stop`, `lo`, and `hi` are encouraged when they increase readability.
* `n`, `m` are preferred for sizes, e.g. in `Vector α n` or `xs.size = n`.
* `w` is preferred for the width of a `BitVec`.

View File

@@ -1,522 +0,0 @@
# Standard library style
Please take some time to familiarize yourself with the stylistic conventions of
the project and the specific part of the library you are planning to contribute
to. While the Lean compiler may not enforce strict formatting rules,
consistently formatted code is much easier for others to read and maintain.
Attention to formatting is more than a cosmetic concern—it reflects the same
level of precision and care required to meet the deeper standards of the Lean 4
standard library.
Below we will give specific formatting prescriptions for various language constructs. Note that this style guide only applies to the Lean standard library, even though some examples in the guide are taken from other parts of the Lean code base.
## Basic whitespace rules
Syntactic elements (like `:`, `:=`, `|`, `::`) are surrounded by single spaces, with the exception of `,` and `;`, which are followed by a space but not preceded by one. Delimiters (like `()`, `{}`) do not have spaces on the inside, with the exceptions of subtype notation and structure instance notation.
Examples of correctly formatted function parameters:
* `{α : Type u}`
* `[BEq α]`
* `(cmp : αα → Ordering)`
* `(hab : a = b)`
* `{d : { l : List ((n : Nat) × Vector Nat n) // l.length % 2 = 0 }}`
Examples of correctly formatted terms:
* `1 :: [2, 3]`
* `letI : Ord α := ⟨cmp⟩; True`
* `(⟨2, 3⟩ : Nat × Nat)`
* `((2, 3) : Nat × Nat)`
* `{ x with fst := f (4 + f 0), snd := 4, .. }`
* `match 1 with | 0 => 0 | _ => 0`
* `fun ⟨a, b⟩ _ _ => by cases hab <;> apply id; rw [hbc]`
Configure your editor to remove trailing whitespace. If you have set up Visual Studio Code for Lean development in the recommended way then the correct setting is applied automatically.
## Splitting terms across multiple lines
When splitting a term across multiple lines, increase indentation by two spaces starting from the second line. When splitting a function application, try to split at argument boundaries. If an argument itself needs to be split, increase indentation further as appropriate.
When splitting at an infix operator, the operator goes at the end of the first line, not at the beginning of the second line. When splitting at an infix operator, you may or may not increase indentation depth, depending on what is more readable.
When splitting an `if`-`then`-`else` expression, the `then` keyword wants to stay with the condition and the `else` keyword wants to stay with the alternative term. Otherwise, indent as if the `if` and `else` keywords were arguments to the same function.
When splitting a comma-separated bracketed sequence (i.e., anonymous constructor application, list/array/vector literal, tuple) it is allowed to indent subsequent lines for alignment, but indenting by two spaces is also allowed.
Do not orphan parentheses.
Correct:
```lean
def MacroScopesView.isPrefixOf (v₁ v₂ : MacroScopesView) : Bool :=
v₁.name.isPrefixOf v₂.name &&
v₁.scopes == v₂.scopes &&
v₁.mainModule == v₂.mainModule &&
v₁.imported == v₂.imported
```
Correct:
```lean
theorem eraseP_eq_iff {p} {l : List α} :
l.eraseP p = l'
(( a l, ¬ p a) l = l')
a l₁ l₂, ( b l₁, ¬ p b) p a
l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ :=
sorry
```
Correct:
```lean
example : Nat :=
functionWithAVeryLongNameSoThatSomeArgumentsWillNotFit firstArgument secondArgument
(firstArgumentWithAnEquallyLongNameAndThatFunctionDoesHaveMoreArguments firstArgument
secondArgument)
secondArgument
```
Correct:
```lean
theorem size_alter [LawfulBEq α] {k : α} {f : Option (β k) Option (β k)} (h : m.WF) :
(m.alter k f).size =
if m.contains k && (f (m.get? k)).isNone then
m.size - 1
else if !m.contains k && (f (m.get? k)).isSome then
m.size + 1
else
m.size := by
simp_to_raw using Raw₀.size_alter
```
Correct:
```lean
theorem get?_alter [LawfulBEq α] {k k' : α} {f : Option (β k) Option (β k)} (h : m.WF) :
(m.alter k f).get? k' =
if h : k == k' then
cast (congrArg (Option β) (eq_of_beq h)) (f (m.get? k))
else m.get? k' := by
simp_to_raw using Raw₀.get?_alter
```
Correct:
```lean
example : Nat × Nat :=
imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm
```
Correct:
```lean
example : Nat × Nat :=
imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm
```
Correct:
```lean
example : Vector Nat :=
#v[imagineThisWasALongTerm,
imagineThisWasAnotherLongTerm]
```
## Basic file structure
Every file should start with a copyright header, imports (in the standard library, this always includes a `prelude` declaration) and a module documentation string. There should not be a blank line between the copyright header and the imports. There should be a blank line between the imports and the module documentation string.
If you explicitly declare universe variables, do so at the top of the file, after the module documentation.
Correct:
```lean
/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro,
Yury Kudryashov
-/
prelude
import Init.Data.List.Pairwise
import Init.Data.List.Find
/-!
**# Lemmas about `List.eraseP` and `List.erase`.**
-/
universe u u'
```
Syntax that is not supposed to be user-facing must be scoped. New public syntax must always be discussed explicitly in an RFC.
## Top-level commands and declarations
All top-level commands are unindented. Sectioning commands like `section` and `namespace` do not increase the indentation level.
Attributes may be placed on the same line as the rest of the command or on a separate line.
Multi-line declaration headers are indented by four spaces starting from the second line. The colon that indicates the type of a declaration may not be placed at the start of a line or on its own line.
Declaration bodies are indented by two spaces. Short declaration bodies may be placed on the same line as the declaration type.
Correct:
```lean
theorem eraseP_eq_iff {p} {l : List α} :
l.eraseP p = l'
(( a l, ¬ p a) l = l')
a l₁ l₂, ( b l₁, ¬ p b) p a
l = l₁ ++ a :: l₂ l' = l₁ ++ l₂ :=
sorry
```
Correct:
```lean
@[simp] theorem eraseP_nil : [].eraseP p = [] := rfl
```
Correct:
```lean
@[simp]
theorem eraseP_nil : [].eraseP p = [] := rfl
```
### Documentation comments
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
Declarations should be documented as required by the `docBlame` linter, which may be activated in a file using
`set_option linter.missingDocs true` (we allow these to stay in the file).
Single-line documentation comments should go on the same line as `/--`/`-/`, while multi-line documentation strings
should have these delimiters on their own line, with the documentation comment itself unindented.
Documentation comments must be written in the indicative mood. Use American orthography.
Correct:
```lean
/-- Carries out a monadic action on each mapping in the hash map in some order. -/
@[inline] def forM (f : (a : α) β a m PUnit) (b : Raw α β) : m PUnit :=
b.buckets.forM (AssocList.forM f)
```
Correct:
```lean
/--
Monadically computes a value by folding the given function over the mappings in the hash
map in some order.
-/
@[inline] def foldM (f : δ (a : α) β a m δ) (init : δ) (b : Raw α β) : m δ :=
b.buckets.foldlM (fun acc l => l.foldlM f acc) init
```
### Where clauses
The `where` keyword should be unindented, and all declarations bound by it should be indented with two spaces.
Blank lines before and after `where` and between declarations bound by `where` are optional and should be chosen
to maximize readability.
Correct:
```lean
@[simp] theorem partition_eq_filter_filter (p : α Bool) (l : List α) :
partition p l = (filter p l, filter (not p) l) := by
simp [partition, aux]
where
aux (l) {as bs} : partition.loop p l (as, bs) =
(as.reverse ++ filter p l, bs.reverse ++ filter (not p) l) :=
match l with
| [] => by simp [partition.loop, filter]
| a :: l => by cases pa : p a <;> simp [partition.loop, pa, aux, filter, append_assoc]
```
### Termination arguments
The `termination_by`, `decreasing_by`, `partial_fixpoint` keywords should be unindented. The associated terms should be indented like declaration bodies.
Correct:
```lean
@[inline] def multiShortOption (handle : Char m PUnit) (opt : String) : m PUnit := do
let rec loop (p : String.Pos) := do
if h : opt.atEnd p then
return
else
handle (opt.get' p h)
loop (opt.next' p h)
termination_by opt.utf8ByteSize - p.byteIdx
decreasing_by
simp [String.atEnd] at h
apply Nat.sub_lt_sub_left h
simp [String.lt_next opt p]
loop 1
```
Correct:
```lean
def substrEq (s1 : String) (off1 : String.Pos) (s2 : String) (off2 : String.Pos) (sz : Nat) : Bool :=
off1.byteIdx + sz s1.endPos.byteIdx && off2.byteIdx + sz s2.endPos.byteIdx && loop off1 off2 { byteIdx := off1.byteIdx + sz }
where
loop (off1 off2 stop1 : Pos) :=
if _h : off1.byteIdx < stop1.byteIdx then
let c₁ := s1.get off1
let c₂ := s2.get off2
c₁ == c₂ && loop (off1 + c₁) (off2 + c₂) stop1
else true
termination_by stop1.1 - off1.1
decreasing_by
have := Nat.sub_lt_sub_left _h (Nat.add_lt_add_left c₁.utf8Size_pos off1.1)
decreasing_tactic
```
Correct:
```lean
theorem div_add_mod (m n : Nat) : n * (m / n) + m % n = m := by
rw [div_eq, mod_eq]
have h : Decidable (0 < n n m) := inferInstance
cases h with
| isFalse h => simp [h]
| isTrue h =>
simp [h]
have ih := div_add_mod (m - n) n
rw [Nat.left_distrib, Nat.mul_one, Nat.add_assoc, Nat.add_left_comm, ih, Nat.add_comm, Nat.sub_add_cancel h.2]
decreasing_by apply div_rec_lemma; assumption
```
### Deriving
The `deriving` clause should be unindented.
Correct:
```lean
structure Iterator where
array : ByteArray
idx : Nat
deriving Inhabited
```
## Notation and Unicode
We generally prefer to use notation as available. We usually prefer the Unicode versions of notations over non-Unicode alternatives.
There are some rules and exceptions regarding specific notations which are listed below:
* Sigma types: use `(a : α) × β a` instead of `Σ a, β a` or `Sigma β`.
* Function arrows: use `fun a => f x` instead of `fun x ↦ f x` or `λ x => f x` or any other variant.
## Language constructs
### Pattern matching, induction etc.
Match arms are indented at the indentation level that the match statement would have if it was on its own line. If the match is implicit, then the arms should be indented as if the match was explicitly given. The content of match arms is indented two spaces, so that it appears on the same level as the match pattern.
Correct:
```lean
def alter [BEq α] {β : Type v} (a : α) (f : Option β Option β) :
AssocList α (fun _ => β) AssocList α (fun _ => β)
| nil => match f none with
| none => nil
| some b => AssocList.cons a b nil
| cons k v l =>
if k == a then
match f v with
| none => l
| some b => cons a b l
else
cons k v (alter a f l)
```
Correct:
```lean
theorem eq_append_cons_of_mem {a : α} {xs : List α} (h : a xs) :
as bs, xs = as ++ a :: bs a as := by
induction xs with
| nil => cases h
| cons x xs ih =>
simp at h
cases h with
| inl h => exact [], xs, by simp_all
| inr h =>
by_cases h' : a = x
· subst h'
exact [], xs, by simp
· obtain as, bs, rfl, h := ih h
exact x :: as, bs, rfl, by simp_all
```
Aligning match arms is allowed, but not required.
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
Correct:
```lean
def mkEqTrans? (h₁? h₂? : Option Expr) : MetaM (Option Expr) :=
match h₁?, h₂? with
| none, none => return none
| none, some h => return h
| some h, none => return h
| some h₁, some h₂ => mkEqTrans h₁ h₂
```
### Structures
Note to external contributors: this is a section where the Lean style and the mathlib style are different.
When using structure instance syntax over multiple lines, the opening brace should go on the preceding line, while the closing brace should go on its own line. The rest of the syntax should be indented by one level. During structure updates, the `with` clause goes on the same line as the opening brace. Aligning at the assignment symbol is allowed but not required.
Correct:
```lean
def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind) (reportExts := true) :
IO AddConstAsyncResult := do
let sigPromise IO.Promise.new
let infoPromise IO.Promise.new
let extensionsPromise IO.Promise.new
let checkedEnvPromise IO.Promise.new
let asyncConst := {
constInfo := {
name := constName
kind
sig := sigPromise.result
constInfo := infoPromise.result
}
exts? := guard reportExts *> some extensionsPromise.result
}
return {
constName, kind
mainEnv := { env with
asyncConsts := env.asyncConsts.add asyncConst
checked := checkedEnvPromise.result }
asyncEnv := { env with
asyncCtx? := some { declPrefix := privateToUserName constName.eraseMacroScopes }
}
sigPromise, infoPromise, extensionsPromise, checkedEnvPromise
}
```
Correct:
```lean
instance [Inhabited α] : Inhabited (Descr α β σ) where
default := {
name := default
mkInitial := default
ofOLeanEntry := default
toOLeanEntry := default
addEntry := fun s _ => s
}
```
### Declaring structures
When defining structure types, do not parenthesize structure fields.
When declaring a structure type with a custom constructor name, put the custom name on its own line, indented like the
structure fields, and add a documentation comment.
Correct:
```lean
/--
A bitvector of the specified width.
This is represented as the underlying `Nat` number in both the runtime
and the kernel, inheriting all the special support for `Nat`.
-/
structure BitVec (w : Nat) where
/--
Constructs a `BitVec w` from a number less than `2^w`.
O(1), because we use `Fin` as the internal representation of a bitvector.
-/
ofFin ::
/--
Interprets a bitvector as a number less than `2^w`.
O(1), because we use `Fin` as the internal representation of a bitvector.
-/
toFin : Fin (2 ^ w)
```
## Tactic proofs
Tactic proofs are the most common thing to break during any kind of upgrade, so it is important to write them in a way that minimizes the likelihood of proofs breaking and that makes it easy to debug breakages if they do occur.
If there are multiple goals, either use a tactic combinator (like `all_goals`) to operate on all of them or a clearly specified subset, or use focus dots to work on goals one at a time. Using structured proofs (e.g., `induction … with`) is encouraged but not mandatory.
Squeeze non-terminal `simp`s (i.e., calls to `simp` which do not close the goal). Squeezing terminal `simp`s is generally discouraged, although there are exceptions (for example if squeezing yields a noticeable performance improvement).
Do not over-golf proofs in ways that are likely to lead to hard-to-debug breakage. Examples of things to avoid include complex multi-goal manipulation using lots of tactic combinators, complex uses of the substitution operator (`▸`) and clever point-free expressions (possibly involving anonymous function notation for multiple arguments).
Do not under-golf proofs: for routine tasks, use the most powerful tactics available.
Do not use `erw`. Avoid using `rfl` after `simp` or `rw`, as this usually indicates a missing lemma that should be used instead of `rfl`.
Use `(d)simp` or `rw` instead of `delta` or `unfold`. Use `refine` instead of `refine`. Use `haveI` and `letI` only if they are actually required.
Prefer highly automated tactics (like `grind` and `omega`) over low-level proofs, unless the automated tactic requires unacceptable additional imports or has bad performance. If you decide against using a highly automated tactic, leave a comment explaining the decision.
## `do` notation
The `do` keyword goes on the same line as the corresponding `:=` (or `=>`, or similar). `Id.run do` should be treated as if it was a bare `do`.
Use early `return` statements to reduce nesting depth and make the non-exceptional control flow of a function easier to see.
Alternatives for `let` matches may be placed in the same line or in the next line, indented by two spaces. If the term that is
being matched on is itself more than one line and there is an alternative present, consider breaking immediately after `←` and indent
as far as necessary to ensure readability.
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl findFunDecl? fvarId | throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl
findFunDecl? fvarId
| throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
let some decl findFunDecl?
fvarId
| throwError "unknown local function {fvarId.name}"
return decl
```
Correct:
```lean
def tagUntaggedGoals (parentTag : Name) (newSuffix : Name) (newGoals : List MVarId) : TacticM Unit := do
let mctx getMCtx
let mut numAnonymous := 0
for g in newGoals do
if mctx.isAnonymousMVar g then
numAnonymous := numAnonymous + 1
modifyMCtx fun mctx => Id.run do
let mut mctx := mctx
let mut idx := 1
for g in newGoals do
if mctx.isAnonymousMVar g then
if numAnonymous == 1 then
mctx := mctx.setMVarUserName g parentTag
else
mctx := mctx.setMVarUserName g (parentTag ++ newSuffix.appendIndexAfter idx)
idx := idx + 1
pure mctx
```

View File

@@ -1,98 +0,0 @@
# The Lean 4 standard library
Maintainer team (in alphabetical order): Henrik Böving, Markus Himmel
(community contact & external contribution coordinator), Kim Morrison, Paul
Reichert, Sofia Rodrigues.
The Lean 4 standard library is a core part of the Lean distribution, providing
essential building blocks for functional programming, verified software
development, and software verification. Unlike the standard libraries of most
other languages, many of its components are formally verified and can be used
as part of verified applications.
The standard library is a public API that contains the components listed in the
standard library outline below. Not all public APIs in the Lean distribution
are part of the standard library, and the standard library does not correspond
to a certain directory within the Lean source repository (like `Std`). For
example, the metaprogramming framework is not part of the standard library, but
basic types like `True` and `Nat` are.
The standard library is under active development. Our guiding principles are:
* Provide comprehensive, verified building blocks for real-world software.
* Build a public API of the highest quality with excellent internal consistency.
* Carefully optimize components that may be used in performance-critical software.
* Ensure smooth adoption and maintenance for users.
* Offer excellent documentation, example projects, and guides.
* Provide a reliable and extensible basis that libraries for software
development, software verification and mathematics can build on.
The standard library is principally developed by the Lean FRO. Community
contributions are welcome. If you would like to contribute, please refer to the
call for contributions below.
### Standard library outline
1. Core types and operations
1. Basic types
2. Numeric types, including floating point numbers
3. Containers
4. Strings and formatting
2. Language constructs
1. Ranges and iterators
2. Comparison, ordering, hashing and related type classes
3. Basic monad infrastructure
3. Libraries
1. Random numbers
2. Dates and times
4. Operating system abstractions
1. Concurrency and parallelism primitives
2. Asynchronous I/O
3. FFI helpers
4. Environment, file system, processes
5. Locales
The material covered in the first three sections (core types and operations,
language constructs and libraries) will be verified, with the exception of
floating point numbers and the parts of the libraries that interface with the
operating system (e.g., sources of operating system randomness or time zone
database access).
### Call for contributions
Thank you for taking interest in contributing to the Lean standard library\!
There are two main ways for community members to contribute to the Lean
standard library: by contributing experience reports or by contributing code
and lemmas.
**If you are using Lean for software verification or verified software
development:** hearing about your experiences using Lean and its standard
library for software verification is extremely valuable to us. We are committed
to building a standard library suitable for real-world applications and your
input will directly influence the continued evolution of the Lean standard
library. Please reach out to the standard library maintainer team via Zulip
(either in a public thread in the \#lean4 channel or via direct message). Even
just a link to your code helps. Thanks\!
**If you have code that you believe could enhance the Lean 4 standard
library:** we encourage you to initiate a discussion in the \#lean4 channel on
Zulip. This is the most effective way to receive preliminary feedback on your
contribution. The Lean standard library has a very precise scope and it has
very high quality standards, so at the moment we are mostly interested in
contributions that expand upon existing material rather than introducing novel
concepts.
**If you would like to contribute code to the standard library but dont know
what to work on:** we are always excited to meet motivated community members
who would like to contribute, and there is always impactful work that is
suitable for new contributors. Please reach out to Markus Himmel on Zulip to
discuss possible contributions.
As laid out in the [project-wide External Contribution
Guidelines](../../CONTRIBUTING.md),
PRs are much more likely to be merged if they are preceded by an RFC or if you
discussed your planned contribution with a member of the standard library
maintainer team. When in doubt, introducing yourself is always a good idea.
All code in the standard library is expected to strictly adhere to the
[standard library coding conventions](./style.md).

1146
doc/style.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,80 +0,0 @@
# Balanced Parentheses as an Embedded Domain Specific Language
Let's look at how to use macros to extend the Lean 4 parser and embed a language for building _balanced parentheses_.
This language accepts strings given by the [BNF grammar](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form)
```
Dyck ::=
"(" Dyck ")"
| "{" Dyck "}"
| end
```
We begin by defining an inductive data type of the grammar we wish to parse:
```lean,ignore
inductive Dyck : Type where
| round : Dyck → Dyck -- ( <inner> )
| curly : Dyck → Dyck -- { <inner> }
| leaf : Dyck
```
We begin by declaring a _syntax category_ using the `declare_syntax_cat <category>` command.
This names our grammar and allows us to specify parsing rules associated with our grammar.
```lean,ignore
declare_syntax_cat brack
```
Next, we specify the grammar using the `syntax <parse rule>` command:
```lean,ignore
syntax "end" : brack
```
The above means that the token "end" lives in syntax category `brack`.
Similarly, we declare the rules `"(" Dyck ")"` and `"{" Dyck "}"` using the rules:
```lean,ignore
syntax "(" brack ")" : brack
syntax "{" brack "}" : brack
```
Finally, we need a way to build _Lean 4 terms_ from this grammar -- that is, we must translate out of this
grammar into a `Dyck` value, which is a Lean 4 term. For this, we create a new kind of "quotation" that
consumes syntax in `brack` and produces a `term`.
```lean,ignore
syntax "`[Dyck| " brack "]" : term
```
To specify the transformation rules, we use `macro_rules` to declare how the syntax `` `[Dyck| <brack>] ``
produces terms. This is written using a pattern-matching style syntax, where the left-hand side
declares the syntax pattern to be matched, and the right-hand side declares the production. Syntax placeholders (antiquotations)
are introduced via the `$<var-name>` syntax. The right-hand side is
an arbitrary Lean term that we are producing.
```lean,ignore
macro_rules
| `(`[Dyck| end]) => `(Dyck.leaf)
| `(`[Dyck| ($b)]) => `(Dyck.round `[Dyck| $b]) -- recurse
| `(`[Dyck| {$b}]) => `(Dyck.curly `[Dyck| $b]) -- recurse
```
```lean,ignore
#check `[Dyck| end] -- Dyck.leaf
#check `[Dyck| {(end)}] -- Dyck.curl (Dyck.round Dyck.leaf)
```
In summary, we've seen:
- How to declare a syntax category for the Dyck grammar.
- How to specify parse trees of this grammar using `syntax`
- How to translate out of this grammar into Lean 4 terms using `macro_rules`.
The full program listing is given below:
```lean
{{#include syntax_example.lean}}
```

View File

@@ -1,4 +0,0 @@
# Syntax Metaprogramming Examples
- [Balanced Parentheses](./syntax_example.md)
- [Arithmetic DSL](./metaprogramming-arith.md)

View File

@@ -1,91 +0,0 @@
You can copy highlighted code [straight from VS Code](https://code.visualstudio.com/updates/v1_10#_copy-with-syntax-highlighting) to any rich text editor supporting HTML input. For highlighting code in LaTeX, there are two options:
* [listings](https://ctan.org/pkg/listings), which is a common package and simple to set up, but you may run into some restrictions of it and LaTeX around Unicode
* [`minted`](https://ctan.org/pkg/minted), a LaTeX package wrapping the [Pygments](https://pygments.org/) syntax highlighting library. It needs a few more steps to set up, but provides unrestricted support for Unicode when combined with XeLaTeX or LuaLaTex.
## Example with `listings`
Save [`lstlean.tex`](https://raw.githubusercontent.com/leanprover/lean4/master/doc/latex/lstlean.tex) into the same directory, or anywhere in your `TEXINPUTS` path, as the following test file:
```latex
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{listings}
\usepackage{amssymb}
\usepackage{color}
\definecolor{keywordcolor}{rgb}{0.7, 0.1, 0.1} % red
\definecolor{tacticcolor}{rgb}{0.0, 0.1, 0.6} % blue
\definecolor{commentcolor}{rgb}{0.4, 0.4, 0.4} % grey
\definecolor{symbolcolor}{rgb}{0.0, 0.1, 0.6} % blue
\definecolor{sortcolor}{rgb}{0.1, 0.5, 0.1} % green
\definecolor{attributecolor}{rgb}{0.7, 0.1, 0.1} % red
\def\lstlanguagefiles{lstlean.tex}
% set default language
\lstset{language=lean}
\begin{document}
\begin{lstlisting}
theorem funext {f₁ f₂ : ∀ (x : α), β x} (h : ∀ x, f₁ x = f₂ x) : f₁ = f₂ := by
show extfunApp (Quotient.mk f₁) = extfunApp (Quotient.mk f₂)
apply congrArg
apply Quotient.sound
exact h
\end{lstlisting}
\end{document}
```
Compile the file via
```bash
$ pdflatex test.tex
```
* for older LaTeX versions, you might need to use `[utf8x]` instead of `[utf8]` with `inputenc`
## Example with `minted`
First [install Pygments](https://pygments.org/download/) (version 2.18 or newer).
Then save the following sample LaTeX file `test.tex` into the same directory:
```latex
\documentclass{article}
\usepackage{fontspec}
% switch to a monospace font supporting more Unicode characters
\setmonofont{FreeMono}
\usepackage{minted}
\newmintinline[lean]{lean4}{bgcolor=white}
\newminted[leancode]{lean4}{fontsize=\footnotesize}
\usemintedstyle{tango} % a nice, colorful theme
\begin{document}
\begin{leancode}
theorem funext {f₁ f₂ : ∀ (x : α), β x} (h : ∀ x, f₁ x = f₂ x) : f₁ = f₂ := by
show extfunApp (Quotient.mk' f₁) = extfunApp (Quotient.mk' f₂)
apply congrArg
apply Quotient.sound
exact h
\end{leancode}
\end{document}
```
You can then compile `test.tex` by executing the following command:
```bash
xelatex --shell-escape test.tex
```
Some remarks:
- either `xelatex` or `lualatex` is required to handle Unicode characters in the code.
- `--shell-escape` is needed to allow `xelatex` to execute `pygmentize` in a shell.
- If the chosen monospace font is missing some Unicode symbols, you can direct them to be displayed using a fallback font or other replacement LaTeX code.
``` latex
\usepackage{newunicodechar}
\newfontfamily{\freeserif}{DejaVu Sans}
\newunicodechar{✝}{\freeserif{✝}}
\newunicodechar{𝓞}{\ensuremath{\mathcal{O}}}
```
- If you are using an old version of Pygments, you can copy
[`lean.py`](https://raw.githubusercontent.com/pygments/pygments/master/pygments/lexers/lean.py) into your working directory,
and use `lean4.py:Lean4Lexer -x` instead of `lean4` above.
If your version of `minted` is v2.7 or newer, but before v3.0,
you will additionally need to follow the workaround described in https://github.com/gpoore/minted/issues/360.

View File

@@ -1,393 +0,0 @@
# Tactics
Tactics are metaprograms, that is, programs that create programs.
Lean is implemented in Lean, you can import its implementation using `import Lean`.
The `Lean` package is part of the Lean distribution.
You can use the functions in the `Lean` package to write your own metaprograms
that automate repetitive tasks when writing programs and proofs.
We provide the **tactic** domain specific language (DSL) for using the tactic framework.
The tactic DSL provides commands for creating terms (and proofs). You
don't need to import the `Lean` package for using the tactic DSL.
Simple extensions can be implemented using macros. More complex extensions require
the `Lean` package. Notation used to write Lean terms can be easily lifted to the tactic DSL.
Tactics are instructions that tell Lean how to construct a term or proof.
Tactics operate on holes also known as goals. Each hole represents a missing
part of the term you are trying to build. Internally these holes are represented
as metavariables. They have a type and a local context. The local context contains
all local variables in scope.
In the following example, we prove the same simple theorem using different tactics.
The keyword `by` instructs Lean to use the tactic DSL to construct a term.
Our initial goal is a hole with type `p q → q p`. The tactic `intro h`
fills this hole using the term `fun h => ?m` where `?m` is a new hole we need to solve.
This hole has type `q p`, and the local context contains `h : p q`.
The tactic `cases` fills the hole using `Or.casesOn h (fun h1 => ?m1) (fun h2 => ?m2)`
where `?m1` and `?m2` are new holes. The tactic `apply Or.inr` fills the hole `?m1`
with the application `Or.inr ?m3`, and `exact h1` fills `?m3` with `h1`.
The tactic `assumption` tries to fill a hole by searching the local context for a term with the same type.
```lean
theorem ex1 : p q q p := by
intro h
cases h with
| inl h1 =>
apply Or.inr
exact h1
| inr h2 =>
apply Or.inl
assumption
#print ex1
/-
theorem ex1 : {p q : Prop} → p q → q p :=
fun {p q : Prop} (h : p q) =>
Or.casesOn h (fun (h1 : p) => Or.inr h1) fun (h2 : q) => Or.inl h2
-/
-- You can use `match-with` in tactics.
theorem ex2 : p q q p := by
intro h
match h with
| Or.inl _ => apply Or.inr; assumption
| Or.inr h2 => apply Or.inl; exact h2
-- As we have the `fun+match` syntax sugar for terms,
-- we have the `intro+match` syntax sugar
theorem ex3 : p q q p := by
intro
| Or.inl h1 =>
apply Or.inr
exact h1
| Or.inr h2 =>
apply Or.inl
assumption
```
The examples above are all structured, but Lean 4 still supports unstructured
proofs. Unstructured proofs are useful when creating reusable scripts that may
discharge different goals.
Here is an unstructured version of the example above.
```lean
theorem ex1 : p q q p := by
intro h
cases h
apply Or.inr
assumption
apply Or.inl
assumption
done -- fails with an error here if there are unsolvable goals
theorem ex2 : p q q p := by
intro h
cases h
focus -- instructs Lean to `focus` on the first goal,
apply Or.inr
assumption
-- it will fail if there are still unsolvable goals here
focus
apply Or.inl
assumption
theorem ex3 : p q q p := by
intro h
cases h
-- You can still use curly braces and semicolons instead of
-- whitespace sensitive notation as in the previous example
{ apply Or.inr;
assumption
-- It will fail if there are unsolved goals
}
{ apply Or.inl;
assumption
}
-- Many tactics tag subgoals. The tactic `cases` tag goals using constructor names.
-- The tactic `case tag => tactics` instructs Lean to solve the goal
-- with the matching tag.
theorem ex4 : p q q p := by
intro h
cases h
case inr =>
apply Or.inl
assumption
case inl =>
apply Or.inr
assumption
-- Same example for curly braces and semicolons aficionados
theorem ex5 : p q q p := by {
intro h;
cases h;
case inr => {
apply Or.inl;
assumption
}
case inl => {
apply Or.inr;
assumption
}
}
```
## Rewrite
TODO
## Pattern matching
As a convenience, pattern-matching has been integrated into tactics such as `intro` and `funext`.
```lean
theorem ex1 : s q r p r q p := by
intro _, hq, _ hp, _
exact hq, hp
theorem ex2 :
(fun (x : Nat × Nat) (y : Nat × Nat) => x.1 + y.2)
=
(fun (x : Nat × Nat) (z : Nat × Nat) => z.2 + x.1) := by
funext (a, b) (c, d)
show a + d = d + a
rw [Nat.add_comm]
```
## Induction
The `induction` tactic now supports user-defined induction principles with
multiple targets (aka major premises).
```lean
/-
theorem Nat.mod.inductionOn
{motive : Nat → Nat → Sort u}
(x y : Nat)
(ind : ∀ x y, 0 < y ∧ y ≤ x → motive (x - y) y → motive x y)
(base : ∀ x y, ¬(0 < y ∧ y ≤ x) → motive x y)
: motive x y :=
-/
theorem ex (x : Nat) {y : Nat} (h : y > 0) : x % y < y := by
induction x, y using Nat.mod.inductionOn with
| ind x y h₁ ih =>
rw [Nat.mod_eq_sub_mod h₁.2]
exact ih h
| base x y h₁ =>
have : ¬ 0 < y ¬ y x := Iff.mp (Decidable.not_and_iff_or_not ..) h₁
match this with
| Or.inl h₁ => exact absurd h h₁
| Or.inr h₁ =>
have hgt : y > x := Nat.gt_of_not_le h₁
rw [ Nat.mod_eq_of_lt hgt] at hgt
assumption
```
## Cases
TODO
## Injection
TODO
## Split
The `split` tactic can be used to split the cases of an if-then-else or
match into new subgoals, which can then be discharged individually.
```lean
def addMoreIfOdd (n : Nat) := if n % 2 = 0 then n + 1 else n + 2
/- Examine each branch of the conditional to show that the result
is always positive -/
example (n : Nat) : 0 < addMoreIfOdd n := by
simp only [addMoreIfOdd]
split
next => exact Nat.zero_lt_succ _
next => exact Nat.zero_lt_succ _
```
```lean
def binToChar (n : Nat) : Option Char :=
match n with
| 0 => some '0'
| 1 => some '1'
| _ => none
example (n : Nat) : (binToChar n).isSome -> n = 0 n = 1 := by
simp only [binToChar]
split
next => exact fun _ => Or.inl rfl
next => exact fun _ => Or.inr rfl
next => intro h; cases h
/- Hypotheses about previous cases can be accessed by assigning them a
name, like `ne_zero` below. Information about the matched term can also
be preserved using the `generalizing` tactic: -/
example (n : Nat) : (n = 0) -> (binToChar n = some '0') := by
simp only [binToChar]
split
case h_1 => intro _; rfl
case h_2 => intro h; cases h
/- Here, we can introduce `n ≠ 0` and `n ≠ 1` this case assumes
neither of the previous cases matched. -/
case h_3 ne_zero _ => intro eq_zero; exact absurd eq_zero ne_zero
```
## Dependent pattern matching
The `match-with` expression implements dependent pattern matching. You can use it to create concise proofs.
```lean
inductive Mem : α List α Prop where
| head (a : α) (as : List α) : Mem a (a::as)
| tail (a b : α) (bs : List α) : Mem a bs Mem a (b::bs)
infix:50 (priority := high) "" => Mem
theorem mem_split {a : α} {as : List α} (h : a as) : s t, as = s ++ a :: t :=
match a, as, h with
| _, _, Mem.head a bs => [], bs, rfl
| _, _, Mem.tail a b bs h =>
match bs, mem_split h with
| _, s, t, rfl => b::s, t, List.cons_append .. rfl
```
In the tactic DSL, the right-hand-side of each alternative in a `match-with` is a sequence of tactics instead of a term.
Here is a similar proof using the tactic DSL.
```lean
# inductive Mem : α List α Prop where
# | head (a : α) (as : List α) : Mem a (a::as)
# | tail (a b : α) (bs : List α) : Mem a bs Mem a (b::bs)
# infix:50 (priority := high) "" => Mem
theorem mem_split {a : α} {as : List α} (h : a as) : s t, as = s ++ a :: t := by
match a, as, h with
| _, _, Mem.head a bs => exists []; exists bs; rfl
| _, _, Mem.tail a b bs h =>
match bs, mem_split h with
| _, s, t, rfl =>
exists b::s; exists t;
rw [List.cons_append]
```
We can use `match-with` nested in tactics.
Here is a similar proof that uses the `induction` tactic instead of recursion.
```lean
# inductive Mem : α List α Prop where
# | head (a : α) (as : List α) : Mem a (a::as)
# | tail (a b : α) (bs : List α) : Mem a bs Mem a (b::bs)
# infix:50 (priority := high) "" => Mem
theorem mem_split {a : α} {as : List α} (h : a as) : s t, as = s ++ a :: t := by
induction as with
| nil => cases h
| cons b bs ih => cases h with
| head a bs => exact [], bs, rfl
| tail a b bs h =>
match bs, ih h with
| _, s, t, rfl =>
exists b::s; exists t
rw [List.cons_append]
```
You can create your own notation using existing tactics. In the following example,
we define a simple `obtain` tactic using macros. We say it is simple because it takes only one
discriminant. Later, we show how to create more complex automation using macros.
```lean
# inductive Mem : α List α Prop where
# | head (a : α) (as : List α) : Mem a (a::as)
# | tail (a b : α) (bs : List α) : Mem a bs Mem a (b::bs)
# infix:50 (priority := high) "" => Mem
macro "obtain " p:term " from " d:term : tactic =>
`(tactic| match $d:term with | $p:term => ?_)
theorem mem_split {a : α} {as : List α} (h : a as) : s t, as = s ++ a :: t := by
induction as with
| cons b bs ih => cases h with
| tail a b bs h =>
obtain s, t, h from ih h
exists b::s; exists t
rw [h, List.cons_append]
| head a bs => exact [], bs, rfl
| nil => cases h
```
## Extensible tactics
In the following example, we define the notation `triv` for the tactic DSL using
the command `syntax`. Then, we use the command `macro_rules` to specify what should
be done when `triv` is used. You can provide different expansions, and the tactic DSL
interpreter will try all of them until one succeeds.
```lean
-- Define a new notation for the tactic DSL
syntax "triv" : tactic
macro_rules
| `(tactic| triv) => `(tactic| assumption)
theorem ex1 (h : p) : p := by
triv
-- You cannot prove the following theorem using `triv`
-- theorem ex2 (x : α) : x = x := by
-- triv
-- Let's extend `triv`. The `by` DSL interpreter
-- tries all possible macro extensions for `triv` until one succeeds
macro_rules
| `(tactic| triv) => `(tactic| rfl)
theorem ex2 (x : α) : x = x := by
triv
theorem ex3 (x : α) (h : p) : x = x p := by
apply And.intro <;> triv
```
# `let-rec`
You can use `let rec` to write local recursive functions. We lifted it to the tactic DSL,
and you can use it to create proofs by induction.
```lean
theorem length_replicateTR {α} (n : Nat) (a : α) : (List.replicateTR n a).length = n := by
let rec aux (n : Nat) (as : List α)
: (List.replicateTR.loop a n as).length = n + as.length := by
match n with
| 0 => rw [Nat.zero_add]; rfl
| n+1 =>
show List.length (List.replicateTR.loop a n (a::as)) = Nat.succ n + as.length
rw [aux n, List.length_cons, Nat.add_succ, Nat.succ_add]
exact aux n []
```
You can also introduce auxiliary recursive declarations using `where` clause after your definition.
Lean converts them into a `let rec`.
```lean
theorem length_replicateTR {α} (n : Nat) (a : α) : (List.replicateTR n a).length = n :=
loop n []
where
loop n as : (List.replicateTR.loop a n as).length = n + as.length := by
match n with
| 0 => rw [Nat.zero_add]; rfl
| n+1 =>
show List.length (List.replicateTR.loop a n (a::as)) = Nat.succ n + as.length
rw [loop n, List.length_cons, Nat.add_succ, Nat.succ_add]
```
# `begin-end` lovers
If you love Lean 3 `begin ... end` tactic blocks and commas, you can define this notation
in Lean 4 using macros in a few lines of code.
```lean
{{#include ../tests/lean/beginEndAsMacro.lean:doc}}
```

View File

@@ -1,200 +0,0 @@
# Tour of Lean
The best way to learn about Lean is to read and write Lean code.
This article will act as a tour through some of the key features of the Lean
language and give you some code snippets that you can execute on your machine.
To learn about setting up a development environment, check out [Setting Up Lean](./setup.md).
There are two primary concepts in Lean: functions and types.
This tour will emphasize features of the language which fall into
these two concepts.
# Functions and Namespaces
The most fundamental pieces of any Lean program are functions organized into namespaces.
[Functions](./functions.md) perform work on inputs to produce outputs,
and they are organized under [namespaces](./namespaces.md),
which are the primary way you group things in Lean.
They are defined using the `def` command,
which give the function a name and define its arguments.
```lean
namespace BasicFunctions
-- The `#eval` command evaluates an expression on the fly and prints the result.
#eval 2+2
-- You use 'def' to define a function. This one accepts a natural number
-- and returns a natural number.
-- Parentheses are optional for function arguments, except for when
-- you use an explicit type annotation.
-- Lean can often infer the type of the function's arguments.
def sampleFunction1 x := x*x + 3
-- Apply the function, naming the function return result using 'def'.
-- The variable type is inferred from the function return type.
def result1 := sampleFunction1 4573
-- This line uses an interpolated string to print the result. Expressions inside
-- braces `{}` are converted into strings using the polymorphic method `toString`
#eval println! "The result of squaring the integer 4573 and adding 3 is {result1}"
-- When needed, annotate the type of a parameter name using '(argument : type)'.
def sampleFunction2 (x : Nat) := 2*x*x - x + 3
def result2 := sampleFunction2 (7 + 4)
#eval println! "The result of applying the 2nd sample function to (7 + 4) is {result2}"
-- Conditionals use if/then/else
def sampleFunction3 (x : Int) :=
if x > 100 then
2*x*x - x + 3
else
2*x*x + x - 37
#eval println! "The result of applying sampleFunction3 to 2 is {sampleFunction3 2}"
end BasicFunctions
```
```lean
-- Lean has first-class functions.
-- `twice` takes two arguments `f` and `a` where
-- `f` is a function from natural numbers to natural numbers, and
-- `a` is a natural number.
def twice (f : Nat Nat) (a : Nat) :=
f (f a)
-- `fun` is used to declare anonymous functions
#eval twice (fun x => x + 2) 10
-- You can prove theorems about your functions.
-- The following theorem states that for any natural number `a`,
-- adding 2 twice produces a value equal to `a + 4`.
theorem twiceAdd2 (a : Nat) : twice (fun x => x + 2) a = a + 4 :=
-- The proof is by reflexivity. Lean "symbolically" reduces both sides of the equality
-- until they are identical.
rfl
-- `(· + 2)` is syntax sugar for `(fun x => x + 2)`. The parentheses + `·` notation
-- is useful for defining simple anonymous functions.
#eval twice (· + 2) 10
-- Enumerated types are a special case of inductive types in Lean,
-- which we will learn about later.
-- The following command creates a new type `Weekday`.
inductive Weekday where
| sunday : Weekday
| monday : Weekday
| tuesday : Weekday
| wednesday : Weekday
| thursday : Weekday
| friday : Weekday
| saturday : Weekday
-- `Weekday` has 7 constructors/elements.
-- The constructors live in the `Weekday` namespace.
-- Think of `sunday`, `monday`, …, `saturday` as being distinct elements of `Weekday`,
-- with no other distinguishing properties.
-- The command `#check` prints the type of a term in Lean.
#check Weekday.sunday
#check Weekday.monday
-- The `open` command opens a namespace, making all declarations in it accessible without
-- qualification.
open Weekday
#check sunday
#check tuesday
-- You can define functions by pattern matching.
-- The following function converts a `Weekday` into a natural number.
def natOfWeekday (d : Weekday) : Nat :=
match d with
| sunday => 1
| monday => 2
| tuesday => 3
| wednesday => 4
| thursday => 5
| friday => 6
| saturday => 7
#eval natOfWeekday tuesday
def isMonday : Weekday Bool :=
-- `fun` + `match` is a common idiom.
-- The following expression is syntax sugar for
-- `fun d => match d with | monday => true | _ => false`.
fun
| monday => true
| _ => false
#eval isMonday monday
#eval isMonday sunday
-- Lean has support for type classes and polymorphic methods.
-- The `toString` method converts a value into a `String`.
#eval toString 10
#eval toString (10, 20)
-- The method `toString` converts values of any type that implements
-- the class `ToString`.
-- You can implement instances of `ToString` for your own types.
instance : ToString Weekday where
toString (d : Weekday) : String :=
match d with
| sunday => "Sunday"
| monday => "Monday"
| tuesday => "Tuesday"
| wednesday => "Wednesday"
| thursday => "Thursday"
| friday => "Friday"
| saturday => "Saturday"
#eval toString (sunday, 10)
def Weekday.next (d : Weekday) : Weekday :=
match d with
| sunday => monday
| monday => tuesday
| tuesday => wednesday
| wednesday => thursday
| thursday => friday
| friday => saturday
| saturday => sunday
#eval Weekday.next Weekday.wednesday
-- Since the `Weekday` namespace has already been opened, you can also write
#eval next wednesday
-- Matching on a parameter like in the previous definition
-- is so common that Lean provides syntax sugar for it. The following
-- function uses it.
def Weekday.previous : Weekday -> Weekday
| sunday => saturday
| monday => sunday
| tuesday => monday
| wednesday => tuesday
| thursday => wednesday
| friday => thursday
| saturday => friday
#eval next (previous wednesday)
-- We can prove that for any `Weekday` `d`, `next (previous d) = d`
theorem Weekday.nextOfPrevious (d : Weekday) : next (previous d) = d :=
match d with
| sunday => rfl
| monday => rfl
| tuesday => rfl
| wednesday => rfl
| thursday => rfl
| friday => rfl
| saturday => rfl
-- You can automate definitions such as `Weekday.nextOfPrevious`
-- using metaprogramming (or "tactics").
theorem Weekday.nextOfPrevious' (d : Weekday) : next (previous d) = d := by
cases d -- A proof by case distinction
all_goals rfl -- Each case is solved using `rfl`
```

View File

@@ -1,5 +0,0 @@
Theorem Proving in Lean
=======================
We strongly encourage you to read the book [Theorem Proving in Lean](https://lean-lang.org/theorem_proving_in_lean4/title_page.html).
Many Lean users consider it to be the Lean Bible.

View File

@@ -1,129 +0,0 @@
## Types as objects
One way in which Lean's dependent type theory extends simple type theory is that types themselves --- entities like ``Nat`` and ``Bool`` ---
are first-class citizens, which is to say that they themselves are objects. For that to be the case, each of them also has to have a type.
```lean
#check Nat -- Type
#check Bool -- Type
#check Nat → Bool -- Type
#check Nat × Bool -- Type
#check Nat → Nat -- ...
#check Nat × Nat → Nat
#check Nat → Nat → Nat
#check Nat → (Nat → Nat)
#check Nat → Nat → Bool
#check (Nat → Nat) → Nat
```
We see that each one of the expressions above is an object of type ``Type``. We can also declare new constants and constructors for types:
```lean
constant α : Type
constant β : Type
constant F : Type → Type
constant G : Type → Type → Type
#check α -- Type
#check F α -- Type
#check F Nat -- Type
#check G α -- Type → Type
#check G α β -- Type
#check G α Nat -- Type
```
Indeed, we have already seen an example of a function of type ``Type → Type → Type``, namely, the Cartesian product.
```lean
constant α : Type
constant β : Type
#check Prod α β -- Type
#check Prod Nat Nat -- Type
```
Here is another example: given any type ``α``, the type ``List α`` denotes the type of lists of elements of type ``α``.
```lean
constant α : Type
#check List α -- Type
#check List Nat -- Type
```
Given that every expression in Lean has a type, it is natural to ask: what type does ``Type`` itself have?
```lean
#check Type -- Type 1
```
We have actually come up against one of the most subtle aspects of Lean's typing system.
Lean's underlying foundation has an infinite hierarchy of types:
```lean
#check Type -- Type 1
#check Type 1 -- Type 2
#check Type 2 -- Type 3
#check Type 3 -- Type 4
#check Type 4 -- Type 5
```
Think of ``Type 0`` as a universe of "small" or "ordinary" types.
``Type 1`` is then a larger universe of types, which contains ``Type 0`` as an element,
and ``Type 2`` is an even larger universe of types, which contains ``Type 1`` as an element.
The list is indefinite, so that there is a ``Type n`` for every natural number ``n``.
``Type`` is an abbreviation for ``Type 0``:
```lean
#check Type
#check Type 0
```
There is also another type universe, ``Prop``, which has special properties.
```lean
#check Prop -- Type
```
We will discuss ``Prop`` later.
We want some operations, however, to be *polymorphic* over type universes. For example, ``List α`` should
make sense for any type ``α``, no matter which type universe ``α`` lives in. This explains the type annotation of the function ``List``:
```lean
#check List -- Type u_1 → Type u_1
```
Here ``u_1`` is a variable ranging over type levels. The output of the ``#check`` command means that whenever ``α`` has type ``Type n``, ``List α`` also has type ``Type n``. The function ``Prod`` is similarly polymorphic:
```lean
#check Prod -- Type u_1 → Type u_2 → Type (max u_1 u_2)
```
To define polymorphic constants and variables, Lean allows us to declare universe variables explicitly using the `universe` command:
```lean
universe u
constant α : Type u
#check α
```
Equivalently, we can write ``Type _`` to avoid giving the arbitrary universe a name:
```lean
constant α : Type _
#check α
```
Several Lean 3 users use the shorthand `Type*` for `Type _`. The `Type*` notation is not builtin in Lean 4, but you can easily define it using a `macro`.
```lean
macro "Type*" : term => `(Type _)
def f (α : Type*) (a : α) := a
def g (α : Type _) (a : α) := a
#check f
#check g
```
We explain later how the `macro` command works.

View File

@@ -1,56 +0,0 @@
# Types
Every programming language needs a type system and
Lean has a rich extensible inductive type system.
## Basic Types
Lean has built in support for the following basic types:
- [Bool](bool.md) : a `true` or `false` value.
- [Int](integers.md) : multiple precision integers (with no overflows!).
- [Nat](integers.md) : natural numbers, or non-negative integers (also with no overflows!).
- [Float](float.md): floating point numbers.
- [Char](char.md): a Unicode character.
- [String](string.md): a UTF-8 encoded string of characters.
- [Array](array.md): a dynamic (aka growable) array of typed objects.
- [List](list.md): a linked list of typed objects.
- TODO: what else?
And Lean allows you to create your own custom types using:
- [Enumerated Types](enum.md): a special case of inductive types.
- [Type Classes](typeclasses.md): a way of creating custom polymorphism.
- [Types as objects](typeobjs.md): a way of manipulating types themselves.
- [Structures](struct.md): a collection of named and typed fields. A
structure is actually special case of inductive datatype.
- [Inductive Types](inductive.md): TODO: add one liner...
## Universes
Every type in Lean is, by definition, an expression of type `Sort u`
for some universe level `u`. A universe level is one of the
following:
* a natural number, `n`
* a universe variable, `u` (declared with the command `universe` or `universes`)
* an expression `u + n`, where `u` is a universe level and `n` is a natural number
* an expression `max u v`, where `u` and `v` are universes
* an expression `imax u v`, where `u` and `v` are universe levels
The last one denotes the universe level `0` if `v` is `0`, and `max u v` otherwise.
```lean
universe u v
#check Sort u -- Type u
#check Sort 5 -- Type 4 : Type 5
#check Sort (u + 1) -- Type u : Type (u + 1)
#check Sort (u + 3) -- Type (u + 2) : Type (u + 3)
#check Sort (max u v) -- Sort (max u v) : Type (max u v)
#check Sort (max (u + 3) v) -- Sort (max (u + 3) v) : Type (max (u + 3) v)
#check Sort (imax (u + 3) v) -- Sort (imax (u + 3) v) : Type (imax (u + 3) v)
#check Prop -- Type
#check Type -- Type 1
#check Type 1 -- Type 1 : Type 2
```

View File

@@ -1 +0,0 @@
# Using Lean

View File

@@ -1,42 +0,0 @@
# What is Lean
Lean is a functional programming language that makes it easy to
write correct and maintainable code.
You can also use Lean as an interactive theorem prover.
Lean programming primarily involves defining types and functions.
This allows your focus to remain on the problem domain and manipulating its data,
rather than the details of programming.
```lean
-- Defines a function that takes a name and produces a greeting.
def getGreeting (name : String) := s!"Hello, {name}! Isn't Lean great?"
-- The `main` function is the entry point of your program.
-- Its type is `IO Unit` because it can perform `IO` operations (side effects).
def main : IO Unit :=
-- Define a list of names
let names := ["Sebastian", "Leo", "Daniel"]
-- Map each name to a greeting
let greetings := names.map getGreeting
-- Print the list of greetings
for greeting in greetings do
IO.println greeting
```
Lean has numerous features, including:
- Type inference
- First-class functions
- Powerful data types
- Pattern matching
- [Type classes](./typeclass.md)
- [Monads](./monads/intro.md)
- [Extensible syntax](./syntax.md)
- Hygienic macros
- [Dependent types](https://lean-lang.org/theorem_proving_in_lean4/dependent_type_theory.html)
- [Metaprogramming](./macro_overview.md)
- Multithreading
- Verification: you can prove properties of your functions using Lean itself

100
flake.lock generated
View File

@@ -1,112 +1,50 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1710889954,
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
"type": "github"
"lastModified": 1745636243,
"narHash": "sha256-kbNvlQZf8wwok3d2X1kM/TlXH/MZ+03ZNv+IPPBx+DM=",
"rev": "f771eb401a46846c1aebd20552521b233dd7e18b",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-25.05pre789333.f771eb401a46/nixexprs.tar.xz"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-cadical": {
"locked": {
"lastModified": 1740791350,
"narHash": "sha256-igS2Z4tVw5W/x3lCZeeadt0vcU9fxtetZ/RyrqsCRQ0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "199169a2135e6b864a888e89a2ace345703c025d",
"type": "github"
"type": "tarball",
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
}
},
"nixpkgs-old": {
"flake": false,
"locked": {
"lastModified": 1581379743,
"narHash": "sha256-i1XCn9rKuLjvCdu2UeXKzGLF6IuQePQKFt4hEKRU5oc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59",
"type": "github"
"lastModified": 1582018169,
"narHash": "sha256-qv1iK1IchpZKSeWL3NEs4U5Jl5QVyNHDdiMJvLOI4Yc=",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/19.03/nixos-19.03.173691.34c7eb7545d/nixexprs.tar.xz"
},
"original": {
"owner": "NixOS",
"ref": "nixos-19.03",
"repo": "nixpkgs",
"type": "github"
"type": "tarball",
"url": "https://channels.nixos.org/nixos-19.03/nixexprs.tar.xz"
}
},
"nixpkgs-older": {
"flake": false,
"locked": {
"lastModified": 1523316493,
"narHash": "sha256-5qJS+i5ECICPAKA6FhPLIWkhPKDnOZsZbh2PHYF1Kbs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0b307aa73804bbd7a7172899e59ae0b8c347a62d",
"type": "github"
"lastModified": 1550657948,
"narHash": "sha256-BE0XqzNfzvhhtTXv37LyySyq4moL7z1i1hMvwbFNL/I=",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/18.03/nixos-18.03.133402.cb0e20d6db9/nixexprs.tar.xz"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0b307aa73804bbd7a7172899e59ae0b8c347a62d",
"type": "github"
"type": "tarball",
"url": "https://channels.nixos.org/nixos-18.03/nixexprs.tar.xz"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"nixpkgs-cadical": "nixpkgs-cadical",
"nixpkgs-old": "nixpkgs-old",
"nixpkgs-older": "nixpkgs-older"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",

View File

@@ -1,29 +1,22 @@
{
description = "Lean development flake. Not intended for end users.";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# We use channels so we're not affected by GitHub's rate limits
inputs.nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz";
# old nixpkgs used for portable release with older glibc (2.27)
inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03";
inputs.nixpkgs-old.url = "https://channels.nixos.org/nixos-19.03/nixexprs.tar.xz";
inputs.nixpkgs-old.flake = false;
# old nixpkgs used for portable release with older glibc (2.26)
inputs.nixpkgs-older.url = "github:NixOS/nixpkgs/0b307aa73804bbd7a7172899e59ae0b8c347a62d";
inputs.nixpkgs-older.url = "https://channels.nixos.org/nixos-18.03/nixexprs.tar.xz";
inputs.nixpkgs-older.flake = false;
# for cadical 2.1.2; sync with CMakeLists.txt by taking commit from https://www.nixhub.io/packages/cadical
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/199169a2135e6b864a888e89a2ace345703c025d";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = inputs: inputs.flake-utils.lib.eachDefaultSystem (system:
outputs = inputs: builtins.foldl' inputs.nixpkgs.lib.attrsets.recursiveUpdate {} (builtins.map (system:
let
pkgs = import inputs.nixpkgs { inherit system; };
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old = import inputs.nixpkgs-older { inherit system; };
# An old nixpkgs for creating releases with an old glibc
pkgsDist-old-aarch = import inputs.nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
pkgsCadical = import inputs.nixpkgs-cadical { inherit system; };
cadical = if pkgs.stdenv.isLinux then
# use statically-linked cadical on Linux to avoid glibc versioning troubles
pkgsCadical.pkgsStatic.cadical.overrideAttrs { doCheck = false; }
else pkgsCadical.cadical;
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; };
@@ -31,7 +24,7 @@
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
} ({
buildInputs = with pkgs; [
cmake gmp libuv ccache cadical pkg-config
cmake gmp libuv ccache pkg-config
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
gdb
tree # for CI
@@ -67,15 +60,17 @@
GDB = pkgsDist.gdb;
});
in {
packages = {
packages.${system} = {
# to be removed when Nix CI is not needed anymore
inherit (lean-packages) cacheRoots test update-stage0-commit ciShell;
deprecated = lean-packages;
};
# The default development shell for working on lean itself
devShells.default = devShellWithDist pkgs;
devShells.oldGlibc = devShellWithDist pkgsDist-old;
devShells.oldGlibcAArch = devShellWithDist pkgsDist-old-aarch;
});
devShells.${system} = {
# The default development shell for working on lean itself
default = devShellWithDist pkgs;
oldGlibc = devShellWithDist pkgsDist-old;
oldGlibcAArch = devShellWithDist pkgsDist-old-aarch;
};
}) ["x86_64-linux" "aarch64-linux"]);
}

View File

@@ -194,7 +194,7 @@ with builtins; let
modCandidates = mapAttrs (mod: header:
let
deps = if header.errors == []
then map (m: m.module) header.imports
then map (m: m.module) header.result.imports
else abort "errors while parsing imports of ${mod}:\n${lib.concatStringsSep "\n" header.errors}";
in mkMod mod (map (dep: if modDepsMap ? ${dep} then modCandidates.${dep} else externalModMap.${dep}) deps)) modDepsMap;
expandGlob = g:
@@ -206,7 +206,7 @@ with builtins; let
# subset of `modCandidates` that is transitively reachable from `roots`
mods' = listToAttrs (map (e: { name = e.key; value = modCandidates.${e.key}; }) (genericClosure {
startSet = map (m: { key = m; }) (concatMap expandGlob roots);
operator = e: if modDepsMap ? ${e.key} then map (m: { key = m.module; }) (filter (m: modCandidates ? ${m.module}) modDepsMap.${e.key}.imports) else [];
operator = e: if modDepsMap ? ${e.key} then map (m: { key = m.module; }) (filter (m: modCandidates ? ${m.module}) modDepsMap.${e.key}.result.imports) else [];
}));
allLinkFlags = lib.foldr (shared: acc: acc ++ [ "-L${shared}" "-l${shared.linkName or shared.name}" ]) linkFlags allNativeSharedLibs;

View File

@@ -1,4 +1,5 @@
import Lean.Data.Lsp
import Lean.Elab.Import
open Lean
open Lean.Lsp
open Lean.JsonRpc
@@ -7,14 +8,12 @@ open Lean.JsonRpc
Tests language server memory use by repeatedly re-elaborate a given file.
NOTE: only works on Linux for now.
HACK: The line that is to be prepended with a space is hard-coded below to be sufficiently far down
not to touch the imports for usual files.
-/
def main (args : List String) : IO Unit := do
let leanCmd :: file :: iters :: args := args | panic! "usage: script <lean> <file> <#iterations> <server-args>..."
let uri := s!"file:///{file}"
let file IO.FS.realPath file
let uri := s!"file://{file}"
Ipc.runWith leanCmd (#["--worker", "-DstderrAsMessages=false"] ++ args ++ #[uri]) do
-- for use with heaptrack:
--Ipc.runWith "heaptrack" (#[leanCmd, "--worker", "-DstderrAsMessages=false"] ++ args ++ #[uri]) do
@@ -33,6 +32,8 @@ def main (args : List String) : IO Unit := do
Ipc.writeRequest 0, "initialize", { capabilities : InitializeParams }
let text IO.FS.readFile file
let (_, headerEndPos, _) Elab.parseImports text
let headerEndPos := FileMap.ofString text |>.leanPosToLspPos headerEndPos
let mut requestNo : Nat := 1
let mut versionNo : Nat := 1
Ipc.writeNotification "textDocument/didOpen", {
@@ -40,15 +41,14 @@ def main (args : List String) : IO Unit := do
for i in [0:iters.toNat!] do
if i > 0 then
versionNo := versionNo + 1
let pos := { line := 19, character := 0 }
let params : DidChangeTextDocumentParams := {
textDocument := {
uri := uri
version? := versionNo
}
contentChanges := #[TextDocumentContentChangeEvent.rangeChange {
start := pos
«end» := pos
start := headerEndPos
«end» := headerEndPos
} " "]
}
let params := toJson params

View File

@@ -47,10 +47,10 @@ def run_command(command, check=True, capture_output=True):
def clone_repo(repo, temp_dir):
"""Clone the repository to a temporary directory using shallow clone."""
print(f"Shallow cloning {repo}...")
# Keep the shallow clone for efficiency
clone_result = run_command(f"gh repo clone {repo} {temp_dir} -- --depth=1", check=False)
"""Clone the repository to a temporary directory."""
print(f"Cloning {repo}...")
# Remove shallow clone for better merge detection
clone_result = run_command(f"gh repo clone {repo} {temp_dir}", check=False)
if clone_result.returncode != 0:
print(f"Failed to clone repository {repo}.")
print(f"Error: {clone_result.stderr}")
@@ -95,26 +95,16 @@ def check_and_merge(repo, branch, tag, temp_dir):
if checkout_result.returncode != 0:
return False
# Try merging the tag in a dry-run to check if it can be merged cleanly
print(f"Checking if {tag} can be merged cleanly into {branch}...")
merge_check = run_command(f"git merge --no-commit --no-ff {tag}", check=False)
# Try merging the tag directly
print(f"Merging {tag} into {branch}...")
merge_result = run_command(f"git merge {tag} --no-edit", check=False)
if merge_check.returncode != 0:
if merge_result.returncode != 0:
print(f"Cannot merge {tag} cleanly into {branch}.")
print("Merge conflicts would occur. Aborting merge.")
run_command("git merge --abort")
return False
# Abort the test merge
run_command("git reset --hard HEAD")
# Now perform the actual merge and push to remote
print(f"Merging {tag} into {branch}...")
merge_result = run_command(f"git merge {tag} --no-edit")
if merge_result.returncode != 0:
print(f"Failed to merge {tag} into {branch}.")
return False
print(f"Pushing changes to remote...")
push_result = run_command(f"git push origin {branch}")
if push_result.returncode != 0:

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
set -uo pipefail
set -euxo pipefail
# run from root build directory (from inside nix-shell or otherwise defining GLIBC/ZLIB/GMP) as in
# ```
@@ -14,6 +14,7 @@ set -uo pipefail
else
ln -s llvm llvm-host
fi
mkdir -p stage0/lib
mkdir -p stage1/{bin,lib,lib/glibc,include/clang}
CP="cp -d" # preserve symlinks
# a C compiler!
@@ -25,6 +26,8 @@ cp -L llvm/bin/llvm-ar stage1/bin/
# dependencies of the above
$CP llvm/lib/lib{clang-cpp,LLVM}*.so* stage1/lib/
$CP $ZLIB/lib/libz.so* stage1/lib/
# also copy USE_LLVM deps into stage 0
$CP llvm/lib/libLLVM*.so* $ZLIB/lib/libz.so* stage0/lib/
# general clang++ dependency, breaks cross-library C++ exceptions if linked statically
$CP $GCC_LIB/lib/libgcc_s.so* stage1/lib/
# bundle libatomic (referenced by LLVM >= 15, and required by the lean executable to run)
@@ -39,20 +42,21 @@ $CP $GLIBC/lib/*crt* stage1/lib/
# runtime
(cd llvm; $CP --parents lib/clang/*/lib/*/{clang_rt.*.o,libclang_rt.builtins*} ../stage1)
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a $LIBUV/lib/libuv.a stage1/lib/
# LLVM 15 appears to ship the dependencies in 'llvm/lib/<target-triple>/' and 'llvm/include/<target-triple>/'
# but clang-15 that we use to compile is linked against 'llvm/lib/' and 'llvm/include'
# LLVM 19 appears to ship the dependencies in 'llvm/lib/<target-triple>/' and 'llvm/include/<target-triple>/'
# but clang-19 that we use to compile is linked against 'llvm/lib/' and 'llvm/include'
# https://github.com/llvm/llvm-project/issues/54955
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* llvm/lib/
$CP llvm-host/lib/*/lib{c++,c++abi,unwind}.* llvm-host/lib/
# libc++ headers are looked up in the host compiler's root, so copy over target-specific includes
$CP -r llvm/include/*-*-* llvm-host/include/
$CP -r llvm/include/*-*-* llvm-host/include/ || true
# glibc: use for linking (so Lean programs don't embed newer symbol versions), but not for running (because libc.so, librt.so, and ld.so must be compatible)!
$CP $GLIBC/lib/libc_nonshared.a stage1/lib/glibc
# libpthread_nonshared.a must be linked in order to be able to use `pthread_atfork(3)`. LibUV uses this function.
$CP $GLIBC/lib/libpthread_nonshared.a stage1/lib/glibc
for f in $GLIBC/lib/lib{c,dl,m,rt,pthread}-*; do b=$(basename $f); cp $f stage1/lib/glibc/${b%-*}.so; done
for f in $GLIBC/lib/{ld,lib{c,dl,m,rt,pthread}}-*; do b=$(basename $f); cp $f stage1/lib/glibc/${b%-*}.so; done
OPTIONS=()
echo -n " -DLEAN_STANDALONE=ON"
# We build cadical using the custom toolchain on Linux to avoid glibc versioning issues
echo -n " -DLEAN_STANDALONE=ON -DCADICAL_USE_CUSTOM_CXX=ON"
echo -n " -DCMAKE_CXX_COMPILER=$PWD/llvm-host/bin/clang++ -DLEAN_CXX_STDLIB='-Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic'"
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/include ${EXTRA_FLAGS:-}'"
# use target compiler directly when not cross-compiling
@@ -64,7 +68,9 @@ fi
# use `-nostdinc` to make sure headers are not visible by default (in particular, not to `#include_next` in the clang headers),
# but do not change sysroot so users can still link against system libs
echo -n " -DLEANC_INTERNAL_FLAGS='--sysroot ROOT -nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='--sysroot ROOT -L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a ROOT/lib/glibc/libpthread_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -luv -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
# ld.so is usually included by the libc.so linker script but we discard those. Make sure it is linked to only after `libc.so` like in the original
# linker script so that no libc symbols are bound to it instead.
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='--sysroot ROOT -L ROOT/lib -L ROOT/lib/glibc -lc -lc_nonshared -Wl,--as-needed -l:ld.so -Wl,--no-as-needed -lpthread_nonshared -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -luv -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
# when not using the above flags, link GMP dynamically/as usual
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -luv -lpthread -ldl -lrt -Wl,--no-as-needed'"
# do not set `LEAN_CC` for tests

View File

@@ -7,6 +7,7 @@ import base64
import subprocess
import sys
import os
import re # Import re module
# Import run_command from merge_remote.py
from merge_remote import run_command
@@ -58,13 +59,29 @@ def release_page_exists(repo_url, tag_name, github_token):
response = requests.get(api_url, headers=headers)
return response.status_code == 200
def get_release_notes(repo_url, tag_name, github_token):
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/releases/tags/{tag_name}"
headers = {'Authorization': f'token {github_token}'} if github_token else {}
response = requests.get(api_url, headers=headers)
if response.status_code == 200:
return response.json().get("body", "").strip()
return None
def get_release_notes(tag_name):
"""Fetch release notes page title from lean-lang.org."""
# Strip -rcX suffix if present for the URL
base_tag = tag_name.split('-')[0]
reference_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
try:
response = requests.get(reference_url)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
# Extract title using regex
match = re.search(r"<title>(.*?)</title>", response.text, re.IGNORECASE | re.DOTALL)
if match:
return match.group(1).strip()
else:
print(f" ⚠️ Could not find <title> tag in {reference_url}")
return None
except requests.exceptions.RequestException as e:
print(f" ❌ Error fetching release notes from {reference_url}: {e}")
return None
except Exception as e:
print(f" ❌ An unexpected error occurred while processing release notes: {e}")
return None
def get_branch_content(repo_url, branch, file_path, github_token):
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/contents/{file_path}?ref={branch}"
@@ -255,6 +272,7 @@ def main():
branch_name = f"releases/v{version_major}.{version_minor}.0"
if not branch_exists(lean_repo_url, branch_name, github_token):
print(f" ❌ Branch {branch_name} does not exist")
print(f" 🟡 After creating the branch, we'll need to check CMake version settings.")
lean4_success = False
else:
print(f" ✅ Branch {branch_name} exists")
@@ -274,14 +292,22 @@ def main():
lean4_success = False
else:
print(f" ✅ Release page for {toolchain} exists")
release_notes = get_release_notes(lean_repo_url, toolchain, github_token)
if not (release_notes and toolchain in release_notes.splitlines()[0].strip()):
previous_minor_version = version_minor - 1
previous_release = f"v{version_major}.{previous_minor_version}.0"
print(f" ❌ Release notes not published. Please run `script/release_notes.py --since {previous_release}` on branch `{branch_name}`.")
lean4_success = False
else:
print(f" ✅ Release notes look good.")
# Check the actual release notes page title
actual_title = get_release_notes(toolchain)
expected_title_prefix = f"Lean {toolchain.lstrip('v')}" # e.g., "Lean 4.19.0" or "Lean 4.19.0-rc1"
if actual_title is None:
# Error already printed by get_release_notes
lean4_success = False
elif not actual_title.startswith(expected_title_prefix):
# Construct URL for the error message (using the base tag)
base_tag = toolchain.split('-')[0]
check_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
print(f" ❌ Release notes page title mismatch. Expected prefix '{expected_title_prefix}', got '{actual_title}'. Check {check_url}")
lean4_success = False
else:
print(f" ✅ Release notes page title looks good ('{actual_title}').")
repo_status["lean4"] = lean4_success
@@ -360,10 +386,24 @@ def main():
if check_stable and not is_release_candidate(toolchain):
if not is_merged_into_stable(url, toolchain, "stable", github_token, verbose):
org_repo = extract_org_repo_from_url(url)
print(f" ❌ Tag {toolchain} is not merged into stable")
print(f" Run `script/merge_remote.py {org_repo} stable {toolchain}` to merge it")
repo_status[name] = False
continue
if args.dry_run:
print(f" ❌ Tag {toolchain} is not merged into stable")
print(f" Run `script/merge_remote.py {org_repo} stable {toolchain}` to merge it")
repo_status[name] = False
continue
else:
print(f" … Tag {toolchain} is not merged into stable. Running `script/merge_remote.py {org_repo} stable {toolchain}`...")
# Run the script to merge the tag
subprocess.run(["script/merge_remote.py", org_repo, "stable", toolchain])
# Check again if the tag is merged now
if not is_merged_into_stable(url, toolchain, "stable", github_token, verbose):
print(f" ❌ Manual intervention required.")
repo_status[name] = False
continue
# This will print in all successful cases - whether tag was merged initially or was merged successfully
print(f" ✅ Tag {toolchain} is merged into stable")
if check_bump:

View File

@@ -21,12 +21,19 @@ repositories:
branch: master
dependencies: []
- name: lean4-cli
url: https://github.com/leanprover/lean4-cli
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: doc-gen4
url: https://github.com/leanprover/doc-gen4
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
dependencies: [lean4-cli]
- name: verso
url: https://github.com/leanprover/verso
@@ -42,20 +49,13 @@ repositories:
branch: main
dependencies: [verso]
- name: lean4-cli
url: https://github.com/leanprover/lean4-cli
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: ProofWidgets4
url: https://github.com/leanprover-community/ProofWidgets4
toolchain-tag: false
stable-branch: false
branch: main
dependencies:
- Batteries
- batteries
- name: aesop
url: https://github.com/leanprover-community/aesop
@@ -63,7 +63,7 @@ repositories:
stable-branch: true
branch: master
dependencies:
- Batteries
- batteries
- name: import-graph
url: https://github.com/leanprover-community/import-graph
@@ -71,8 +71,8 @@ repositories:
stable-branch: false
branch: main
dependencies:
- Cli
- Batteries
- lean4-cli
- batteries
- name: plausible
url: https://github.com/leanprover-community/plausible
@@ -88,10 +88,11 @@ repositories:
branch: master
bump-branch: true
dependencies:
- Aesop
- aesop
- ProofWidgets4
- lean4checker
- Batteries
- batteries
- lean4-cli
- doc-gen4
- import-graph
- plausible
@@ -102,4 +103,4 @@ repositories:
stable-branch: true
branch: master
dependencies:
- Mathlib
- mathlib4

View File

@@ -68,7 +68,7 @@ def generate_script(repo, version, config):
]
# Special cases for specific repositories
if repo_name == "REPL":
if repo_name == "repl":
script_lines.extend([
"lake update",
"cd test/Mathlib",
@@ -79,7 +79,7 @@ def generate_script(repo, version, config):
"./test.sh"
])
elif dependencies:
script_lines.append('echo "Please update the dependencies in lakefile.{lean,toml}"')
script_lines.append('perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*')
script_lines.append("lake update")
script_lines.append("")
@@ -89,13 +89,20 @@ def generate_script(repo, version, config):
""
])
if re.search(r'rc\d+$', version) and repo_name in ["Batteries", "Mathlib"]:
if re.search(r'rc\d+$', version) and repo_name in ["batteries", "mathlib4"]:
script_lines.extend([
"echo 'This repo has nightly-testing infrastructure'",
f"git merge origin/bump/{version.split('-rc')[0]}",
"echo 'Please resolve any conflicts.'",
""
])
if re.search(r'rc\d+$', version) and repo_name in ["verso", "reference-manual"]:
script_lines.extend([
"echo 'This repo does development on nightly-testing: remember to rebase merge the PR.'",
f"git merge origin/nightly-testing",
"echo 'Please resolve any conflicts.'",
""
])
if repo_name != "Mathlib":
script_lines.extend([
"lake build && if lake check-test; then lake test; fi",
@@ -104,7 +111,7 @@ def generate_script(repo, version, config):
script_lines.extend([
'gh pr create --title "chore: bump toolchain to ' + version + '" --body ""',
"echo 'Please review the PR and merge it.'",
"echo 'Please review the PR and merge or rebase it.'",
""
])

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 20)
set(LEAN_VERSION_MINOR 21)
set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -191,10 +191,11 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
# Initialize CXXFLAGS.
set(CMAKE_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -DLEAN_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\" -DLEAN_EXPORTING")
set(CMAKE_CXX_FLAGS_DEBUG "-DLEAN_DEBUG -DLEAN_TRACE")
set(CMAKE_CXX_FLAGS_DEBUG "-DLEAN_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHASSERT "-DLEAN_DEBUG")
# SPLIT_STACK
if (SPLIT_STACK)
@@ -221,6 +222,7 @@ elseif (MSVC)
set(CMAKE_CXX_FLAGS_DEBUG "/Od /Zi ${CMAKE_CXX_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_MINSIZEREL "/Os /Zc:inline ${CMAKE_CXX_FLAGS_MINSIZEREL}")
set(CMAKE_CXX_FLAGS_RELEASE "/O2 /Oi /Oy /Zc:inline ${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHASSERT "/O2 /Oi /Oy /Zc:inline ${CMAKE_CXX_FLAGS_RELWITHASSERT}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/O2 /Oi /Zi ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
set(LEAN_EXTRA_LINKER_FLAGS "/LTCG:INCREMENTAL ${LEAN_EXTRA_LINKER_FLAGS}")
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} ${LEAN_EXTRA_LINKER_FLAGS}")
@@ -240,11 +242,13 @@ if (NOT MSVC)
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os ${CMAKE_CXX_FLAGS_MINSIZEREL}")
endif ()
set(CMAKE_CXX_FLAGS_RELEASE "-O3 ${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHASSERT "-O3 ${CMAKE_CXX_FLAGS_RELWITHASSERT}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g3 -fno-omit-frame-pointer ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
elseif (MULTI_THREAD)
set(CMAKE_CXX_FLAGS_DEBUG "/MTd ${CMAKE_CXX_FLAGS_DEBUG}")
set(CMAKE_CXX_FLAGS_MINSIZEREL "/MT ${CMAKE_CXX_FLAGS_MINSIZEREL}")
set(CMAKE_CXX_FLAGS_RELEASE "/MT ${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELWITHASSERT "/MT ${CMAKE_CXX_FLAGS_RELWITHASSERT}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MT ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
endif ()
@@ -365,8 +369,8 @@ if(LLVM)
execute_process(COMMAND ${LLVM_CONFIG} --version COMMAND_ERROR_IS_FATAL ANY OUTPUT_VARIABLE LLVM_CONFIG_VERSION ECHO_OUTPUT_VARIABLE OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REGEX MATCH "^[0-9]*" LLVM_CONFIG_MAJOR_VERSION ${LLVM_CONFIG_VERSION})
message(STATUS "Found 'llvm-config' at '${LLVM_CONFIG}' with version '${LLVM_CONFIG_VERSION}', major version '${LLVM_CONFIG_MAJOR_VERSION}'")
if (NOT LLVM_CONFIG_MAJOR_VERSION STREQUAL "15")
message(FATAL_ERROR "Unable to find llvm-config version 15. Found invalid version '${LLVM_CONFIG_MAJOR_VERSION}'")
if (NOT LLVM_CONFIG_MAJOR_VERSION STREQUAL "19")
message(FATAL_ERROR "Unable to find llvm-config version 19. Found invalid version '${LLVM_CONFIG_MAJOR_VERSION}'")
endif()
# -DLEAN_LLVM is used to conditionally compile Lean features that depend on LLVM
string(APPEND CMAKE_CXX_FLAGS " -D LEAN_LLVM")
@@ -507,7 +511,10 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# import libraries created by the stdlib.make targets
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup")
# The second flag is necessary to even *load* dylibs without resolved symbols, as can happen
# if a Lake `extern_lib` depends on a symbols defined by the Lean library but is loaded even
# before definition.
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup -Wl,-no_fixup_chains")
endif()
# Linux ignores undefined symbols in shared libraries by default
@@ -682,7 +689,7 @@ add_custom_target(make_stdlib ALL
# The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server
# for a parallelized nested build, but CMake doesn't let us do that.
# We use `lean` from the previous stage, but `leanc`, headers, etc. from the current stage
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean Leanc
VERBATIM)
# if we have LLVM enabled, then build `lean.h.bc` which has the LLVM bitcode
@@ -761,7 +768,7 @@ if(${STAGE} GREATER 0 AND EXISTS ${LEAN_SOURCE_DIR}/Leanc.lean AND NOT ${CMAKE_S
add_custom_target(leanc ALL
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/leanc
DEPENDS leanshared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Leanc
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanc
VERBATIM)
endif()
@@ -780,12 +787,11 @@ add_custom_target(clean-olean
DEPENDS clean-stdlib)
install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION lib
PATTERN temp
PATTERN "*.export"
PATTERN "*.hash"
PATTERN "*.trace"
PATTERN "*.rsp"
EXCLUDE)
PATTERN temp EXCLUDE
PATTERN "*.export" EXCLUDE
PATTERN "*.hash" EXCLUDE
PATTERN "*.trace" EXCLUDE
PATTERN "*.rsp" EXCLUDE)
# symlink source into expected installation location for go-to-definition, if file system allows it
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src)
@@ -817,7 +823,6 @@ endif()
# Escape for `make`. Yes, twice.
string(REPLACE "$" "\\\$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}")
configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make)
# hacky
@@ -844,6 +849,4 @@ endif()
if(USE_LAKE AND STAGE EQUAL 1)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../tests/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../lakefile.toml)
endif()

View File

@@ -3,6 +3,8 @@ Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Prelude
import Init.Notation

View File

@@ -4,6 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/
module
prelude
import Init.Prelude
import Init.Tactics
@@ -38,5 +40,5 @@ This gadget is supported by
It is ineffective in other positions (hyptheses of rewrite rules) or when used by other tactics
(e.g. `apply`).
-/
@[simp ]
@[simp , expose]
def binderNameHint {α : Sort u} {β : Sort v} {γ : Sort w} (v : α) (binder : β) (e : γ) : γ := e

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner
-/
module
prelude
import Init.NotationExtra

View File

@@ -3,6 +3,8 @@ Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.Classical
@@ -40,24 +42,3 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
/-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/
@[simp] theorem dite_eq_ite [Decidable P] :
(dite P (fun _ => a) (fun _ => b)) = ite P a b := rfl
@[deprecated "Use `ite_eq_right_iff`" (since := "2024-09-18")]
theorem ite_some_none_eq_none [Decidable P] :
(if P then some x else none) = none ¬ P := by
simp only [ite_eq_right_iff, reduceCtorEq]
rfl
@[deprecated "Use `Option.ite_none_right_eq_some`" (since := "2024-09-18")]
theorem ite_some_none_eq_some [Decidable P] :
(if P then some x else none) = some y P x = y := by
split <;> simp_all
@[deprecated "Use `dite_eq_right_iff" (since := "2024-09-18")]
theorem dite_some_none_eq_none [Decidable P] {x : P α} :
(if h : P then some (x h) else none) = none ¬P := by
simp
@[deprecated "Use `Option.dite_none_right_eq_some`" (since := "2024-09-18")]
theorem dite_some_none_eq_some [Decidable P] {x : P α} {y : α} :
(if h : P then some (x h) else none) = some y h : P, x h = y := by
by_cases h : P <;> simp [h]

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.PropLemmas

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.Prelude
set_option linter.missingDocs true -- keep it documented
@@ -307,9 +309,6 @@ instance boolToSort : CoeSort Bool Prop where
instance decPropToBool (p : Prop) [Decidable p] : CoeDep Prop p Bool where
coe := decide p
instance optionCoe {α : Type u} : Coe α (Option α) where
coe := some
instance subtypeCoe {α : Sort u} {p : α Prop} : CoeOut (Subtype p) α where
coe v := v.val

View File

@@ -3,6 +3,8 @@ Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Control.Basic
import Init.Control.State

View File

@@ -3,6 +3,8 @@ Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura, Sebastian Ullrich
-/
module
prelude
import Init.Core
import Init.BinderNameHint

View File

@@ -3,6 +3,8 @@ Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Control.State
import Init.Control.Except

View File

@@ -5,6 +5,8 @@ Authors: Jared Roesch, Sebastian Ullrich
The Except monad transformer.
-/
module
prelude
import Init.Control.Basic
import Init.Control.Id
@@ -125,7 +127,7 @@ end Except
/--
Adds exceptions of type `ε` to a monad `m`.
-/
def ExceptT (ε : Type u) (m : Type u Type v) (α : Type u) : Type v :=
@[expose] def ExceptT (ε : Type u) (m : Type u Type v) (α : Type u) : Type v :=
m (Except ε α)
/--

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Control.Lawful.Basic
@@ -16,7 +18,7 @@ Adds exceptions of type `ε` to a monad `m`.
Instead of using `Except ε` to model exceptions, this implementation uses continuation passing
style. This has different performance characteristics from `ExceptT ε`.
-/
def ExceptCpsT (ε : Type u) (m : Type u Type v) (α : Type u) := (β : Type u) (α m β) (ε m β) m β
@[expose] def ExceptCpsT (ε : Type u) (m : Type u Type v) (α : Type u) := (β : Type u) (α m β) (ε m β) m β
namespace ExceptCpsT

View File

@@ -5,6 +5,8 @@ Authors: Sebastian Ullrich
The identity Monad.
-/
module
prelude
import Init.Core
@@ -32,7 +34,7 @@ def containsFive (xs : List Nat) : Bool := Id.run do
true
```
-/
def Id (type : Type u) : Type u := type
@[expose] def Id (type : Type u) : Type u := type
namespace Id
@@ -54,7 +56,7 @@ Runs a computation in the identity monad.
This function is the identity function. Because its parameter has type `Id α`, it causes
`do`-notation in its arguments to use the `Monad Id` instance.
-/
@[always_inline, inline]
@[always_inline, inline, expose]
protected def run (x : Id α) : α := x
instance [OfNat α n] : OfNat (Id α) n :=

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.Control.Lawful.Basic
import Init.Control.Lawful.Instances

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.SimpLemmas
import Init.Meta
@@ -142,6 +144,7 @@ class LawfulMonad (m : Type u → Type v) [Monad m] : Prop extends LawfulApplica
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
attribute [simp] pure_bind bind_assoc bind_pure_comp
attribute [grind] pure_bind
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
show x >>= (fun a => pure (id a)) = x

View File

@@ -3,9 +3,12 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
-/
module
prelude
import Init.Control.Lawful.Basic
import Init.Control.Except
import all Init.Control.Except
import all Init.Control.State
import Init.Control.StateRef
import Init.Ext
@@ -96,7 +99,7 @@ end ExceptT
instance : LawfulMonad (Except ε) := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl)
(pure_bind := fun _ _ => rfl)
(pure_bind := fun _ _ => by rfl)
(bind_assoc := fun a _ _ => by cases a <;> rfl)
instance : LawfulApplicative (Except ε) := inferInstance
@@ -245,7 +248,7 @@ instance : LawfulMonad (EStateM ε σ) := .mk'
match x s with
| .ok _ _ => rfl
| .error _ _ => rfl)
(pure_bind := fun _ _ => rfl)
(pure_bind := fun _ _ => by rfl)
(bind_assoc := fun x _ _ => funext <| fun s => by
dsimp only [EStateM.instMonad, EStateM.bind]
match x s with

View File

@@ -3,6 +3,8 @@ Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
module
prelude
import Init.Control.Lawful.Basic
import Init.RCases

View File

@@ -3,6 +3,8 @@ Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich
-/
module
prelude
import Init.Data.Option.Basic
import Init.Control.Basic
@@ -18,7 +20,7 @@ instance : ToBool (Option α) := ⟨Option.isSome⟩
Adds the ability to fail to a monad. Unlike ordinary exceptions, there is no way to signal why a
failure occurred.
-/
def OptionT (m : Type u Type v) (α : Type u) : Type v :=
@[expose] def OptionT (m : Type u Type v) (α : Type u) : Type v :=
m (Option α)
/--
@@ -59,6 +61,9 @@ instance : Monad (OptionT m) where
pure := OptionT.pure
bind := OptionT.bind
instance {m : Type u Type v} [Pure m] : Inhabited (OptionT m α) where
default := pure (f:=m) default
/--
Recovers from failures. Typically used via the `<|>` operator.
-/
@@ -95,7 +100,7 @@ Handles failures by treating them as exceptions of type `Unit`.
-/
@[always_inline, inline] protected def tryCatch (x : OptionT m α) (handle : Unit OptionT m α) : OptionT m α := OptionT.mk do
let some a x | handle ()
pure a
pure <| some a
instance : MonadExceptOf Unit (OptionT m) where
throw := fun _ => OptionT.fail

View File

@@ -5,6 +5,8 @@ Authors: Sebastian Ullrich
The Reader monad transformer for passing immutable State.
-/
module
prelude
import Init.Control.Basic
import Init.Control.Id

View File

@@ -5,6 +5,8 @@ Authors: Leonardo de Moura, Sebastian Ullrich
The State monad transformer.
-/
module
prelude
import Init.Control.Basic
import Init.Control.Id
@@ -20,7 +22,7 @@ Adds a mutable state of type `σ` to a monad.
Actions in the resulting monad are functions that take an initial state and return, in `m`, a tuple
of a value and a state.
-/
def StateT (σ : Type u) (m : Type u Type v) (α : Type u) : Type (max u v) :=
@[expose] def StateT (σ : Type u) (m : Type u Type v) (α : Type u) : Type (max u v) :=
σ m (α × σ)
/--
@@ -45,7 +47,7 @@ A tuple-based state monad.
Actions in `StateM σ` are functions that take an initial state and return a value paired with a
final state.
-/
@[reducible]
@[expose, reducible]
def StateM (σ α : Type u) : Type u := StateT σ Id α
instance {σ α} [Subsingleton σ] [Subsingleton α] : Subsingleton (StateM σ α) where

View File

@@ -3,6 +3,8 @@ Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Control.Lawful.Basic
@@ -16,7 +18,7 @@ The State monad transformer using CPS style.
An alternative implementation of a state monad transformer that internally uses continuation passing
style instead of tuples.
-/
def StateCpsT (σ : Type u) (m : Type u Type v) (α : Type u) := (δ : Type u) σ (α σ m δ) m δ
@[expose] def StateCpsT (σ : Type u) (m : Type u Type v) (α : Type u) := (δ : Type u) σ (α σ m δ) m δ
namespace StateCpsT

View File

@@ -5,6 +5,8 @@ Authors: Leonardo de Moura, Sebastian Ullrich
The State monad transformer using IO references.
-/
module
prelude
import Init.System.ST
@@ -15,7 +17,7 @@ A state monad that uses an actual mutable reference cell (i.e. an `ST.Ref ω σ`
The macro `StateRefT σ m α` infers `ω` from `m`. It should normally be used instead.
-/
def StateRefT' (ω : Type) (σ : Type) (m : Type Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
@[expose] def StateRefT' (ω : Type) (σ : Type) (m : Type Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
/-! Recall that `StateRefT` is a macro that infers `ω` from the `m`. -/

View File

@@ -5,6 +5,8 @@ Authors: Leonardo de Moura
Notation for operators defined at Prelude.lean
-/
module
prelude
import Init.Tactics
import Init.Meta
@@ -318,6 +320,25 @@ syntax "repeat " convSeq : conv
macro_rules
| `(conv| repeat $seq) => `(conv| first | ($seq); repeat $seq | skip)
/--
Extracts `let` and `let_fun` expressions from within the target expression.
This is the conv mode version of the `extract_lets` tactic.
- `extract_lets` extracts all the lets from the target.
- `extract_lets x y z` extracts all the lets from the target and uses `x`, `y`, and `z` for the first names.
Using `_` for a name leaves it unnamed.
Limitation: the extracted local declarations do not persist outside of the `conv` goal.
See also `lift_lets`, which does not extract lets as local declarations.
-/
syntax (name := extractLets) "extract_lets " optConfig (ppSpace colGt (ident <|> hole))* : conv
/--
Lifts `let` and `let_fun` expressions within the target expression as far out as possible.
This is the conv mode version of the `lift_lets` tactic.
-/
syntax (name := liftLets) "lift_lets " optConfig : conv
/--
`conv => ...` allows the user to perform targeted rewriting on a goal or hypothesis,
by focusing on particular subexpressions.

View File

@@ -5,11 +5,15 @@ Authors: Leonardo de Moura
notation, basic datatypes and type classes
-/
module
prelude
import Init.Prelude
import Init.SizeOf
set_option linter.missingDocs true -- keep it documented
@[expose] section
universe u v w
/--
@@ -21,6 +25,8 @@ which applies to all applications of the function).
theorem id_def {α : Sort u} (a : α) : id a = a := rfl
attribute [grind] id
/--
`flip f a b` is `f b a`. It is useful for "point-free" programming,
since it can sometimes be used to avoid introducing variables.
@@ -47,6 +53,9 @@ theorem Function.comp_def {α β δ} (f : β → δ) (g : α → β) : f ∘ g =
@[simp] theorem Function.false_comp {f : α β} : ((fun _ => false) f) = fun _ => false := by
rfl
@[simp] theorem Function.comp_id (f : α β) : f id = f := rfl
@[simp] theorem Function.id_comp (f : α β) : id f = f := rfl
attribute [simp] namedPattern
/--
@@ -738,6 +747,20 @@ Unlike `x ≠ y` (which is notation for `Ne x y`), this is `Bool` valued instead
recommended_spelling "bne" for "!=" in [bne, «term_!=_»]
/-- `ReflBEq α` says that the `BEq` implementation is reflexive. -/
class ReflBEq (α) [BEq α] : Prop where
/-- `==` is reflexive, that is, `(a == a) = true`. -/
protected rfl {a : α} : a == a
@[simp] theorem BEq.rfl [BEq α] [ReflBEq α] {a : α} : a == a := ReflBEq.rfl
theorem BEq.refl [BEq α] [ReflBEq α] (a : α) : a == a := BEq.rfl
theorem beq_of_eq [BEq α] [ReflBEq α] {a b : α} : a = b a == b
| rfl => BEq.rfl
theorem not_eq_of_beq_eq_false [BEq α] [ReflBEq α] {a b : α} (h : (a == b) = false) : ¬a = b := by
intro h'; subst h'; have : true = false := BEq.rfl.symm.trans h; contradiction
/--
A Boolean equality test coincides with propositional equality.
@@ -745,11 +768,9 @@ In other words:
* `a == b` implies `a = b`.
* `a == a` is true.
-/
class LawfulBEq (α : Type u) [BEq α] : Prop where
class LawfulBEq (α : Type u) [BEq α] : Prop extends ReflBEq α where
/-- If `a == b` evaluates to `true`, then `a` and `b` are equal in the logic. -/
eq_of_beq : {a b : α} a == b a = b
/-- `==` is reflexive, that is, `(a == a) = true`. -/
protected rfl : {a : α} a == a
export LawfulBEq (eq_of_beq)
@@ -761,6 +782,15 @@ instance [DecidableEq α] : LawfulBEq α where
eq_of_beq := of_decide_eq_true
rfl := of_decide_eq_self_eq_true _
/--
Non-instance for `DecidableEq` from `LawfulBEq`.
To use this, add `attribute [local instance 5] instDecidableEqOfLawfulBEq` at the top of a file.
-/
def instDecidableEqOfLawfulBEq [BEq α] [LawfulBEq α] : DecidableEq α := fun x y =>
match h : x == y with
| false => .isFalse (not_eq_of_beq_eq_false h)
| true => .isTrue (eq_of_beq h)
instance : LawfulBEq Char := inferInstance
instance : LawfulBEq String := inferInstance
@@ -855,8 +885,8 @@ theorem Bool.of_not_eq_false : {b : Bool} → ¬ (b = false) → b = true
| true, _ => rfl
| false, h => absurd rfl h
theorem ne_of_beq_false [BEq α] [LawfulBEq α] {a b : α} (h : (a == b) = false) : a b := by
intro h'; subst h'; have : true = false := Eq.trans LawfulBEq.rfl.symm h; contradiction
theorem ne_of_beq_false [BEq α] [ReflBEq α] {a b : α} (h : (a == b) = false) : a b :=
not_eq_of_beq_eq_false h
theorem beq_false_of_ne [BEq α] [LawfulBEq α] {a b : α} (h : a b) : (a == b) = false :=
have : ¬ (a == b) = true := by
@@ -915,6 +945,34 @@ term.
theorem eqRec_heq {α : Sort u} {φ : α Sort v} {a a' : α} : (h : a = a') (p : φ a) HEq (Eq.recOn (motive := fun x _ => φ x) h p) p
| rfl, p => HEq.refl p
/--
Heterogeneous equality with an `Eq.rec` application on the left is equivalent to a heterogeneous
equality on the original term.
-/
theorem eqRec_heq_iff {α : Sort u} {a : α} {motive : (b : α) a = b Sort v}
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} :
HEq (@Eq.rec α a motive refl b h) c HEq refl c :=
h.rec (fun _ => id, id) c
/--
Heterogeneous equality with an `Eq.rec` application on the right is equivalent to a heterogeneous
equality on the original term.
-/
theorem heq_eqRec_iff {α : Sort u} {a : α} {motive : (b : α) a = b Sort v}
{b : α} {refl : motive a (Eq.refl a)} {h : a = b} {c : motive b h} :
HEq c (@Eq.rec α a motive refl b h) HEq c refl :=
h.rec (fun _ => id, id) c
/--
Moves an cast using `Eq.rec` from the function to the argument.
Note: because the motive isn't reliably detected by unification,
it needs to be provided as an explicit parameter.
-/
theorem apply_eqRec {α : Sort u} {a : α} (motive : (b : α) a = b Sort v)
{b : α} {h : a = b} {c : motive a (Eq.refl a) β} {d : motive b h} :
@Eq.rec α a (fun b h => motive b h β) c b h d = c (h.symm d) := by
cases h; rfl
/--
If casting a term with `Eq.rec` to another type makes it equal to some other term, then the two
terms are heterogeneously equal.
@@ -960,7 +1018,7 @@ theorem HEq.comm {a : α} {b : β} : HEq a b ↔ HEq b a := Iff.intro HEq.symm H
theorem heq_comm {a : α} {b : β} : HEq a b HEq b a := HEq.comm
@[symm] theorem Iff.symm (h : a b) : b a := Iff.intro h.mpr h.mp
theorem Iff.comm: (a b) (b a) := Iff.intro Iff.symm Iff.symm
theorem Iff.comm : (a b) (b a) := Iff.intro Iff.symm Iff.symm
theorem iff_comm : (a b) (b a) := Iff.comm
@[symm] theorem And.symm : a b b a := fun ha, hb => hb, ha
@@ -1125,12 +1183,12 @@ theorem dif_eq_if (c : Prop) {h : Decidable c} {α : Sort u} (t : α) (e : α) :
| isTrue _ => rfl
| isFalse _ => rfl
instance {c t e : Prop} [dC : Decidable c] [dT : Decidable t] [dE : Decidable e] : Decidable (if c then t else e) :=
instance {c t e : Prop} [dC : Decidable c] [dT : Decidable t] [dE : Decidable e] : Decidable (if c then t else e) :=
match dC with
| isTrue _ => dT
| isFalse _ => dE
instance {c : Prop} {t : c Prop} {e : ¬c Prop} [dC : Decidable c] [dT : h, Decidable (t h)] [dE : h, Decidable (e h)] : Decidable (if h : c then t h else e h) :=
instance {c : Prop} {t : c Prop} {e : ¬c Prop} [dC : Decidable c] [dT : h, Decidable (t h)] [dE : h, Decidable (e h)] : Decidable (if h : c then t h else e h) :=
match dC with
| isTrue hc => dT hc
| isFalse hc => dE hc
@@ -1296,6 +1354,15 @@ theorem eta (a : {x // p x}) (h : p (val a)) : mk (val a) h = a := by
cases a
exact rfl
instance {α : Type u} {p : α Prop} [BEq α] : BEq {x : α // p x} :=
fun x y => x.1 == y.1
instance {α : Type u} {p : α Prop} [BEq α] [ReflBEq α] : ReflBEq {x : α // p x} where
rfl {x} := BEq.refl x.1
instance {α : Type u} {p : α Prop} [BEq α] [LawfulBEq α] : LawfulBEq {x : α // p x} where
eq_of_beq h := Subtype.eq (eq_of_beq h)
instance {α : Type u} {p : α Prop} [DecidableEq α] : DecidableEq {x : α // p x} :=
fun a, h₁ b, h₂ =>
if h : a = b then isTrue (by subst h; exact rfl)
@@ -1564,7 +1631,7 @@ theorem Nat.succ.injEq (u v : Nat) : (u.succ = v.succ) = (u = v) :=
Eq.propIntro Nat.succ.inj (congrArg Nat.succ)
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b a = b :=
eq_of_beq, by intro h; subst h; exact LawfulBEq.rfl
eq_of_beq, beq_of_eq
/-! # Prop lemmas -/
@@ -1837,9 +1904,7 @@ protected abbrev hrecOn
(f : (a : α) motive (Quot.mk r a))
(c : (a b : α) (p : r a b) HEq (f a) (f b))
: motive q :=
Quot.recOn q f fun a b p => eq_of_heq <|
have p₁ : HEq (Eq.ndrec (f a) (sound p)) (f a) := eqRec_heq (sound p) (f a)
HEq.trans p₁ (c a b p)
Quot.recOn q f fun a b p => eq_of_heq (eqRec_heq_iff.mpr (c a b p))
end
end Quot
@@ -2202,6 +2267,27 @@ theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x}
show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
exact congrArg extfunApp (Quot.sound h)
/--
Like `Quot.liftOn q f h` but allows `f a` to "know" that `q = Quot.mk r a`.
-/
protected abbrev Quot.pliftOn {α : Sort u} {r : α α Prop}
(q : Quot r)
(f : (a : α) q = Quot.mk r a β)
(h : (a b : α) (h h'), r a b f a h = f b h') : β :=
q.rec (motive := fun q' => q = q' β) f
(fun a b p => funext fun h' =>
(apply_eqRec (motive := fun b _ => q = b)).trans
(@h a b (h'.trans (sound p).symm) h' p)) rfl
/--
Like `Quotient.liftOn q f h` but allows `f a` to "know" that `q = Quotient.mk s a`.
-/
protected abbrev Quotient.pliftOn {α : Sort u} {s : Setoid α}
(q : Quotient s)
(f : (a : α) q = Quotient.mk s a β)
(h : (a b : α) (h h'), a b f a h = f b h') : β :=
Quot.pliftOn q f h
instance Pi.instSubsingleton {α : Sort u} {β : α Sort v} [ a, Subsingleton (β a)] :
Subsingleton ( a, β a) where
allEq f g := funext fun a => Subsingleton.elim (f a) (g a)
@@ -2443,9 +2529,6 @@ class Antisymm (r : αα → Prop) : Prop where
/-- An antisymmetric relation `r` satisfies `r a b → r b a → a = b`. -/
antisymm (a b : α) : r a b r b a a = b
@[deprecated Antisymm (since := "2024-10-16"), inherit_doc Antisymm]
abbrev _root_.Antisymm (r : α α Prop) : Prop := Std.Antisymm r
/-- `Asymm X r` means that the binary relation `r` on `X` is asymmetric, that is,
`r a b → ¬ r b a`. -/
class Asymm (r : α α Prop) : Prop where

View File

@@ -3,6 +3,8 @@ Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Init.Data.Basic
import Init.Data.Nat
@@ -34,7 +36,6 @@ import Init.Data.Stream
import Init.Data.Prod
import Init.Data.AC
import Init.Data.Queue
import Init.Data.Channel
import Init.Data.Sum
import Init.Data.BEq
import Init.Data.Subtype

View File

@@ -4,6 +4,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Dany Fabian
-/
module
prelude
import Init.Classical
import Init.ByCases

View File

@@ -3,6 +3,8 @@ Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner
-/
module
prelude
import Init.Data.Array.Basic
import Init.Data.Array.QSort

Some files were not shown because too many files have changed in this diff Show More