Compare commits

..

1 Commits

Author SHA1 Message Date
Henrik Böving
70d1229b2c wip 2025-11-19 09:46:00 +00:00
3051 changed files with 15534 additions and 60226 deletions

View File

@@ -1,47 +1,14 @@
To build Lean you should use `make -j -C build/release`.
To run a test you should use `cd tests/lean/run && ./test_single.sh example_test.lean`.
## New features
When asked to implement new features:
* begin by reviewing existing relevant code and tests
* write comprehensive tests first (expecting that these will initially fail)
* and then iterate on the implementation until the tests pass.
All new tests should go in `tests/lean/run/`. These tests don't have expected output; we just check there are no errors. You should use `#guard_msgs` to check for specific messages.
To build Lean you should use `make -j$(nproc) -C build/release`.
## Success Criteria
To run a test you should use `cd tests/lean/run && ./test_single.sh example_test.lean`.
*Never* report success on a task unless you have verified both a clean build without errors, and that the relevant tests pass.
*Never* report success on a task unless you have verified both a clean build without errors, and that the relevant tests pass. You have to keep working until you have verified both of these.
## Build System Safety
All new tests should go in `tests/lean/run/`. Note that these tests don't have expected output, and just run on a success or failure basis. So you should use `#guard_msgs` to check for specific messages.
**NEVER manually delete build directories** (build/, stage0/, stage1/, etc.) even when builds fail.
- ONLY use the project's documented build command: `make -j -C build/release`
- If a build is broken, ask the user before attempting any manual cleanup
## LSP and IDE Diagnostics
After rebuilding, LSP diagnostics may be stale until the user interacts with files. Trust command-line test results over IDE diagnostics.
## Update prompting when the user is frustrated
If the user expresses frustration with you, stop and ask them to help update this `.claude/CLAUDE.md` file with missing guidance.
## Creating pull requests
Follow the commit convention in `doc/dev/commit_convention.md`.
**Title format:** `<type>: <subject>` where type is one of: `feat`, `fix`, `doc`, `style`, `refactor`, `test`, `chore`, `perf`.
Subject should use imperative present tense ("add" not "added"), no capitalization, no trailing period.
**Body format:** The first paragraph must start with "This PR". This paragraph is automatically incorporated into release notes. Use imperative present tense. Include motivation and contrast with previous behavior when relevant.
Example:
```
feat: add optional binder limit to `mkPatternFromTheorem`
This PR adds a `num?` parameter to `mkPatternFromTheorem` to control how many
leading quantifiers are stripped when creating a pattern.
```
If you are not following best practices specific to this repository and the user expresses frustration, stop and ask them to help update this `.claude/CLAUDE.md` file with the missing guidance.

View File

@@ -39,7 +39,6 @@ These comments explain the scripts' behavior, which repositories get special han
## Important Notes
- **NEVER merge PRs autonomously** - always wait for the user to merge PRs themselves
- The `release_steps.py` script is idempotent - it's safe to rerun
- The `release_checklist.py` script is idempotent - it's safe to rerun
- Some repositories depend on others (e.g., mathlib4 depends on batteries, aesop, etc.)

1
.gitattributes vendored
View File

@@ -4,7 +4,6 @@ RELEASES.md merge=union
stage0/** binary linguist-generated
# The following file is often manually edited, so do show it in diffs
stage0/src/stdlib_flags.h -binary -linguist-generated
doc/std/grove/GroveStdlib/Generated/** linguist-generated
# These files should not have line endings translated on Windows, because
# it throws off parser tests. Later lines override earlier ones, so the
# runner code is still treated as ordinary text.

View File

@@ -9,7 +9,7 @@ assignees: ''
### Prerequisites
<!-- Please put an X between the brackets as you perform the following steps: -->
Please put an X between the brackets as you perform the following steps:
* [ ] Check that your issue is not already filed:
https://github.com/leanprover/lean4/issues

View File

@@ -220,7 +220,7 @@ jobs:
path: pack/*
- name: Lean stats
run: |
build/$TARGET_STAGE/bin/lean --stats src/Lean.lean
build/$TARGET_STAGE/bin/lean --stats src/Lean.lean -Dexperimental.module=true
if: ${{ !matrix.cross }}
- name: Test
id: test

View File

@@ -1,57 +0,0 @@
name: Check stdlib_flags.h modifications
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
check-stdlib-flags:
runs-on: ubuntu-latest
steps:
- name: Check if stdlib_flags.h was modified
uses: actions/github-script@v8
with:
script: |
// Get the list of files changed in this PR
const files = await github.paginate(
github.rest.pulls.listFiles,
{
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
}
);
// Check if stdlib_flags.h was modified
const stdlibFlagsModified = files.some(file =>
file.filename === 'src/stdlib_flags.h'
);
if (stdlibFlagsModified) {
console.log('src/stdlib_flags.h was modified in this PR');
// Check if the unlock label is present
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
const hasUnlockLabel = pr.labels.some(label =>
label.name === 'unlock-upstream-stdlib-flags'
);
if (!hasUnlockLabel) {
core.setFailed(
'src/stdlib_flags.h was modified. This is likely a mistake. If you would like to change ' +
'bootstrapping settings or request a stage0 update, you should modify stage0/src/stdlib_flags.h. ' +
'If you really want to change src/stdlib_flags.h (which should be extremely rare), set the ' +
'unlock-upstream-stdlib-flags label.'
);
} else {
console.log('Found unlock-upstream-stdlib-flags');
}
} else {
console.log('src/stdlib_flags.h was not modified');
}

View File

@@ -52,7 +52,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v5
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly' || (startsWith(github.ref, 'refs/tags/') && github.repository == 'leanprover/lean4')
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
- name: Set Nightly
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly'
id: set-nightly
@@ -106,54 +106,9 @@ jobs:
TAG_NAME="${GITHUB_REF##*/}"
echo "RELEASE_TAG=$TAG_NAME" >> "$GITHUB_OUTPUT"
- name: Validate CMakeLists.txt version matches tag
if: steps.set-release.outputs.RELEASE_TAG != ''
run: |
echo "Validating CMakeLists.txt version matches tag ${{ steps.set-release.outputs.RELEASE_TAG }}"
# Extract version values from CMakeLists.txt
CMAKE_MAJOR=$(grep -E "^set\(LEAN_VERSION_MAJOR " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_MINOR=$(grep -E "^set\(LEAN_VERSION_MINOR " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_PATCH=$(grep -E "^set\(LEAN_VERSION_PATCH " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | grep -oE '[0-9]+')
# Expected values from tag parsing
TAG_MAJOR="${{ steps.set-release.outputs.LEAN_VERSION_MAJOR }}"
TAG_MINOR="${{ steps.set-release.outputs.LEAN_VERSION_MINOR }}"
TAG_PATCH="${{ steps.set-release.outputs.LEAN_VERSION_PATCH }}"
ERRORS=""
if [[ "$CMAKE_MAJOR" != "$TAG_MAJOR" ]]; then
ERRORS+="LEAN_VERSION_MAJOR: expected $TAG_MAJOR, found $CMAKE_MAJOR\n"
fi
if [[ "$CMAKE_MINOR" != "$TAG_MINOR" ]]; then
ERRORS+="LEAN_VERSION_MINOR: expected $TAG_MINOR, found $CMAKE_MINOR\n"
fi
if [[ "$CMAKE_PATCH" != "$TAG_PATCH" ]]; then
ERRORS+="LEAN_VERSION_PATCH: expected $TAG_PATCH, found $CMAKE_PATCH\n"
fi
if [[ "$CMAKE_IS_RELEASE" != "1" ]]; then
ERRORS+="LEAN_VERSION_IS_RELEASE: expected 1, found $CMAKE_IS_RELEASE\n"
fi
if [[ -n "$ERRORS" ]]; then
echo "::error::Version mismatch between tag and src/CMakeLists.txt"
echo ""
echo "Tag ${{ steps.set-release.outputs.RELEASE_TAG }} expects version $TAG_MAJOR.$TAG_MINOR.$TAG_PATCH"
echo "But src/CMakeLists.txt has mismatched values:"
echo -e "$ERRORS"
echo ""
echo "Fix src/CMakeLists.txt, delete the tag, and re-tag."
exit 1
fi
echo "Version validation passed: $TAG_MAJOR.$TAG_MINOR.$TAG_PATCH"
# 0: PRs without special label
# 1: PRs with `merge-ci` label, merge queue checks, master commits
# 2: nightlies
# 3: PRs with `release-ci` label, full releases
# 2: PRs with `release-ci` label, releases (incl. nightlies)
- name: Set check level
id: set-level
# We do not use github.event.pull_request.labels.*.name here because
@@ -163,16 +118,14 @@ jobs:
check_level=0
fast=false
if [[ -n "${{ steps.set-release.outputs.RELEASE_TAG }}" || -n "${{ steps.set-release-custom.outputs.RELEASE_TAG }}" ]]; then
check_level=3
elif [[ -n "${{ steps.set-nightly.outputs.nightly }}" ]]; then
if [[ -n "${{ steps.set-nightly.outputs.nightly }}" || -n "${{ steps.set-release.outputs.RELEASE_TAG }}" || -n "${{ steps.set-release-custom.outputs.RELEASE_TAG }}" ]]; then
check_level=2
elif [[ "${{ github.event_name }}" != "pull_request" ]]; then
check_level=1
else
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }} --jq '.labels')"
if echo "$labels" | grep -q "release-ci"; then
check_level=3
check_level=2
elif echo "$labels" | grep -q "merge-ci"; then
check_level=1
fi
@@ -257,25 +210,17 @@ jobs:
"test": true,
"CMAKE_PRESET": "reldebug",
},
{
// TODO: suddenly started failing in CI
/*{
"name": "Linux fsanitize",
// Always run on large if available, more reliable regarding timeouts
"os": large ? "nscloud-ubuntu-22.04-amd64-8x16-with-cache" : "ubuntu-latest",
"os": "ubuntu-latest",
"enabled": level >= 2,
// do not fail nightlies on this for now
"secondary": level <= 2,
"test": true,
// turn off custom allocator & symbolic functions to make LSAN do its magic
"CMAKE_PRESET": "sanitize",
// `StackOverflow*` correctly triggers ubsan.
// `reverse-ffi` fails to link in sanitizers.
// `interactive` and `async_select_channel` fail nondeterministically, would need to
// be investigated..
// 9366 is too close to timeout.
// `bv_` sometimes times out calling into cadical even though we should be using the
// standard compile flags for it.
"CTEST_OPTIONS": "-E 'StackOverflow|reverse-ffi|interactive|async_select_channel|9366|run/bv_'"
},
// exclude seriously slow/problematic tests (laketests crash)
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
},*/
{
"name": "macOS",
"os": "macos-15-intel",
@@ -307,7 +252,7 @@ jobs:
},
{
"name": "Windows",
"os": large && (fast || level >= 2) ? "namespace-profile-windows-amd64-4x16" : "windows-2022",
"os": large && (fast || level == 2) ? "namespace-profile-windows-amd64-4x16" : "windows-2022",
"release": true,
"enabled": level >= 2,
"test": true,

View File

@@ -51,7 +51,7 @@ jobs:
- name: Fetch upstream invalidated facts
if: ${{ steps.should-run.outputs.should-run == 'true' && steps.workflow-info.outputs.pullRequestNumber != '' }}
id: fetch-upstream
uses: TwoFx/grove-action/fetch-upstream@v0.5
uses: TwoFx/grove-action/fetch-upstream@v0.4
with:
artifact-name: grove-invalidated-facts
base-ref: master
@@ -65,7 +65,6 @@ jobs:
workflow: ci.yml
path: artifacts
name: "build-Linux release"
allow_forks: true
name_is_regexp: true
- name: Unpack toolchain
@@ -96,7 +95,7 @@ jobs:
- name: Build
if: ${{ steps.should-run.outputs.should-run == 'true' }}
id: build
uses: TwoFx/grove-action/build@v0.5
uses: TwoFx/grove-action/build@v0.4
with:
project-path: doc/std/grove
script-name: grove-stdlib

View File

@@ -127,7 +127,7 @@ jobs:
description: "${{ github.repository_owner }}/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}-${{ env.SHORT_SHA }}",
});
- name: Add toolchain-available label
- name: Add label
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v8
with:
@@ -166,14 +166,22 @@ jobs:
if [ "$NIGHTLY_SHA" = "$MERGE_BASE_SHA" ]; then
echo "The merge base of this PR coincides with the nightly release"
BATTERIES_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/batteries.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
MATHLIB_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/mathlib4-nightly-testing.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
if [[ -n "$BATTERIES_REMOTE_TAGS" ]]; then
echo "... and Batteries has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE=""
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
else
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Mathlib CI should run now."
fi
else
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Mathlib CI should run now."
echo "... but Batteries does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
MESSAGE="- ❗ Batteries CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Batteries CI should run now."
fi
else
echo "The most recently nightly tag on this branch has SHA: $NIGHTLY_SHA"
@@ -507,18 +515,6 @@ jobs:
run: |
git push origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
- name: Add mathlib4-nightly-available label
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/github-script@v8
with:
script: |
await github.rest.issues.addLabels({
issue_number: ${{ steps.workflow-info.outputs.pullRequestNumber }},
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['mathlib4-nightly-available']
})
# We next automatically create a reference manual branch using this toolchain.
# Reference manual CI will be responsible for reporting back success or failure
# to the PR comments asynchronously (and thus transitively SubVerso/Verso).

View File

@@ -44,9 +44,7 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
set(CADICAL_CXX c++)
if (CADICAL_USE_CUSTOM_CXX)
set(CADICAL_CXX ${CMAKE_CXX_COMPILER})
# Use same platform flags as for Lean executables, in particular from `prepare-llvm-linux.sh`,
# but not Lean-specific `LEAN_EXTRA_CXX_FLAGS` such as fsanitize.
set(CADICAL_CXXFLAGS "${CMAKE_CXX_FLAGS}")
set(CADICAL_CXXFLAGS "${LEAN_EXTRA_CXX_FLAGS}")
set(CADICAL_LDFLAGS "-Wl,-rpath=\\$$ORIGIN/../lib")
endif()
find_program(CCACHE ccache)

View File

@@ -41,7 +41,7 @@
"SMALL_ALLOCATOR": "OFF",
"USE_MIMALLOC": "OFF",
"BSYMBOLIC": "OFF",
"LEAN_TEST_VARS": "MAIN_STACK_SIZE=16000 LSAN_OPTIONS=max_leaks=10"
"LEAN_TEST_VARS": "MAIN_STACK_SIZE=16000"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/sanitize"

View File

@@ -72,9 +72,6 @@ update the archived C source code of the stage 0 compiler in `stage0/src`.
The github repository will automatically update stage0 on `master` once
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync.
To trigger this, modify `stage0/src/stdlib_flags.h` (e.g., by adding or changing
a comment). When `update-stage0` runs, it will overwrite `stage0/src/stdlib_flags.h`
with the contents of `src/stdlib_flags.h`, bringing them back in sync.
NOTE: A full rebuild of stage 1 will only be triggered when the *committed* contents of `stage0/` are changed.
Thus if you change files in it manually instead of through `update-stage0-commit` (see below) or fetching updates from git, you either need to commit those changes first or run `make -C build/release clean-stdlib`.

View File

@@ -1,9 +1,189 @@
# Foreign Function Interface
The Lean FFI documentation is now part of the [Lean language reference](https://lean-lang.org/doc/reference/latest/).
NOTE: The current interface was designed for internal use in Lean and should be considered **unstable**.
It will be refined and extended in the future.
* [General FFI](https://lean-lang.org/doc/reference/latest/find/?domain=Verso.Genre.Manual.section&name=ffi)
* [Representation of inductive types](https://lean-lang.org/doc/reference/latest/find/?domain=Verso.Genre.Manual.section&name=inductive-types-ffi)
* [String](https://lean-lang.org/doc/reference/latest/find/?domain=Verso.Genre.Manual.section&name=string-ffi)
* [Array](https://lean-lang.org/doc/reference/latest/find/?domain=Verso.Genre.Manual.section&name=array-ffi)
As Lean is written partially in Lean itself and partially in C++, it offers efficient interoperability between the two languages (or rather, between Lean and any language supporting C interfaces).
This support is however currently limited to transferring Lean data types; in particular, it is not possible yet to pass or return compound data structures such as C `struct`s by value from or to Lean.
There are two primary attributes for interoperating with other languages:
* `@[extern "sym"] constant leanSym : ...` binds a Lean declaration to the external symbol `sym`.
It can also be used with `def` to provide an internal definition, but ensuring consistency of both definitions is up to the user.
* `@[export sym] def leanSym : ...` exports `leanSym` under the unmangled symbol name `sym`.
For simple examples of how to call foreign code from Lean and vice versa, see <https://github.com/leanprover/lean4/blob/master/src/lake/examples/ffi> and <https://github.com/leanprover/lean4/blob/master/src/lake/examples/reverse-ffi>, respectively.
## The Lean ABI
The Lean Application Binary Interface (ABI) describes how the signature of a Lean declaration is encoded as a native calling convention.
It is based on the standard C ABI and calling convention of the target platform.
For a Lean declaration marked with either `@[extern "sym"]` or `@[export sym]` for some symbol name `sym`, let `α₁ → ... → αₙ → β` be the normalized declaration's type.
If `n` is 0, the corresponding C declaration is
```c
extern s sym;
```
where `s` is the C translation of `β` as specified in the next section.
In the case of an `@[extern]` definition, the symbol's value is guaranteed to be initialized only after calling the Lean module's initializer or that of an importing module; see [Initialization](#initialization).
If `n` is greater than 0, the corresponding C declaration is
```c
s sym(t, ..., tₘ);
```
where the parameter types `tᵢ` are the C translation of the `αᵢ` as in the next section.
In the case of `@[extern]` all *irrelevant* types are removed first; see next section.
### Translating Types from Lean to C
* The integer types `UInt8`, ..., `UInt64`, `USize` are represented by the C types `uint8_t`, ..., `uint64_t`, `size_t`, respectively
* `Char` is represented by `uint32_t`
* `Float` is represented by `double`
* An *enum* inductive type of at least 2 and at most 2^32 constructors, each of which with no parameters, is represented by the first type of `uint8_t`, `uint16_t`, `uint32_t` that is sufficient to represent all constructor indices.
For example, the type `Bool` is represented as `uint8_t` with values `0` for `false` and `1` for `true`.
* `Decidable α` is represented the same way as `Bool`
* An inductive type with a *trivial structure*, that is,
* it is none of the types described above
* it is not marked `unsafe`
* it has a single constructor with a single parameter of *relevant* type
is represented by the representation of that parameter's type.
For example, `{ x : α // p }`, the `Subtype` structure of a value of type `α` and an irrelevant proof, is represented by the representation of `α`.
Similarly, the signed integer types `Int8`, ..., `Int64`, `ISize` are also represented by the unsigned C types `uint8_t`, ..., `uint64_t`, `size_t`, respectively, because they have a trivial structure.
* `Nat` and `Int` are represented by `lean_object *`.
Their runtime values is either a pointer to an opaque bignum object or, if the lowest bit of the "pointer" is 1 (`lean_is_scalar`), an encoded unboxed natural number or integer (`lean_box`/`lean_unbox`).
* A universe `Sort u`, type constructor `... → Sort u`, `Void α` or proposition `p : Prop` is *irrelevant* and is either statically erased (see above) or represented as a `lean_object *` with the runtime value `lean_box(0)`
* Any other type is represented by `lean_object *`.
Its runtime value is a pointer to an object of a subtype of `lean_object` (see the "Inductive types" section below) or the unboxed value `lean_box(cidx)` for the `cidx`th constructor of an inductive type if this constructor does not have any relevant parameters.
Example: the runtime value of `u : Unit` is always `lean_box(0)`.
#### Inductive types
For inductive types which are in the fallback `lean_object *` case above and not trivial constructors, the type is stored as a `lean_ctor_object`, and `lean_is_ctor` will return true. A `lean_ctor_object` stores the constructor index in the header, and the fields are stored in the `m_objs` portion of the object.
The memory order of the fields is derived from the types and order of the fields in the declaration. They are ordered as follows:
* Non-scalar fields stored as `lean_object *`
* Fields of type `USize`
* Other scalar fields, in decreasing order by size
Within each group the fields are ordered in declaration order. Trivial wrapper types count as their underlying wrapped type for this purpose.
* To access fields of the first kind, use `lean_ctor_get(val, i)` to get the `i`th non-scalar field.
* To access `USize` fields, use `lean_ctor_get_usize(val, n+i)` to get the `i`th usize field and `n` is the total number of fields of the first kind.
* To access other scalar fields, use `lean_ctor_get_uintN(val, off)` or `lean_ctor_get_usize(val, off)` as appropriate. Here `off` is the byte offset of the field in the structure, starting at `n*sizeof(void*)` where `n` is the number of fields of the first two kinds.
For example, a structure such as
```lean
structure S where
ptr_1 : Array Nat
usize_1 : USize
sc64_1 : UInt64
sc64_2 : { x : UInt64 // x > 0 } -- wrappers of scalars count as scalars
sc64_3 : Float -- `Float` is 64 bit
sc8_1 : Bool
sc16_1 : UInt16
sc8_2 : UInt8
sc64_4 : UInt64
usize_2 : USize
sc32_1 : Char -- trivial wrapper around `UInt32`
sc32_2 : UInt32
sc16_2 : UInt16
```
would get re-sorted into the following memory order:
* `S.ptr_1` - `lean_ctor_get(val, 0)`
* `S.usize_1` - `lean_ctor_get_usize(val, 1)`
* `S.usize_2` - `lean_ctor_get_usize(val, 2)`
* `S.sc64_1` - `lean_ctor_get_uint64(val, sizeof(void*)*3)`
* `S.sc64_2` - `lean_ctor_get_uint64(val, sizeof(void*)*3 + 8)`
* `S.sc64_3` - `lean_ctor_get_float(val, sizeof(void*)*3 + 16)`
* `S.sc64_4` - `lean_ctor_get_uint64(val, sizeof(void*)*3 + 24)`
* `S.sc32_1` - `lean_ctor_get_uint32(val, sizeof(void*)*3 + 32)`
* `S.sc32_2` - `lean_ctor_get_uint32(val, sizeof(void*)*3 + 36)`
* `S.sc16_1` - `lean_ctor_get_uint16(val, sizeof(void*)*3 + 40)`
* `S.sc16_2` - `lean_ctor_get_uint16(val, sizeof(void*)*3 + 42)`
* `S.sc8_1` - `lean_ctor_get_uint8(val, sizeof(void*)*3 + 44)`
* `S.sc8_2` - `lean_ctor_get_uint8(val, sizeof(void*)*3 + 45)`
### Borrowing
By default, all `lean_object *` parameters of an `@[extern]` function are considered *owned*, i.e. the external code is passed a "virtual RC token" and is responsible for passing this token along to another consuming function (exactly once) or freeing it via `lean_dec`.
To reduce reference counting overhead, parameters can be marked as *borrowed* by prefixing their type with `@&`.
Borrowed objects must only be passed to other non-consuming functions (arbitrarily often) or converted to owned values using `lean_inc`.
In `lean.h`, the `lean_object *` aliases `lean_obj_arg` and `b_lean_obj_arg` are used to mark this difference on the C side.
Return values and `@[export]` parameters are always owned at the moment.
## Initialization
When including Lean code as part of a larger program, modules must be *initialized* before accessing any of their declarations.
Module initialization entails
* initialization of all "constants" (nullary functions), including closed terms lifted out of other functions
* execution of all `[init]` functions
* execution of all `[builtin_init]` functions, if the `builtin` parameter of the module initializer has been set
The module initializer is automatically run with the `builtin` flag for executables compiled from Lean code and for "plugins" loaded with `lean --plugin`.
For all other modules imported by `lean`, the initializer is run without `builtin`.
Thus `[init]` functions are run iff their module is imported, regardless of whether they have native code available or not, while `[builtin_init]` functions are only run for native executable or plugins, regardless of whether their module is imported or not.
`lean` uses built-in initializers for e.g. registering basic parsers that should be available even without importing their module (which is necessary for bootstrapping).
The initializer for module `A.B` in a package `foo` is called `initialize_foo_A_B`. For modules in the Lean core (e.g., `Init.Prelude`), the initializer is called `initialize_Init_Prelude`. Module initializers will automatically initialize any imported modules. They are also idempotent (when run with the same `builtin` flag), but not thread-safe.
**Important for process-related functionality**: If your application needs to use process-related functions from libuv, such as `Std.Internal.IO.Process.getProcessTitle` and `Std.Internal.IO.Process.setProcessTitle`, you must call `lean_setup_args(argc, argv)` (which returns a potentially modified `argv` that must be used in place of the original) **before** calling `lean_initialize()` or `lean_initialize_runtime_module()`. This sets up process handling capabilities correctly, which is essential for certain system-level operations that Lean's runtime may depend on.
Together with initialization of the Lean runtime, you should execute code like the following exactly once before accessing any Lean declarations:
```c
void lean_initialize_runtime_module();
void lean_initialize();
char ** lean_setup_args(int argc, char ** argv);
lean_object * initialize_A_B(uint8_t builtin);
lean_object * initialize_C(uint8_t builtin);
...
argv = lean_setup_args(argc, argv); // if using process-related functionality
lean_initialize_runtime_module();
//lean_initialize(); // necessary (and replaces `lean_initialize_runtime_module`) if you (indirectly) access the `Lean` package
lean_object * res;
// use same default as for Lean executables
uint8_t builtin = 1;
res = initialize_A_B(builtin);
if (lean_io_result_is_ok(res)) {
lean_dec_ref(res);
} else {
lean_io_result_show_error(res);
lean_dec(res);
return ...; // do not access Lean declarations if initialization failed
}
res = initialize_C(builtin);
if (lean_io_result_is_ok(res)) {
...
//lean_init_task_manager(); // necessary if you (indirectly) use `Task`
lean_io_mark_end_initialization();
```
In addition, any other thread not spawned by the Lean runtime itself must be initialized for Lean use by calling
```c
void lean_initialize_thread();
```
and should be finalized in order to free all thread-local resources by calling
```c
void lean_finalize_thread();
```
## `@[extern]` in the Interpreter
The interpreter can run Lean declarations for which symbols are available in loaded shared libraries, which includes `@[extern]` declarations.
Thus to e.g. run `#eval` on such a declaration, you need to
1. compile (at least) the module containing the declaration and its dependencies into a shared library, and then
1. pass this library to `lean --load-dynlib=` to run code `import`ing this module.
Note that it is not sufficient to load the foreign library containing the external symbol because the interpreter depends on code that is emitted for each `@[extern]` declaration.
Thus it is not possible to interpret an `@[extern]` declaration in the same file.
See [`tests/compiler/foreign`](https://github.com/leanprover/lean4/tree/master/tests/compiler/foreign/) for an example.

View File

@@ -69,10 +69,6 @@ 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.
- `lean-fro.org`:
After updating the toolchains and running `lake update`, you must run `scripts/update.sh` to regenerate
the site content. This script updates generated files that depend on the Lean version.
The `release_steps.py` script handles this automatically.
- 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

View File

@@ -51,10 +51,6 @@ All these tests are included by [src/shell/CMakeLists.txt](https://github.com/le
codes and do not check the expected output even though output is
produced, it is ignored.
**Note:** Tests in this directory run with `-Dlinter.all=false` to reduce noise.
If your test needs to verify linter behavior (e.g., deprecation warnings),
explicitly enable the relevant linter with `set_option linter.<name> true`.
- [`tests/lean/interactive`](https://github.com/leanprover/lean4/tree/master/tests/lean/interactive/): are designed to test server requests at a
given position in the input file. Each .lean file contains comments
that indicate how to simulate a client request at that position.

View File

@@ -4,7 +4,6 @@ import GroveStdlib.Generated.«associative-creation-operations»
import GroveStdlib.Generated.«associative-modification-operations»
import GroveStdlib.Generated.«associative-create-then-query»
import GroveStdlib.Generated.«associative-all-operations-covered»
import GroveStdlib.Generated.«slice-producing»
/-
This file is autogenerated by grove. You can manually edit it, for example to resolve merge
@@ -21,4 +20,3 @@ def restoreState : RestoreStateM Unit := do
«associative-modification-operations».restoreState
«associative-create-then-query».restoreState
«associative-all-operations-covered».restoreState
«slice-producing».restoreState

View File

@@ -1,459 +0,0 @@
import Grove.Framework
/-
This file is autogenerated by grove. You can manually edit it, for example to resolve merge
conflicts, but be careful.
-/
open Grove.Framework Widget
namespace GroveStdlib.Generated.«slice-producing»
def «c8a13d6d-7ed6-4cd1-a386-23e2d55ce6f7» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "c8a13d6d-7ed6-4cd1-a386-23e2d55ce6f7"
rowId := "c8a13d6d-7ed6-4cd1-a386-23e2d55ce6f7"
rowState := #["String", "String.slice", Declaration.def {
name := `String.slice
renderedStatement := "String.slice (s : String) (startInclusive endExclusive : s.Pos)\n (h : startInclusive ≤ endExclusive) : String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.slice", Declaration.def {
name := `String.Slice.slice
renderedStatement := "String.Slice.slice (s : String.Slice) (newStart newEnd : s.Pos) (h : newStart ≤ newEnd) :\n String.Slice"
isDeprecated := false
}
,"string-pos-forwards", "String.Pos.slice", Declaration.def {
name := `String.Pos.slice
renderedStatement := "String.Pos.slice {s : String} (pos p₀ p₁ : s.Pos) (h₁ : p₀ ≤ pos) (h₂ : pos ≤ p₁) :\n (s.slice p₀ p₁ ⋯).Pos"
isDeprecated := false
}
,"string-pos-backwards", "String.Pos.ofSlice", Declaration.def {
name := `String.Pos.ofSlice
renderedStatement := "String.Pos.ofSlice {s : String} {p₀ p₁ : s.Pos} {h : p₀ ≤ p₁} (pos : (s.slice p₀ p₁ h).Pos) : s.Pos"
isDeprecated := false
}
,"string-slice-pos-forwards", "String.Slice.Pos.slice", Declaration.def {
name := `String.Slice.Pos.slice
renderedStatement := "String.Slice.Pos.slice {s : String.Slice} (pos p₀ p₁ : s.Pos) (h₁ : p₀ ≤ pos) (h₂ : pos ≤ p₁) :\n (s.slice p₀ p₁ ⋯).Pos"
isDeprecated := false
}
,"string-slice-pos-backwards", "String.Slice.Pos.ofSlice", Declaration.def {
name := `String.Slice.Pos.ofSlice
renderedStatement := "String.Slice.Pos.ofSlice {s : String.Slice} {p₀ p₁ : s.Pos} {h : p₀ ≤ p₁}\n (pos : (s.slice p₀ p₁ h).Pos) : s.Pos"
isDeprecated := false
}
,"string-pos-noproof", "String.Pos.sliceOrPanic", Declaration.def {
name := `String.Pos.sliceOrPanic
renderedStatement := "String.Pos.sliceOrPanic {s : String} (pos p₀ p₁ : s.Pos) {h : p₀ ≤ p₁} : (s.slice p₀ p₁ h).Pos"
isDeprecated := false
}
,"string-slice-pos-noproof", "String.Slice.Pos.sliceOrPanic", Declaration.def {
name := `String.Slice.Pos.sliceOrPanic
renderedStatement := "String.Slice.Pos.sliceOrPanic {s : String.Slice} (pos p₀ p₁ : s.Pos) {h : p₀ ≤ p₁} :\n (s.slice p₀ p₁ h).Pos"
isDeprecated := false
}
,]
metadata := {
status := .done
comment := ""
}
def «21b4fdfd-f8b3-44f5-a59e-57f1dc1d6819» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "21b4fdfd-f8b3-44f5-a59e-57f1dc1d6819"
rowId := "21b4fdfd-f8b3-44f5-a59e-57f1dc1d6819"
rowState := #["String", "String.slice?", Declaration.def {
name := `String.slice?
renderedStatement := "String.slice? (s : String) (startInclusive endExclusive : s.Pos) : Option String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.slice?", Declaration.def {
name := `String.Slice.slice?
renderedStatement := "String.Slice.slice? (s : String.Slice) (newStart newEnd : s.Pos) : Option String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .postponed
comment := "Would be good to have better support"
}
def «6f2b6ecb-2f0c-4e45-9da3-eb7f2e15eff0» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "6f2b6ecb-2f0c-4e45-9da3-eb7f2e15eff0"
rowId := "6f2b6ecb-2f0c-4e45-9da3-eb7f2e15eff0"
rowState := #["String", "String.slice!", Declaration.def {
name := `String.slice!
renderedStatement := "String.slice! (s : String) (p₁ p₂ : s.Pos) : String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.slice!", Declaration.def {
name := `String.Slice.slice!
renderedStatement := "String.Slice.slice! (s : String.Slice) (newStart newEnd : s.Pos) : String.Slice"
isDeprecated := false
}
,"string-pos-forwards", "String.Pos.slice!", Declaration.def {
name := `String.Pos.slice!
renderedStatement := "String.Pos.slice! {s : String} (pos p₀ p₁ : s.Pos) : (s.slice! p₀ p₁).Pos"
isDeprecated := false
}
,"string-pos-backwards", "String.Pos.ofSlice!", Declaration.def {
name := `String.Pos.ofSlice!
renderedStatement := "String.Pos.ofSlice! {s : String} {p₀ p₁ : s.Pos} (pos : (s.slice! p₀ p₁).Pos) : s.Pos"
isDeprecated := false
}
,"string-slice-pos-forwards", "String.Slice.Pos.slice!", Declaration.def {
name := `String.Slice.Pos.slice!
renderedStatement := "String.Slice.Pos.slice! {s : String.Slice} (pos p₀ p₁ : s.Pos) : (s.slice! p₀ p₁).Pos"
isDeprecated := false
}
,"string-slice-pos-backwards", "String.Slice.Pos.ofSlice!", Declaration.def {
name := `String.Slice.Pos.ofSlice!
renderedStatement := "String.Slice.Pos.ofSlice! {s : String.Slice} {p₀ p₁ : s.Pos} (pos : (s.slice! p₀ p₁).Pos) : s.Pos"
isDeprecated := false
}
,]
metadata := {
status := .done
comment := ""
}
def «a3bdf66d-bc11-4019-aee9-2f1c1701de52» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "a3bdf66d-bc11-4019-aee9-2f1c1701de52"
rowId := "a3bdf66d-bc11-4019-aee9-2f1c1701de52"
rowState := #["String", "String.trimAsciiStart", Declaration.def {
name := `String.trimAsciiStart
renderedStatement := "String.trimAsciiStart (s : String) : String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.trimAsciiStart", Declaration.def {
name := `String.Slice.trimAsciiStart
renderedStatement := "String.Slice.trimAsciiStart (s : String.Slice) : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing `of` version at least"
}
def «f12b2730-7a4d-465c-8a6d-9d051c300fd5» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "f12b2730-7a4d-465c-8a6d-9d051c300fd5"
rowId := "f12b2730-7a4d-465c-8a6d-9d051c300fd5"
rowState := #["String", "String.trimAsciiEnd", Declaration.def {
name := `String.trimAsciiEnd
renderedStatement := "String.trimAsciiEnd (s : String) : String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.trimAsciiEnd", Declaration.def {
name := `String.Slice.trimAsciiEnd
renderedStatement := "String.Slice.trimAsciiEnd (s : String.Slice) : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing `of` version at least"
}
def «32307b55-d6d1-4756-a947-dbe4dfde573c» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "32307b55-d6d1-4756-a947-dbe4dfde573c"
rowId := "32307b55-d6d1-4756-a947-dbe4dfde573c"
rowState := #["String", "String.trimAscii", Declaration.def {
name := `String.trimAscii
renderedStatement := "String.trimAscii (s : String) : String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.trimAscii", Declaration.def {
name := `String.Slice.trimAscii
renderedStatement := "String.Slice.trimAscii (s : String.Slice) : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing `of` version at least\n"
}
def «dce95a38-f55a-4d6a-ae79-078ffe4b5c15» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "dce95a38-f55a-4d6a-ae79-078ffe4b5c15"
rowId := "dce95a38-f55a-4d6a-ae79-078ffe4b5c15"
rowState := #["String", "String.toSlice", Declaration.def {
name := `String.toSlice
renderedStatement := "String.toSlice (s : String) : String.Slice"
isDeprecated := false
}
,"string-pos-forwards", "String.Pos.toSlice", Declaration.def {
name := `String.Pos.toSlice
renderedStatement := "String.Pos.toSlice {s : String} (pos : s.Pos) : s.toSlice.Pos"
isDeprecated := false
}
,"string-pos-backwards", "String.Pos.ofToSlice", Declaration.def {
name := `String.Pos.ofToSlice
renderedStatement := "String.Pos.ofToSlice {s : String} (pos : s.toSlice.Pos) : s.Pos"
isDeprecated := false
}
,]
metadata := {
status := .done
comment := ""
}
def «005a3f30-5dab-493f-b168-32c36a2bdf7c» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "005a3f30-5dab-493f-b168-32c36a2bdf7c"
rowId := "005a3f30-5dab-493f-b168-32c36a2bdf7c"
rowState := #["String.Slice", "String.Slice.str", Declaration.def {
name := `String.Slice.str
renderedStatement := "String.Slice.str (self : String.Slice) : String"
isDeprecated := false
}
,"string-slice-pos-forwards", "String.Slice.Pos.str", Declaration.def {
name := `String.Slice.Pos.str
renderedStatement := "String.Slice.Pos.str {s : String.Slice} (pos : s.Pos) : s.str.Pos"
isDeprecated := false
}
,"string-slice-pos-backwards", "String.Slice.Pos.ofStr", Declaration.def {
name := `String.Slice.Pos.ofStr
renderedStatement := "String.Slice.Pos.ofStr {s : String.Slice} (pos : s.str.Pos) (h₁ : s.startInclusive ≤ pos)\n (h₂ : pos ≤ s.endExclusive) : s.Pos"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing `no proof` version\n"
}
def «5f1a154c-ae2f-43a1-9409-2ce95b163ef3» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "5f1a154c-ae2f-43a1-9409-2ce95b163ef3"
rowId := "5f1a154c-ae2f-43a1-9409-2ce95b163ef3"
rowState := #["String", "String.drop", Declaration.def {
name := `String.drop
renderedStatement := "String.drop (s : String) (n : Nat) : String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.drop", Declaration.def {
name := `String.Slice.drop
renderedStatement := "String.Slice.drop (s : String.Slice) (n : Nat) : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing position transformations"
}
def «179518d1-ad07-4b2b-8ffe-3b7616e4c4ab» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "179518d1-ad07-4b2b-8ffe-3b7616e4c4ab"
rowId := "179518d1-ad07-4b2b-8ffe-3b7616e4c4ab"
rowState := #["String", "String.take", Declaration.def {
name := `String.take
renderedStatement := "String.take (s : String) (n : Nat) : String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.take", Declaration.def {
name := `String.Slice.take
renderedStatement := "String.Slice.take (s : String.Slice) (n : Nat) : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing position transformations"
}
def «55c587fd-a7a8-4633-a4ae-e2c4e768ad28» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "55c587fd-a7a8-4633-a4ae-e2c4e768ad28"
rowId := "55c587fd-a7a8-4633-a4ae-e2c4e768ad28"
rowState := #["String", "String.dropWhile", Declaration.def {
name := `String.dropWhile
renderedStatement := "String.dropWhile {ρ : Type} (s : String) (pat : ρ) [String.Slice.Pattern.ForwardPattern pat] :\n String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.dropWhile", Declaration.def {
name := `String.Slice.dropWhile
renderedStatement := "String.Slice.dropWhile {ρ : Type} (s : String.Slice) (pat : ρ)\n [String.Slice.Pattern.ForwardPattern pat] : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing position transformations"
}
def «d4444684-4279-4400-9be2-561a7cdb32c1» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "d4444684-4279-4400-9be2-561a7cdb32c1"
rowId := "d4444684-4279-4400-9be2-561a7cdb32c1"
rowState := #["String", "String.takeWhile", Declaration.def {
name := `String.takeWhile
renderedStatement := "String.takeWhile {ρ : Type} (s : String) (pat : ρ) [String.Slice.Pattern.ForwardPattern pat] :\n String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.takeWhile", Declaration.def {
name := `String.Slice.takeWhile
renderedStatement := "String.Slice.takeWhile {ρ : Type} (s : String.Slice) (pat : ρ)\n [String.Slice.Pattern.ForwardPattern pat] : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing position transformations"
}
def «1c9e6689-65a0-4d4b-b001-256e83917d98» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "1c9e6689-65a0-4d4b-b001-256e83917d98"
rowId := "1c9e6689-65a0-4d4b-b001-256e83917d98"
rowState := #["String", "String.dropEndWhile", Declaration.def {
name := `String.dropEndWhile
renderedStatement := "String.dropEndWhile {ρ : Type} (s : String) (pat : ρ) [String.Slice.Pattern.BackwardPattern pat] :\n String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.dropEndWhile", Declaration.def {
name := `String.Slice.dropEndWhile
renderedStatement := "String.Slice.dropEndWhile {ρ : Type} (s : String.Slice) (pat : ρ)\n [String.Slice.Pattern.BackwardPattern pat] : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing position transformations"
}
def «b836052b-3470-4a8e-8989-6951c898de37» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "b836052b-3470-4a8e-8989-6951c898de37"
rowId := "b836052b-3470-4a8e-8989-6951c898de37"
rowState := #["String", "String.takeEndWhile", Declaration.def {
name := `String.takeEndWhile
renderedStatement := "String.takeEndWhile {ρ : Type} (s : String) (pat : ρ) [String.Slice.Pattern.BackwardPattern pat] :\n String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.takeEndWhile", Declaration.def {
name := `String.Slice.takeEndWhile
renderedStatement := "String.Slice.takeEndWhile {ρ : Type} (s : String.Slice) (pat : ρ)\n [String.Slice.Pattern.BackwardPattern pat] : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing position transformations"
}
def «5aa777d8-9642-43d8-9e20-30400fb8bb9d» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "5aa777d8-9642-43d8-9e20-30400fb8bb9d"
rowId := "5aa777d8-9642-43d8-9e20-30400fb8bb9d"
rowState := #["String", "String.dropPrefix", Declaration.def {
name := `String.dropPrefix
renderedStatement := "String.dropPrefix {ρ : Type} (s : String) (pat : ρ) [String.Slice.Pattern.ForwardPattern pat] :\n String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.dropPrefix", Declaration.def {
name := `String.Slice.dropPrefix
renderedStatement := "String.Slice.dropPrefix {ρ : Type} (s : String.Slice) (pat : ρ)\n [String.Slice.Pattern.ForwardPattern pat] : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing position transformations"
}
def «80e3869d-fcfe-459d-8433-fe221f7b3c7a» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "80e3869d-fcfe-459d-8433-fe221f7b3c7a"
rowId := "80e3869d-fcfe-459d-8433-fe221f7b3c7a"
rowState := #["String", "String.dropSuffix", Declaration.def {
name := `String.dropSuffix
renderedStatement := "String.dropSuffix {ρ : Type} (s : String) (pat : ρ) [String.Slice.Pattern.BackwardPattern pat] :\n String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.dropSuffix", Declaration.def {
name := `String.Slice.dropSuffix
renderedStatement := "String.Slice.dropSuffix {ρ : Type} (s : String.Slice) (pat : ρ)\n [String.Slice.Pattern.BackwardPattern pat] : String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .bad
comment := "Missing position transformations"
}
def «4feda3e0-903b-4d52-b34e-0af70f7866e0» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "4feda3e0-903b-4d52-b34e-0af70f7866e0"
rowId := "4feda3e0-903b-4d52-b34e-0af70f7866e0"
rowState := #["String", "String.dropPrefix?", Declaration.def {
name := `String.dropPrefix?
renderedStatement := "String.dropPrefix? {ρ : Type} (s : String) (pat : ρ) [String.Slice.Pattern.ForwardPattern pat] :\n Option String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.dropPrefix?", Declaration.def {
name := `String.Slice.dropPrefix?
renderedStatement := "String.Slice.dropPrefix? {ρ : Type} (s : String.Slice) (pat : ρ)\n [String.Slice.Pattern.ForwardPattern pat] : Option String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .postponed
comment := "Missing position transformations"
}
def «45ca44c8-fbd5-4400-8297-a60778f302b0» : AssociationTable.Fact .declaration where
widgetId := "slice-producing"
factId := "45ca44c8-fbd5-4400-8297-a60778f302b0"
rowId := "45ca44c8-fbd5-4400-8297-a60778f302b0"
rowState := #["String", "String.dropSuffix?", Declaration.def {
name := `String.dropSuffix?
renderedStatement := "String.dropSuffix? {ρ : Type} (s : String) (pat : ρ) [String.Slice.Pattern.BackwardPattern pat] :\n Option String.Slice"
isDeprecated := false
}
,"String.Slice", "String.Slice.dropSuffix?", Declaration.def {
name := `String.Slice.dropSuffix?
renderedStatement := "String.Slice.dropSuffix? {ρ : Type} (s : String.Slice) (pat : ρ)\n [String.Slice.Pattern.BackwardPattern pat] : Option String.Slice"
isDeprecated := false
}
,]
metadata := {
status := .postponed
comment := "Missing position transformations"
}
def table : AssociationTable.Data .declaration where
widgetId := "slice-producing"
rows := #[
"c8a13d6d-7ed6-4cd1-a386-23e2d55ce6f7", "slice", #["String", "String.slice","String.Slice", "String.Slice.slice","string-pos-forwards", "String.Pos.slice","string-pos-backwards", "String.Pos.ofSlice","string-slice-pos-forwards", "String.Slice.Pos.slice","string-slice-pos-backwards", "String.Slice.Pos.ofSlice","string-pos-noproof", "String.Pos.sliceOrPanic","string-slice-pos-noproof", "String.Slice.Pos.sliceOrPanic",],
"21b4fdfd-f8b3-44f5-a59e-57f1dc1d6819", "slice?", #["String", "String.slice?","String.Slice", "String.Slice.slice?",],
"6f2b6ecb-2f0c-4e45-9da3-eb7f2e15eff0", "slice!", #["String", "String.slice!","String.Slice", "String.Slice.slice!","string-pos-forwards", "String.Pos.slice!","string-pos-backwards", "String.Pos.ofSlice!","string-slice-pos-forwards", "String.Slice.Pos.slice!","string-slice-pos-backwards", "String.Slice.Pos.ofSlice!",],
"a3bdf66d-bc11-4019-aee9-2f1c1701de52", "trimAsciiStart", #["String", "String.trimAsciiStart","String.Slice", "String.Slice.trimAsciiStart",],
"f12b2730-7a4d-465c-8a6d-9d051c300fd5", "trimAsciiEnd", #["String", "String.trimAsciiEnd","String.Slice", "String.Slice.trimAsciiEnd",],
"32307b55-d6d1-4756-a947-dbe4dfde573c", "trimAscii", #["String", "String.trimAscii","String.Slice", "String.Slice.trimAscii",],
"dce95a38-f55a-4d6a-ae79-078ffe4b5c15", "toSlice", #["String", "String.toSlice","string-pos-forwards", "String.Pos.toSlice","string-pos-backwards", "String.Pos.ofToSlice",],
"005a3f30-5dab-493f-b168-32c36a2bdf7c", "str", #["String.Slice", "String.Slice.str","string-slice-pos-forwards", "String.Slice.Pos.str","string-slice-pos-backwards", "String.Slice.Pos.ofStr",],
"5f1a154c-ae2f-43a1-9409-2ce95b163ef3", "drop", #["String", "String.drop","String.Slice", "String.Slice.drop",],
"179518d1-ad07-4b2b-8ffe-3b7616e4c4ab", "take", #["String", "String.take","String.Slice", "String.Slice.take",],
"55c587fd-a7a8-4633-a4ae-e2c4e768ad28", "dropWhile", #["String", "String.dropWhile","String.Slice", "String.Slice.dropWhile",],
"d4444684-4279-4400-9be2-561a7cdb32c1", "takeWhile", #["String", "String.takeWhile","String.Slice", "String.Slice.takeWhile",],
"1c9e6689-65a0-4d4b-b001-256e83917d98", "dropEndWhile", #["String", "String.dropEndWhile","String.Slice", "String.Slice.dropEndWhile",],
"b836052b-3470-4a8e-8989-6951c898de37", "takeEndWhile", #["String", "String.takeEndWhile","String.Slice", "String.Slice.takeEndWhile",],
"5aa777d8-9642-43d8-9e20-30400fb8bb9d", "dropPrefix", #["String", "String.dropPrefix","String.Slice", "String.Slice.dropPrefix",],
"80e3869d-fcfe-459d-8433-fe221f7b3c7a", "dropSuffix", #["String", "String.dropSuffix","String.Slice", "String.Slice.dropSuffix",],
"4feda3e0-903b-4d52-b34e-0af70f7866e0", "dropPrefix?", #["String", "String.dropPrefix?","String.Slice", "String.Slice.dropPrefix?",],
"45ca44c8-fbd5-4400-8297-a60778f302b0", "dropSuffix?", #["String", "String.dropSuffix?","String.Slice", "String.Slice.dropSuffix?",],
]
facts := #[
«c8a13d6d-7ed6-4cd1-a386-23e2d55ce6f7»,
«21b4fdfd-f8b3-44f5-a59e-57f1dc1d6819»,
«6f2b6ecb-2f0c-4e45-9da3-eb7f2e15eff0»,
«a3bdf66d-bc11-4019-aee9-2f1c1701de52»,
«f12b2730-7a4d-465c-8a6d-9d051c300fd5»,
«32307b55-d6d1-4756-a947-dbe4dfde573c»,
«dce95a38-f55a-4d6a-ae79-078ffe4b5c15»,
«005a3f30-5dab-493f-b168-32c36a2bdf7c»,
«5f1a154c-ae2f-43a1-9409-2ce95b163ef3»,
«179518d1-ad07-4b2b-8ffe-3b7616e4c4ab»,
«55c587fd-a7a8-4633-a4ae-e2c4e768ad28»,
«d4444684-4279-4400-9be2-561a7cdb32c1»,
«1c9e6689-65a0-4d4b-b001-256e83917d98»,
«b836052b-3470-4a8e-8989-6951c898de37»,
«5aa777d8-9642-43d8-9e20-30400fb8bb9d»,
«80e3869d-fcfe-459d-8433-fe221f7b3c7a»,
«4feda3e0-903b-4d52-b34e-0af70f7866e0»,
«45ca44c8-fbd5-4400-8297-a60778f302b0»,
]
def restoreState : RestoreStateM Unit := do
addAssociationTable table

View File

@@ -15,7 +15,7 @@ namespace GroveStdlib
namespace Std
def introduction : Node :=
.text "introduction", "Welcome to the interactive Lean standard library outline!"
.text "Welcome to the interactive Lean standard library outline!"
end Std

View File

@@ -11,87 +11,9 @@ namespace GroveStdlib.Std.CoreTypesAndOperations
namespace StringsAndFormatting
open Lean Meta
def introduction : Text where
id := "string-introduction"
content := Grove.Markdown.render [
.h1 "The Lean string library",
.text "The Lean standard library contains a fully-featured string library, centered around the types `String` and `String.Slice`.",
.text "`String` is defined as the subtype of `ByteArray` of valid UTF-8 strings. A `String.Slice` is a `String` together with a start and end position.",
.text "`String` is equivalent to `List Char`, but it has a more efficient runtime representation. While the logical model based on `ByteArray` is overwritten in the runtime, the runtime implementation is very similar to the logical model, with the main difference being that the length of a string in Unicode code points is cached in the runtime implementation.",
.text "We are considering removing this feature in the future (i.e., deprecating `String.length`), as the number of UTF-8 codepoints in a string is not particularly useful, and if needed it can be computed in linear time using `s.positions.count`."
]
def highLevelStringTypes : List Lean.Name :=
[`String, `String.Slice, `String.Pos, `String.Slice.Pos]
def creatingStringsAndSlices : Text where
id := "transforming-strings-and-slices"
content := Grove.Markdown.render [
.h2 "Transforming strings and slices",
.text "The Lean standard library contains a number of functions that take one or more strings and slices and return a string or a slice.",
.text "If possible, these functions should avoid allocating a new string, and return a slice of their input(s) instead.",
.text "Usually, for every operation `f`, there will be functions `String.f` and `String.Slice.f`, where `String.f s` is defined as `String.Slice.f s.toSlice`.",
.text "In particular, functions that transform strings and slices should live in the `String` and `String.Slice` namespaces even if they involve a `String.Pos`/`String.Slice.Pos` (like `String.sliceTo`), for reasons that will become clear shortly.",
.h3 "Transforming positions",
.text "Since positions on strings and slices are dependent on the string or slice, whenever users transform a string/slice, they will be interested in interpreting positions on the original string/slice as positions on the result, or vice versa.",
.text "Consequently, every operation that transforms a string or slice should come with a corresponding set of transformations between positions, usually in both directions, possibly with one of the directions being conditional.",
.text "For example, given a string `s` and a position `p` on `s`, we have the slice `s.sliceFrom p`, which is the slice from `p` to the end of `s`. A position on `s.sliceFrom p` can always be interpreted as a position on `s`. This is the \"backwards\" transformation. Conversely, a position `q` on `s` can be interpreted as a position on `s.sliceFrom p` as long as `p ≤ q`. This is the conditional forwards direction.",
.text "The convention for naming these transformations is that the forwards transformation should have the same name as the transformation on strings/slices, but it should be located in the `String.Pos` or `String.Slice.Pos` namespace, depending on the type of the starting position (so that dot notation is possible for the forward direction). The backwards transformation should have the same name as the operation on strings/slices, but with an `of` prefix, and live in the same namespace as the forwards transformation (so in general dot notation will not be available).",
.text "So, in the `sliceFrom` example, the forward direction would be called `String.Pos.sliceFrom`, while the backwards direction should be called `String.Pos.ofSliceFrom` (not `String.Slice.Pos.ofSliceFrom`).",
.text "If one of the directions is conditional, it should have a corresponding panicking operation that does not require a proof; in our example this would be `String.Pos.sliceFrom!`.",
.text "Sometimes there is a name clash for the panicking operations if the operation on strings is already panicking. For example, there are both `String.slice` and `String.slice!`. If the original operation is already panicking, we only provide panicking transformation operations. But now `String.Pos.slice!` could refer both to the panicking forwards transformation associated with `String.slice`, and also to the (only) forwards transformation associated with `String.slice!`. In this situation, we use an `orPanic` suffix to disambiguate. So the panicking forwards operation associated with `String.slice` is called `String.Pos.sliceOrPanic`, and the forwards operation associated with `String.slice!` is called `String.Pos.slice!`."
]
-- TODO: also include the `HAppend` instance(s)
def sliceProducing : AssociationTable (β := Alias Lean.Name) .declaration
[`String, `String.Slice,
Alias.mk `String.Pos "string-pos-forwards" "String.Pos (forwards)",
Alias.mk `String.Pos "string-pos-backwards" "String.Pos (backwards)",
Alias.mk `String.Pos "string-pos-noproof" "String.Pos (no proof)",
Alias.mk `String.Slice.Pos "string-slice-pos-forwards" "String.Slice.Pos (forwards)",
Alias.mk `String.Slice.Pos "string-slice-pos-backwards" "String.Slice.Pos (backwards)",
Alias.mk `String.Slice.Pos "string-slice-pos-noproof" "String.Slice.Pos (no proof)"] where
id := "slice-producing"
title := "String functions returning strings or slices"
description := "Operations on strings and string slices that themselves return a new string slice."
dataSources n := DataSource.definitionsInNamespace n.inner
def sliceProducingComplete : Assertion where
widgetId := "slice-producing-complete"
title := "Slice-producing table is complete"
description := "All functions in the `String.**` namespace that return a string or a slice are covered in the table"
check := do
let mut ans := #[]
let covered := Std.HashSet.ofArray ( valuesInAssociationTable sliceProducing)
let pred : DataSource.DeclarationPredicate :=
DataSource.DeclarationPredicate.all [.isDefinition, .not .isDeprecated,
.notInNamespace `String.Pos.Raw, .notInNamespace `String.Legacy,
.not .isInstance]
let env getEnv
for name in declarationsMatching `String pred do
let some c := env.find? name | continue
if c.type.getForallBody.getUsedConstants.any (fun n => n == ``String || n == ``String.Slice) then
let success : Bool := name.toString covered
ans := ans.push {
assertionId := name.toString
description := s!"`{name}` should appear in the table."
passed := success
message := s!"`{name}` was{if success then "" else " not"} found in the table."
}
return ans
end StringsAndFormatting
open StringsAndFormatting
def stringsAndFormatting : Node :=
.section "strings-and-formatting" "Strings and formatting"
#[.text introduction,
.text creatingStringsAndSlices,
.associationTable sliceProducing,
.assertion sliceProducingComplete]
.section "strings-and-formatting" "Strings and formatting" #[]
end GroveStdlib.Std.CoreTypesAndOperations
end GroveStdlib.Std.CoreTypesAndOperations

View File

@@ -5,7 +5,7 @@
"type": "git",
"subDir": "backend",
"scope": "",
"rev": "c580a425c9b7fa2aebaec2a1d8de16b2e2283c40",
"rev": "3e8aabdea58c11813c5d3b7eeb187ded44ee9a34",
"name": "grove",
"manifestFile": "lake-manifest.json",
"inputRev": "master",
@@ -15,10 +15,10 @@
"type": "git",
"subDir": null,
"scope": "leanprover",
"rev": "d9fc8ae23024be37424a189982c92356e37935c8",
"rev": "1604206fcd0462da9a241beeac0e2df471647435",
"name": "Cli",
"manifestFile": "lake-manifest.json",
"inputRev": "nightly-testing",
"inputRev": "main",
"inherited": true,
"configFile": "lakefile.toml"}],
"name": "grovestdlib",

View File

@@ -1,54 +0,0 @@
This release introduces the Lean module system, which allows files to
control the visibility of their contents for other files. In previous
releases, this feature was available as a preview when the option
`experimental.module` was set to `true`; it is now a fully supported
feature of Lean.
# Benefits
Because modules reduce the amount of information exposed to other
code, they speed up rebuilds because irrelevant changes can be
ignored, they make it possible to be deliberate about API evolution by
hiding details that may change from clients, they help proofs be
checked faster by avoiding accidentally unfolding definitions, and
they lead to smaller executable files through improved dead code
elimination.
# Visibility
A source file is a module if it begins with the `module` keyword. By
default, declarations in a module are private; the `public` modifier
exports them. Proofs of theorems and bodies of definitions are private
by default even when their signatures are public; the bodies of
definitions can be made public by adding the `@[expose]`
attribute. Theorems and opaque constants never expose their bodies.
`public section` and `@[expose] section` change the default visibility
of declarations in the section.
# Imports
Modules may only import other modules. By default, `import` adds the
public information of the imported module to the private scope of the
current module. Adding the `public` modifier to an import places the
imported modules's public information in the public scope of the
current module, exposing it in turn to the current module's clients.
Within a package, `import all` can be used to import another module's
private scope into the current module; this can be used to separate
lemmas or tests from definition modules without exposing details to
downstream clients.
# Meta Code
Code used in metaprograms must be marked `meta`. This ensures that the
code is compiled and available for execution when it is needed during
elaboration. Meta code may only reference other meta code. A whole
module can be made available in the meta phase using `meta import`;
this allows code to be shared across phases by importing the module in
each phase. Code that is reachable from public metaprograms must be
imported via `public meta import`, while local metaprograms can use
plain `meta import` for their dependencies.
The module system is described in detail in [the Lean language reference](https://lean-reference-manual-review.netlify.app/find/?domain=Verso.Genre.Manual.section&name=files).

View File

@@ -3,21 +3,16 @@ Copyright (c) 2023 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Sebastian Ullrich
-/
module
import Lean.Environment
import Lake.CLI.Main
import Lean.ExtraModUses
import Lake.CLI.Main
import Lean.Parser.Module
import Lake.Load.Workspace
/-! # Shake: A Lean import minimizer
/-! # `lake exe shake` command
This command will check the current project (or a specified target module) and all dependencies for
unused imports. This works by looking at generated `.olean` files to deduce required imports and
ensuring that every import is used to contribute some constant or other elaboration dependency
recorded by `recordExtraModUse` and friends.
recorded by `recordExtraModUse`. Because recompilation is not needed this is quite fast (about 8
seconds to check `Mathlib` and all dependencies).
-/
/-- help string for the command line interface -/
@@ -33,83 +28,13 @@ Options:
--force
Skips the `lake build --no-build` sanity check
--keep-implied
Preserves existing imports that are implied by other imports and thus not technically needed
anymore
--keep-prefix
If an import `X` would be replaced in favor of a more specific import `X.Y...` it implies,
preserves the original import instead. More generally, prefers inserting `import X` even if it
was not part of the original imports as long as it was in the original transitive import closure
of the current module.
--keep-public
Preserves all `public` imports to avoid breaking changes for external downstream modules
--add-public
Adds new imports as `public` if they have been in the original public closure of that module.
In other words, public imports will not be removed from a module unless they are unused even
in the private scope, and those that are removed will be re-added as `public` in downstream
modules even if only needed in the private scope there. Unlike `--keep-public`, this may
introduce breaking changes but will still limit the number of inserted imports.
--explain
Gives constants explaining why each module is needed
--fix
Apply the suggested fixes directly. Make sure you have a clean checkout
before running this, so you can review the changes.
--gh-style
Outputs messages that can be parsed by `gh-problem-matcher-wrap`
Annotations:
The following annotations can be added to Lean files in order to configure the behavior of
`shake`. Only the substring `shake: ` directly followed by a directive is checked for, so multiple
directives can be mixed in one line such as `-- shake: keep-downstream, shake: keep-all`, and they
can be surrounded by arbitrary comments such as `-- shake: keep (metaprogram output dependency)`.
* `module -- shake: keep-downstream`:
Preserves this module in all (current) downstream modules, adding new imports of it if needed.
* `module -- shake: keep-all`:
Preserves all existing imports in this module as is. New imports now needed because of upstream
changes may still be added.
* `import X -- shake: keep`:
Preserves this specific import in the current module. The most common use case is to preserve a
public import that will be needed in downstream modules to make sense of the output of a
metaprogram defined in this module. For example, if a tactic is defined that may synthesize a
reference to a theorem when run, there is no way for `shake` to detect this by itself and the
module of that theorem should be publicly imported and annotated with `keep` in the tactic's
module.
```
public import X -- shake: keep (metaprogram output dependency)
...
elab \"my_tactic\" : tactic => do
... mkConst ``f -- `f`, defined in `X`, may appear in the output of this tactic
```
"
open Lean
/-- The parsed CLI arguments. See `help` for more information -/
structure Args where
help : Bool := false
keepImplied : Bool := false
keepPrefix : Bool := false
keepPublic : Bool := false
addPublic : Bool := false
force : Bool := false
githubStyle : Bool := false
explain : Bool := false
trace : Bool := false
fix : Bool := false
/-- `<MODULE>..`: the list of root modules to check -/
mods : Array Name := #[]
/-- We use `Nat` as a bitset for doing efficient set operations.
The bit indexes will usually be a module index. -/
structure Bitset where
@@ -163,7 +88,7 @@ def ofImport : Lean.Import → NeedsKind
end NeedsKind
/-- Logically, a map `NeedsKind → Set ModuleIdx`, or `Set Import`. -/
/-- Logically, a map `NeedsKind → Bitset`. -/
structure Needs where
pub : Bitset
priv : Bitset
@@ -199,20 +124,6 @@ def Needs.union (needs : Needs) (k : NeedsKind) (s : Bitset) : Needs :=
def Needs.sub (needs : Needs) (k : NeedsKind) (s : Bitset) : Needs :=
needs.modify k (fun s' => s' ^^^ (s' s))
instance : Union Needs where
union a b := {
pub := a.pub b.pub
priv := a.priv b.priv
metaPub := a.metaPub b.metaPub
metaPriv := a.metaPriv b.metaPriv }
/-- The list of edits that will be applied in `--fix`. `edits[i] = (removed, added)` where:
* If `j ∈ removed` then we want to delete module named `j` from the imports of `i`
* If `j ∈ added` then we want to add module index `j` to the imports of `i`.
-/
abbrev Edits := Std.HashMap Name (Array Import × Array Import)
/-- The main state of the checker, containing information on all loaded modules. -/
structure State where
env : Environment
@@ -232,10 +143,6 @@ structure State where
changes to upstream headers.
-/
transDepsOrig : Array Needs := #[]
/-- Modules that should always be preserved downstream. -/
preserve : Needs := default
/-- Edits to be applied to the module imports. -/
edits : Edits := {}
def State.mods (s : State) := s.env.header.moduleData
def State.modNames (s : State) := s.env.header.moduleNames
@@ -278,38 +185,13 @@ def addTransitiveImps (transImps : Needs) (imp : Import) (j : Nat) (impTransImps
transImps
def isDeclMeta' (env : Environment) (declName : Name) : Bool :=
-- Matchers are not compiled by themselves but inlined by the compiler, so there is no IR decl
-- to be tagged as `meta`.
-- TODO: It would be better to base the entire `meta` inference on the IR only and consider module
-- references from any other context as compatible with both phases.
let inferFor :=
if declName.isStr && (declName.getString!.startsWith "match_" || declName.getString! == "_unsafe_rec") then declName.getPrefix else declName
-- `isMarkedMeta` knows about non-defs such as `meta structure`, isDeclMeta knows about decls
-- implicitly marked meta
isMarkedMeta env inferFor || isDeclMeta env inferFor
/--
Given an `Expr` reference, returns the declaration name that should be considered the reference, if
any.
-/
def getDepConstName? (env : Environment) (ref : Name) : Option Name := do
-- Ignore references to reserved names, they can be re-generated in-place
guard <| !isReservedName env ref
-- `_simp_...` constants are similar, use base decl instead
return if ref.isStr && ref.getString!.startsWith "_simp_" then
ref.getPrefix
else
ref
/-- Calculates the needs for a given module `mod` from constants and recorded extra uses. -/
def calcNeeds (s : State) (i : ModuleIdx) : Needs := Id.run do
let env := s.env
def calcNeeds (env : Environment) (i : ModuleIdx) : Needs := Id.run do
let mut needs := default
for ci in env.header.moduleData[i]!.constants do
-- Added guard for cases like `structure` that are still exported even if private
let pubCI? := guard (!isPrivateName ci.name) *> (env.setExporting true).find? ci.name
let k := { isExported := pubCI?.isSome, isMeta := isDeclMeta' env ci.name }
let k := { isExported := pubCI?.isSome, isMeta := isMeta env ci.name }
needs := visitExpr k ci.type needs
if let some e := ci.value? (allowOpaque := true) then
-- type and value has identical visibility under `meta`
@@ -324,19 +206,12 @@ def calcNeeds (s : State) (i : ModuleIdx) : Needs := Id.run do
return needs
where
/-- Accumulate the results from expression `e` into `deps`. -/
visitExpr (k : NeedsKind) (e : Expr) (deps : Needs) : Needs :=
let env := s.env
Lean.Expr.foldConsts e deps fun c deps => Id.run do
let mut deps := deps
if let some c := getDepConstName? env c then
if let some j := env.getModuleIdxFor? c then
let k := { k with isMeta := k.isMeta && !isDeclMeta' env c }
if j != i then
deps := deps.union k {j}
for indMod in (indirectModUseExt.getState env)[c]?.getD #[] do
if s.transDeps[i]!.has k indMod then
deps := deps.union k {indMod}
return deps
visitExpr (k : NeedsKind) e deps :=
Lean.Expr.foldConsts e deps fun c deps => match env.getModuleIdxFor? c with
| some j =>
let k := { k with isMeta := k.isMeta && !isMeta env c }
if j != i then deps.union k {j} else deps
| _ => deps
/--
Calculates the same as `calcNeeds` but tracing each module to a use-def declaration pair or
@@ -348,7 +223,7 @@ def getExplanations (env : Environment) (i : ModuleIdx) :
for ci in env.header.moduleData[i]!.constants do
-- Added guard for cases like `structure` that are still exported even if private
let pubCI? := guard (!isPrivateName ci.name) *> (env.setExporting true).find? ci.name
let k := { isExported := pubCI?.isSome, isMeta := isDeclMeta' env ci.name }
let k := { isExported := pubCI?.isSome, isMeta := isMeta env ci.name }
deps := visitExpr k ci.name ci.type deps
if let some e := ci.value? (allowOpaque := true) then
let k := if k.isMeta then k else
@@ -364,18 +239,18 @@ def getExplanations (env : Environment) (i : ModuleIdx) :
where
/-- Accumulate the results from expression `e` into `deps`. -/
visitExpr (k : NeedsKind) name e deps :=
Lean.Expr.foldConsts e deps fun c deps => Id.run do
let mut deps := deps
if let some c := getDepConstName? env c then
if let some j := env.getModuleIdxFor? c then
let k := { k with isMeta := k.isMeta && !isDeclMeta' env c }
if
if let some (some (name', _)) := deps[(j, k)]? then
decide (name.toString.length < name'.toString.length)
else true
then
deps := deps.insert (j, k) (name, c)
return deps
Lean.Expr.foldConsts e deps fun c deps => match env.getModuleIdxFor? c with
| some i =>
let k := { k with isMeta := k.isMeta && !isMeta env c }
if
if let some (some (name', _)) := deps[(i, k)]? then
decide (name.toString.length < name'.toString.length)
else true
then
deps.insert (i, k) (name, c)
else
deps
| _ => deps
partial def initStateFromEnv (env : Environment) : State := Id.run do
let mut s := { env }
@@ -391,6 +266,13 @@ partial def initStateFromEnv (env : Environment) : State := Id.run do
s := { s with transDepsOrig := s.transDeps }
return s
/-- The list of edits that will be applied in `--fix`. `edits[i] = (removed, added)` where:
* If `j ∈ removed` then we want to delete module named `j` from the imports of `i`
* If `j ∈ added` then we want to add module index `j` to the imports of `i`.
-/
abbrev Edits := Std.HashMap Name (Array Import × Array Import)
/-- Register that we want to remove `tgt` from the imports of `src`. -/
def Edits.remove (ed : Edits) (src : Name) (tgt : Import) : Edits :=
match ed.get? src with
@@ -409,8 +291,8 @@ Returns `(path, inputCtx, imports, endPos)` where `imports` is the `Lean.Parser.
and `endPos` is the position of the end of the header.
-/
def parseHeaderFromString (text path : String) :
IO (System.FilePath × (ictx : Parser.InputContext) ×
TSyntax ``Parser.Module.header × String.Pos ictx.fileMap.source) := do
IO (System.FilePath × Parser.InputContext ×
TSyntax ``Parser.Module.header × String.Pos.Raw) := do
let inputCtx := Parser.mkInputContext text path
let (header, parserState, msgs) Parser.parseHeader inputCtx
if !msgs.toList.isEmpty then -- skip this file if there are parse errors
@@ -418,8 +300,8 @@ def parseHeaderFromString (text path : String) :
throw <| .userError "parse errors in file"
-- the insertion point for `add` is the first newline after the imports
let insertion := header.raw.getTailPos?.getD parserState.pos
let insertion := inputCtx.fileMap.source.pos! insertion |>.find (· == '\n') |>.next!
pure path, inputCtx, header, insertion
let insertion := text.findAux (· == '\n') text.endPos insertion + '\n'
pure (path, inputCtx, header, insertion)
/-- Parse a source file to extract the location of the import lines, for edits and error messages.
@@ -427,8 +309,8 @@ Returns `(path, inputCtx, imports, endPos)` where `imports` is the `Lean.Parser.
and `endPos` is the position of the end of the header.
-/
def parseHeader (srcSearchPath : SearchPath) (mod : Name) :
IO (System.FilePath × (ictx : Parser.InputContext) ×
TSyntax ``Parser.Module.header × String.Pos ictx.fileMap.source) := do
IO (System.FilePath × Parser.InputContext ×
TSyntax ``Parser.Module.header × String.Pos.Raw) := do
-- Parse the input file
let some path srcSearchPath.findModuleWithExt "lean" mod
| throw <| .userError s!"error: failed to find source file for {mod}"
@@ -438,7 +320,7 @@ def parseHeader (srcSearchPath : SearchPath) (mod : Name) :
def decodeHeader : TSyntax ``Parser.Module.header Option (TSyntax `module) × Option (TSyntax `prelude) × TSyntaxArray ``Parser.Module.import
| `(Parser.Module.header| $[module%$moduleTk?]? $[prelude%$preludeTk?]? $imports*) =>
(moduleTk?.map .mk, preludeTk?.map .mk, imports)
| stx => panic! s!"unexpected header syntax {stx}"
| _ => unreachable!
def decodeImport : TSyntax ``Parser.Module.import Import
| `(Parser.Module.import| $[public%$pubTk?]? $[meta%$metaTk?]? import $[all%$allTk?]? $id) =>
@@ -447,174 +329,73 @@ def decodeImport : TSyntax ``Parser.Module.import → Import
/-- Analyze and report issues from module `i`. Arguments:
* `pkg`: the first component of the module name
* `srcSearchPath`: Used to find the path for error reporting purposes
* `i`: the module index
* `needs`: the module's calculated needs
* `pinned`: dependencies that should be preserved even if unused
* `edits`: accumulates the list of edits to apply if `--fix` is true
* `addOnly`: if true, only add missing imports, do not remove unused ones
-/
def visitModule (pkg : Name) (srcSearchPath : SearchPath)
(i : Nat) (needs : Needs) (headerStx : TSyntax ``Parser.Module.header) (args : Args)
(addOnly := false) : StateT State IO Unit := do
if isExtraRevModUse ( get).env i then
modify fun s => { s with preserve := s.preserve.union (if args.addPublic then .pub else .priv) {i} }
if args.trace then
IO.eprintln s!"Preserving `{(← get).modNames[i]!}` because of recorded extra rev use"
-- only process modules in the selected package
-- TODO: should be after `keep-downstream` but core headers are not found yet?
if !pkg.isPrefixOf ( get).modNames[i]! then
return
let (module?, prelude?, imports) := decodeHeader headerStx
if module?.any (·.raw.getTrailing?.any (·.toString.contains "shake: keep-downstream")) then
modify fun s => { s with preserve := s.preserve.union (if args.addPublic then .pub else .priv) {i} }
def visitModule (srcSearchPath : SearchPath)
(i : Nat) (needs : Needs) (preserve : Needs) (edits : Edits) (headerStx : TSyntax ``Parser.Module.header)
(addOnly := false) (githubStyle := false) (explain := false) : StateT State IO Edits := do
let s get
let addOnly := addOnly || module?.any (·.raw.getTrailing?.any (·.toString.contains "shake: keep-all"))
let mut deps := needs
-- Add additional preserved imports
for impStx in imports do
let imp := decodeImport impStx
let j := s.env.getModuleIdx? imp.module |>.get!
let k := NeedsKind.ofImport imp
if addOnly ||
args.keepPublic && imp.isExported ||
impStx.raw.getTrailing?.any (·.toString.contains "shake: keep") then
deps := deps.union k {j}
if args.trace then
IO.eprintln s!"Adding `{imp}` as additional dependency"
for j in [0:s.mods.size] do
for k in NeedsKind.all do
-- Remove `meta` while preserving, no use-case for preserving `meta` so far.
-- Downgrade to private unless `--add-public` is used.
if s.transDepsOrig[i]!.has k j &&
(s.preserve.has { k with isMeta := false, isExported := false } j ||
s.preserve.has { k with isMeta := false, isExported := true } j) then
deps := deps.union { k with isMeta := false, isExported := k.isExported && args.addPublic } {j}
-- Do transitive reduction of `needs` in `deps`.
if !addOnly then
for j in [0:s.mods.size] do
let transDeps := s.transDeps[j]!
for k in NeedsKind.all do
if deps.has k j then
let transDeps := addTransitiveImps .empty { k with module := .anonymous } j transDeps
for k' in NeedsKind.all do
deps := deps.sub k' (transDeps.sub k' {j} |>.get k')
let mut deps := needs
let (_, prelude?, imports) := decodeHeader headerStx
if prelude?.isNone then
deps := deps.union .pub {s.env.getModuleIdx? `Init |>.get!}
-- Accumulate `transDeps` which is the non-reflexive transitive closure of the still-live imports
let mut transDeps := Needs.empty
let mut alwaysAdd : Array Import := #[] -- to be added even if implied by other imports
for imp in s.mods[i]!.imports do
let j := s.env.getModuleIdx? imp.module |>.get!
let k := NeedsKind.ofImport imp
if deps.has k j || imp.importAll then
transDeps := addTransitiveImps transDeps imp j s.transDeps[j]!
for imp in imports do
if addOnly || imp.raw.getTrailing?.any (·.toString.toSlice.contains "shake: keep") then
let imp := decodeImport imp
let j := s.env.getModuleIdx? imp.module |>.get!
let k := NeedsKind.ofImport imp
deps := deps.union k {j}
-- skip folder-nested `public (meta)? import`s but remove `meta`
else if s.modNames[i]!.isPrefixOf imp.module then
let imp := { imp with isMeta := false }
let k := { k with isMeta := false }
if args.trace then
IO.eprintln s!"`{imp}` is preserved as folder-nested import"
transDeps := addTransitiveImps transDeps imp j s.transDeps[j]!
deps := deps.union k {j}
if !s.mods[i]!.imports.contains imp then
alwaysAdd := alwaysAdd.push imp
-- If `transDeps` does not cover `deps`, then we have to add back some imports until it does.
-- To minimize new imports we pick only new imports which are not transitively implied by
-- another new import, so we visit module indices in descending order.
let mut keptPrefix := false
let mut newTransDeps := transDeps
let mut toAdd : Array Import := #[]
for j in (0...s.mods.size).toArray.reverse do
for j in [0:s.mods.size] do
let transDeps := s.transDeps[j]!
for k in NeedsKind.all do
if deps.has k j && !newTransDeps.has k j && !newTransDeps.has { k with isExported := true } j then
-- `add-public/keep-prefix` may change the import and even module we're considering
let mut k := k
let mut imp : Import := { k with module := s.modNames[j]! }
let mut j := j
if args.trace then
IO.eprintln s!"`{imp}` is needed"
if args.addPublic && !k.isExported &&
-- also add as public if previously `public meta`, which could be from automatic porting
(s.transDepsOrig[i]!.has { k with isExported := true } j || s.transDepsOrig[i]!.has { k with isExported := true, isMeta := true } j) then
k := { k with isExported := true }
imp := { imp with isExported := true }
if args.trace then
IO.eprintln s!"* upgrading to `{imp}` because of `--add-public`"
if args.keepPrefix then
let rec tryPrefix : Name Option ModuleIdx
| .str p _ => tryPrefix p <|> (do
let j' s.env.getModuleIdx? p
-- `j'` must be reachable from `i` (allow downgrading from `meta`)
guard <| s.transDepsOrig[i]!.has k j' || s.transDepsOrig[i]!.has { k with isMeta := true } j'
let j'transDeps := addTransitiveImps .empty p j' s.transDeps[j']!
-- `j` must be reachable from `j'` (now downgrading must be done in the other direction)
guard <| j'transDeps.has k j || j'transDeps.has { k with isMeta := false } j
return j')
| _ => none
if let some j' := tryPrefix imp.module then
imp := { imp with module := s.modNames[j']! }
j := j'
keptPrefix := true
if args.trace then
IO.eprintln s!"* upgrading to `{imp}` because of `--keep-prefix`"
if !s.mods[i]!.imports.contains imp then
toAdd := toAdd.push imp
if s.transDepsOrig[i]!.has k j && preserve.has k j then
deps := deps.union k {j}
newTransDeps := addTransitiveImps newTransDeps imp j s.transDeps[j]!
if deps.has k j then
let transDeps := addTransitiveImps .empty { k with module := .anonymous } j transDeps
for k' in NeedsKind.all do
deps := deps.sub k' (transDeps.sub k' {j} |>.get k')
if keptPrefix then
-- if an import was replaced by `--keep-prefix`, we did not necessarily visit the modules in
-- dependency order anymore and so we have to redo the transitive closure checking
newTransDeps := transDeps
for j in (0...s.mods.size).toArray.reverse do
for k in NeedsKind.all do
if deps.has k j then
let mut imp : Import := { k with module := s.modNames[j]! }
if toAdd.contains imp && (newTransDeps.has k j || newTransDeps.has { k with isExported := true } j) then
if args.trace then
IO.eprintln s!"Removing `{imp}` from imports to be added because it is now implied"
toAdd := toAdd.erase imp
deps := deps.sub k {j}
else
newTransDeps := addTransitiveImps newTransDeps imp j s.transDeps[j]!
-- now that `toAdd` filtering is done, add `alwaysAdd`
toAdd := alwaysAdd ++ toAdd
-- Any import which is still not in `deps` was unused
-- Any import which is not in `transDeps` was unused.
-- Also accumulate `newDeps` which is the transitive closure of the remaining imports
let mut toRemove : Array Import := #[]
let mut newDeps := Needs.empty
for imp in s.mods[i]!.imports do
let j := s.env.getModuleIdx? imp.module |>.get!
let k := NeedsKind.ofImport imp
if args.keepImplied && newTransDeps.has k j then
if args.trace && !deps.has k j then
IO.eprintln s!"`{imp}` is implied by other imports"
else if !deps.has k j then
if args.trace then
IO.eprintln s!"`{imp}` is now unused"
toRemove := toRemove.push imp
-- A private import should also be removed if the public version has been added
else if !k.isExported && !imp.importAll && newTransDeps.has { k with isExported := true } j then
if args.trace then
IO.eprintln s!"`{imp}` is already covered by `{ { imp with isExported := true } }`"
toRemove := toRemove.push imp
if
-- skip folder-nested imports
s.modNames[i]!.isPrefixOf imp.module ||
imp.importAll then
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
else
let k := NeedsKind.ofImport imp
-- A private import should also be removed if the public version is needed
if !deps.has k j || !k.isExported && deps.has { k with isExported := true } j then
toRemove := toRemove.push imp
else
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
-- If `newDeps` does not cover `deps`, then we have to add back some imports until it does.
-- To minimize new imports we pick only new imports which are not transitively implied by
-- another new import
let mut toAdd : Array Import := #[]
for j in [0:s.mods.size] do
for k in NeedsKind.all do
if deps.has k j && !newDeps.has k j && !newDeps.has { k with isExported := true } j then
let imp := { k with module := s.modNames[j]! }
toAdd := toAdd.push imp
newDeps := addTransitiveImps newDeps imp j s.transDeps[j]!
-- mark and report the removals
modify fun s => { s with
edits := toRemove.foldl (init := s.edits) fun edits imp =>
edits.remove s.modNames[i]! imp }
let mut edits := toRemove.foldl (init := edits) fun edits imp =>
edits.remove s.modNames[i]! imp
if !toAdd.isEmpty || !toRemove.isEmpty || args.explain then
if !toAdd.isEmpty || !toRemove.isEmpty || explain then
if let some path srcSearchPath.findModuleWithExt "lean" s.modNames[i]! then
println! "{path}:"
else
@@ -623,9 +404,9 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
if !toRemove.isEmpty then
println! " remove {toRemove}"
if args.githubStyle then
if githubStyle then
try
let path, inputCtx, stx, endHeader parseHeader srcSearchPath s.modNames[i]!
let (path, inputCtx, stx, endHeader) parseHeader srcSearchPath s.modNames[i]!
let (_, _, imports) := decodeHeader stx
for stx in imports do
if toRemove.any fun imp => imp == decodeImport stx then
@@ -634,15 +415,14 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
(use `lake exe shake --fix` to fix this, or `lake exe shake --update` to ignore)"
if !toAdd.isEmpty then
-- we put the insert message on the beginning of the last import line
let pos := inputCtx.fileMap.toPosition endHeader.offset
let pos := inputCtx.fileMap.toPosition endHeader
println! "{path}:{pos.line-1}:1: warning: \
add {toAdd} instead"
catch _ => pure ()
-- mark and report the additions
modify fun s => { s with
edits := toAdd.foldl (init := s.edits) fun edits imp =>
edits.add s.modNames[i]! imp }
edits := toAdd.foldl (init := edits) fun edits imp =>
edits.add s.modNames[i]! imp
if !toAdd.isEmpty then
println! " add {toAdd}"
@@ -657,15 +437,14 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
let j := s.env.getModuleIdx? imp.module |>.get!
newTransDepsI := addTransitiveImps newTransDepsI imp j s.transDeps[j]!
modify fun s => { s with transDeps := s.transDeps.set! i newTransDepsI }
set { s with transDeps := s.transDeps.set! i newTransDepsI }
if args.explain then
if explain then
let explanation := getExplanations s.env i
let sanitize n := if n.hasMacroScopes then (sanitizeName n).run' { options := {} } else n
let run (imp : Import) := do
let j := s.env.getModuleIdx? imp.module |>.get!
let mut k := NeedsKind.ofImport imp
if let some exp? := explanation[(j, k)]? <|> guard args.addPublic *> explanation[(j, { k with isExported := false})]? then
if let some exp? := explanation[(j, NeedsKind.ofImport imp)]? then
println! " note: `{imp}` required"
if let some (n, c) := exp? then
println! " because `{sanitize n}` refers to `{sanitize c}`"
@@ -676,6 +455,8 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
run j
for i in toAdd do run i
return edits
/-- Convert a list of module names to a bitset of module indexes -/
def toBitset (s : State) (ns : List Name) : Bitset :=
ns.foldl (init := ) fun c name =>
@@ -683,26 +464,40 @@ def toBitset (s : State) (ns : List Name) : Bitset :=
| some i => c {i}
| none => c
/-- The parsed CLI arguments. See `help` for more information -/
structure Args where
/-- `--help`: shows the help -/
help : Bool := false
/-- `--force`: skips the `lake build --no-build` sanity check -/
force : Bool := false
/-- `--gh-style`: output messages that can be parsed by `gh-problem-matcher-wrap` -/
githubStyle : Bool := false
/-- `--explain`: give constants explaining why each module is needed -/
explain : Bool := false
/-- `--fix`: apply the fixes directly -/
fix : Bool := false
/-- `<MODULE>..`: the list of root modules to check -/
mods : Array Name := #[]
local instance : Ord Import where
compare :=
let _ := @lexOrd
compareOn fun imp => (!imp.isExported, imp.module.toString)
compare a b :=
if a.isExported && !b.isExported then
Ordering.lt
else if !a.isExported && b.isExported then
Ordering.gt
else
a.module.cmp b.module
/-- The main entry point. See `help` for more information on arguments. -/
public def main (args : List String) : IO UInt32 := do
def main (args : List String) : IO UInt32 := do
initSearchPath ( findSysroot)
-- Parse the arguments
let rec parseArgs (args : Args) : List String Args
| [] => args
| "--help" :: rest => parseArgs { args with help := true } rest
| "--keep-implied" :: rest => parseArgs { args with keepImplied := true } rest
| "--keep-prefix" :: rest => parseArgs { args with keepPrefix := true } rest
| "--keep-public" :: rest => parseArgs { args with keepPublic := true } rest
| "--add-public" :: rest => parseArgs { args with addPublic := true } rest
| "--force" :: rest => parseArgs { args with force := true } rest
| "--fix" :: rest => parseArgs { args with fix := true } rest
| "--explain" :: rest => parseArgs { args with explain := true } rest
| "--trace" :: rest => parseArgs { args with trace := true } rest
| "--gh-style" :: rest => parseArgs { args with githubStyle := true } rest
| "--" :: rest => { args with mods := args.mods ++ rest.map (·.toName) }
| other :: rest => parseArgs { args with mods := args.mods.push other.toName } rest
@@ -745,69 +540,69 @@ public def main (args : List String) : IO UInt32 := do
let imps := mods.map ({ module := · })
let (_, s) importModulesCore imps (isExported := true) |>.run
let s := s.markAllExported
let mut env finalizeImport s (isModule := true) imps {} (leakEnv := false) (loadExts := false)
-- the one env ext we want to initialize
let is := indirectModUseExt.toEnvExtension.getState env
let newState indirectModUseExt.addImportedFn is.importedEntries { env := env, opts := {} }
env := indirectModUseExt.toEnvExtension.setState (asyncMode := .sync) env { is with state := newState }
let env finalizeImport s (isModule := true) imps {} (leakEnv := false) (loadExts := false)
StateT.run' (s := initStateFromEnv env) do
let s get
-- Parse the config file
-- Run the calculation of the `needs` array in parallel
let needs := s.mods.mapIdx fun i _ =>
Task.spawn fun _ => calcNeeds s i
Task.spawn fun _ => calcNeeds s.env i
-- Parse headers in parallel
let headers s.mods.mapIdxM fun i _ =>
if !pkg.isPrefixOf s.modNames[i]! then
pure <| Task.pure <| .ok default, default, default, default
else
BaseIO.asTask (parseHeader srcSearchPath s.modNames[i]! |>.toBaseIO)
BaseIO.asTask (parseHeader srcSearchPath s.modNames[i]! |>.toBaseIO)
if args.fix then
println! "The following changes will be made automatically:"
-- Check all selected modules
let mut edits : Edits :=
let mut revNeeds : Needs := default
for i in [0:s.mods.size], t in needs, header in headers do
match header.get with
| .ok _, _, stx, _ =>
visitModule pkg srcSearchPath i t.get stx args
| .ok (_, _, stx, _) =>
edits visitModule (addOnly := !pkg.isPrefixOf s.modNames[i]!)
srcSearchPath i t.get revNeeds edits stx args.githubStyle args.explain
if isExtraRevModUse s.env i then
revNeeds := revNeeds.union .priv {i}
| .error e =>
println! e.toString
if !args.fix then
-- return error if any issues were found
return if ( get).edits.isEmpty then 0 else 1
return if edits.isEmpty then 0 else 1
-- Apply the edits to existing files
let mut count := 0
for mod in s.modNames, header? in headers do
let some (remove, add) := ( get).edits[mod]? | continue
let some (remove, add) := edits[mod]? | continue
let add : Array Import := add.qsortOrd
-- Parse the input file
let .ok path, inputCtx, stx, insertion := header?.get | continue
let .ok (path, inputCtx, stx, insertion) := header?.get | continue
let (_, _, imports) := decodeHeader stx
let text := inputCtx.fileMap.source
-- Calculate the edit result
let mut pos : String.Pos text := text.startPos
let mut pos : String.Pos.Raw := 0
let mut out : String := ""
let mut seen : Std.HashSet Import := {}
for stx in imports do
let mod := decodeImport stx
if remove.contains mod || seen.contains mod then
out := out ++ text.extract pos (text.pos! stx.raw.getPos?.get!)
out := out ++ text.extract pos stx.raw.getPos?.get!
-- We use the end position of the syntax, but include whitespace up to the first newline
pos := text.pos! stx.raw.getTailPos?.get! |>.find '\n' |>.next!
pos := text.findAux (· == '\n') text.rawEndPos stx.raw.getTailPos?.get! + '\n'
seen := seen.insert mod
out := out ++ text.extract pos insertion
for mod in add do
if !seen.contains mod then
seen := seen.insert mod
out := out ++ s!"{mod}\n"
out := out ++ text.extract insertion text.endPos
out := out ++ text.extract insertion text.rawEndPos
IO.FS.writeFile path out
count := count + 1

96
script/bench.sh Executable file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/env bash
set -euxo pipefail
cmake --preset release 1>&2
# We benchmark against stage2/bin to test new optimizations.
timeout -s KILL 1h time make -C build/release -j$(nproc) stage3 1>&2
export PATH=$PWD/build/release/stage2/bin:$PATH
# The extra opts used to be passed to the Makefile during benchmarking only but with Lake it is
# easier to configure them statically.
cmake -B build/release/stage3 -S src -DLEAN_EXTRA_LAKEFILE_TOML='weakLeanArgs=["-Dprofiler=true", "-Dprofiler.threshold=9999999", "--stats"]' 1>&2
(
cd tests/bench
timeout -s KILL 1h time temci exec --config speedcenter.yaml --in speedcenter.exec.velcom.yaml 1>&2
temci report run_output.yaml --reporter codespeed2
)
if [ -d .git ]; then
DIR="$(git rev-parse @)"
BASE_URL="https://speed.lean-lang.org/lean4-out/$DIR"
{
cat <<'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lakeprof Report</title>
</head>
<h1>Lakeprof Report</h1>
<button type="button" id="btn_fetch">View build trace in Perfetto</button>
<script type="text/javascript">
const ORIGIN = 'https://ui.perfetto.dev';
const btnFetch = document.getElementById('btn_fetch');
async function fetchAndOpen(traceUrl) {
const resp = await fetch(traceUrl);
// Error checking is left as an exercise to the reader.
const blob = await resp.blob();
const arrayBuffer = await blob.arrayBuffer();
openTrace(arrayBuffer, traceUrl);
}
function openTrace(arrayBuffer, traceUrl) {
const win = window.open(ORIGIN);
if (!win) {
btnFetch.style.background = '#f3ca63';
btnFetch.onclick = () => openTrace(arrayBuffer);
btnFetch.innerText = 'Popups blocked, click here to open the trace file';
return;
}
const timer = setInterval(() => win.postMessage('PING', ORIGIN), 50);
const onMessageHandler = (evt) => {
if (evt.data !== 'PONG') return;
// We got a PONG, the UI is ready.
window.clearInterval(timer);
window.removeEventListener('message', onMessageHandler);
const reopenUrl = new URL(location.href);
reopenUrl.hash = `#reopen=${traceUrl}`;
win.postMessage({
perfetto: {
buffer: arrayBuffer,
title: 'Lake Build Trace',
url: reopenUrl.toString(),
}}, ORIGIN);
};
window.addEventListener('message', onMessageHandler);
}
// This is triggered when following the link from the Perfetto UI's sidebar.
if (location.hash.startsWith('#reopen=')) {
const traceUrl = location.hash.substr(8);
fetchAndOpen(traceUrl);
}
EOF
cat <<EOF
btnFetch.onclick = () => fetchAndOpen("$BASE_URL/lakeprof.trace_event");
</script>
EOF
echo "<pre><code>"
(cd src; lakeprof report -prc)
echo "</code></pre>"
echo "</body></html>"
} | tee index.html
curl -T index.html $BASE_URL/index.html
curl -T src/lakeprof.log $BASE_URL/lakeprof.log
curl -T src/lakeprof.trace_event $BASE_URL/lakeprof.trace_event
fi

View File

@@ -10,16 +10,6 @@ Tests language server memory use by repeatedly re-elaborate a given file.
NOTE: only works on Linux for now.
-/
def determineRSS (pid : UInt32) : IO Nat := do
let status IO.FS.readFile s!"/proc/{pid}/smaps_rollup"
let some rssLine := status.splitOn "\n" |>.find? (·.startsWith "Rss:")
| throw <| IO.userError "No RSS in proc status"
let rssLine := rssLine.dropPrefix "Rss:"
let rssLine := rssLine.dropWhile Char.isWhitespace
let some rssInKB := rssLine.takeWhile Char.isDigit |>.toNat?
| throw <| IO.userError "Cannot parse RSS"
return rssInKB
def main (args : List String) : IO Unit := do
let leanCmd :: file :: iters :: args := args | panic! "usage: script <lean> <file> <#iterations> <server-args>..."
let file IO.FS.realPath file
@@ -44,14 +34,11 @@ def main (args : List String) : IO Unit := do
let text IO.FS.readFile file
let (_, headerEndPos, _) Elab.parseImports text
let headerEndPos := FileMap.ofString text |>.leanPosToLspPos headerEndPos
let n := iters.toNat!
let mut lastRSS? : Option Nat := none
let mut totalRSSDelta : Int := 0
let mut requestNo : Nat := 1
let mut versionNo : Nat := 1
Ipc.writeNotification "textDocument/didOpen", {
textDocument := { uri := uri, languageId := "lean", version := 1, text := text } : DidOpenTextDocumentParams }
for i in [0:n] do
for i in [0:iters.toNat!] do
if i > 0 then
versionNo := versionNo + 1
let params : DidChangeTextDocumentParams := {
@@ -74,16 +61,9 @@ def main (args : List String) : IO Unit := do
IO.eprintln diag.message
requestNo := requestNo + 1
let rss determineRSS ( read).pid
-- The first `didChange` usually results in a significantly higher RSS increase than
-- the others, so we ignore it.
if i > 1 then
if let some lastRSS := lastRSS? then
totalRSSDelta := totalRSSDelta + ((rss : Int) - (lastRSS : Int))
lastRSS? := some rss
let avgRSSDelta := totalRSSDelta / (n - 2)
IO.println s!"avg-reelab-rss-delta: {avgRSSDelta}"
let status IO.FS.readFile s!"/proc/{(← read).pid}/status"
for line in status.splitOn "\n" |>.filter (·.startsWith "RssAnon") do
IO.eprintln line
let _ Ipc.collectDiagnostics requestNo uri versionNo
( Ipc.stdin).writeLspMessage (Message.notification "exit" none)

View File

@@ -1,89 +0,0 @@
import Lean.Data.Lsp
import Lean.Elab.Import
open Lean
open Lean.Lsp
open Lean.JsonRpc
/-!
Tests watchdog memory use by repeatedly re-elaborate a given file.
NOTE: only works on Linux for now.
-/
def determineRSS (pid : UInt32) : IO Nat := do
let status IO.FS.readFile s!"/proc/{pid}/smaps_rollup"
let some rssLine := status.splitOn "\n" |>.find? (·.startsWith "Rss:")
| throw <| IO.userError "No RSS in proc status"
let rssLine := rssLine.dropPrefix "Rss:"
let rssLine := rssLine.dropWhile Char.isWhitespace
let some rssInKB := rssLine.takeWhile Char.isDigit |>.toNat?
| throw <| IO.userError "Cannot parse RSS"
return rssInKB
def main (args : List String) : IO Unit := do
let leanCmd :: file :: iters :: args := args | panic! "usage: script <lean> <file> <#iterations> <server-args>..."
let file IO.FS.realPath file
let uri := s!"file://{file}"
Ipc.runWith leanCmd (#["--server", "-DstderrAsMessages=false"] ++ args ++ #[uri]) do
let capabilities := {
textDocument? := some {
completion? := some {
completionItem? := some {
insertReplaceSupport? := true
}
}
}
}
Ipc.writeRequest 0, "initialize", { capabilities : InitializeParams }
discard <| Ipc.readResponseAs 0 InitializeResult
Ipc.writeNotification "initialized", InitializedParams.mk
let text IO.FS.readFile file
let (_, headerEndPos, _) Elab.parseImports text
let headerEndPos := FileMap.ofString text |>.leanPosToLspPos headerEndPos
let n := iters.toNat!
let mut lastRSS? : Option Nat := none
let mut totalRSSDelta : Int := 0
let mut requestNo : Nat := 1
let mut versionNo : Nat := 1
Ipc.writeNotification "textDocument/didOpen", {
textDocument := { uri := uri, languageId := "lean", version := 1, text := text } : DidOpenTextDocumentParams }
for i in [0:iters.toNat!] do
if i > 0 then
versionNo := versionNo + 1
let params : DidChangeTextDocumentParams := {
textDocument := {
uri := uri
version? := versionNo
}
contentChanges := #[TextDocumentContentChangeEvent.rangeChange {
start := headerEndPos
«end» := headerEndPos
} " "]
}
let params := toJson params
Ipc.writeNotification "textDocument/didChange", params
requestNo := requestNo + 1
let diags Ipc.collectDiagnostics requestNo uri versionNo
if let some diags := diags then
for diag in diags.param.diagnostics do
IO.eprintln diag.message
requestNo := requestNo + 1
Ipc.waitForILeans requestNo uri versionNo
let rss determineRSS ( read).pid
-- The first `didChange` usually results in a significantly higher RSS increase than
-- the others, so we ignore it.
if i > 1 then
if let some lastRSS := lastRSS? then
totalRSSDelta := totalRSSDelta + ((rss : Int) - (lastRSS : Int))
lastRSS? := some rss
let avgRSSDelta := totalRSSDelta / (n - 2)
IO.println s!"avg-reelab-rss-delta: {avgRSSDelta}"
let _ Ipc.collectDiagnostics requestNo uri versionNo
Ipc.shutdown requestNo
discard <| Ipc.waitForExit

View File

@@ -1,441 +0,0 @@
#!/usr/bin/env python3
"""
build_artifact.py: Download pre-built CI artifacts for a Lean commit.
Usage:
build_artifact.py # Download artifact for current HEAD
build_artifact.py --sha abc1234 # Download artifact for specific commit
build_artifact.py --clear-cache # Clear artifact cache
This script downloads pre-built binaries from GitHub Actions CI runs,
which is much faster than building from source (~30s vs 2-5min).
Artifacts are cached in ~/.cache/lean_build_artifact/ for reuse.
"""
import argparse
import json
import os
import platform
import shutil
import subprocess
import sys
import urllib.request
import urllib.error
from pathlib import Path
from typing import Optional
# Constants
GITHUB_API_BASE = "https://api.github.com"
LEAN4_REPO = "leanprover/lean4"
# CI artifact cache
CACHE_DIR = Path.home() / '.cache' / 'lean_build_artifact'
ARTIFACT_CACHE = CACHE_DIR
# Sentinel value indicating CI failed (don't bother building locally)
CI_FAILED = object()
# ANSI colors for terminal output
class Colors:
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
BOLD = '\033[1m'
RESET = '\033[0m'
def color(text: str, c: str) -> str:
"""Apply color to text if stdout is a tty."""
if sys.stdout.isatty():
return f"{c}{text}{Colors.RESET}"
return text
def error(msg: str) -> None:
"""Print error message and exit."""
print(color(f"Error: {msg}", Colors.RED), file=sys.stderr)
sys.exit(1)
def warn(msg: str) -> None:
"""Print warning message."""
print(color(f"Warning: {msg}", Colors.YELLOW), file=sys.stderr)
def info(msg: str) -> None:
"""Print info message."""
print(color(msg, Colors.BLUE), file=sys.stderr)
def success(msg: str) -> None:
"""Print success message."""
print(color(msg, Colors.GREEN), file=sys.stderr)
# -----------------------------------------------------------------------------
# Platform detection
# -----------------------------------------------------------------------------
def get_artifact_name() -> Optional[str]:
"""Get CI artifact name for current platform."""
system = platform.system()
machine = platform.machine()
if system == 'Darwin':
if machine == 'arm64':
return 'build-macOS aarch64'
return 'build-macOS' # Intel
elif system == 'Linux':
if machine == 'aarch64':
return 'build-Linux aarch64'
return 'build-Linux release'
# Windows not supported for CI artifact download
return None
# -----------------------------------------------------------------------------
# GitHub API helpers
# -----------------------------------------------------------------------------
_github_token_warning_shown = False
def get_github_token() -> Optional[str]:
"""Get GitHub token from environment or gh CLI."""
global _github_token_warning_shown
# Check environment variable first
token = os.environ.get('GITHUB_TOKEN')
if token:
return token
# Try to get token from gh CLI
try:
result = subprocess.run(
['gh', 'auth', 'token'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
except (FileNotFoundError, subprocess.TimeoutExpired):
pass
# Warn once if no token available
if not _github_token_warning_shown:
_github_token_warning_shown = True
warn("No GitHub authentication found. API rate limits may apply.")
warn("Run 'gh auth login' or set GITHUB_TOKEN to avoid rate limiting.")
return None
def github_api_request(url: str) -> dict:
"""Make a GitHub API request and return JSON response."""
headers = {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'build-artifact'
}
token = get_github_token()
if token:
headers['Authorization'] = f'token {token}'
req = urllib.request.Request(url, headers=headers)
try:
with urllib.request.urlopen(req, timeout=30) as response:
return json.loads(response.read().decode())
except urllib.error.HTTPError as e:
if e.code == 403:
error(f"GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable to increase limit.")
elif e.code == 404:
error(f"GitHub resource not found: {url}")
else:
error(f"GitHub API error: {e.code} {e.reason}")
except urllib.error.URLError as e:
error(f"Network error accessing GitHub API: {e.reason}")
# -----------------------------------------------------------------------------
# CI artifact cache functions
# -----------------------------------------------------------------------------
def get_cache_path(sha: str) -> Path:
"""Get cache directory for a commit's artifact."""
return ARTIFACT_CACHE / sha[:12]
def is_cached(sha: str) -> bool:
"""Check if artifact for this commit is already cached and valid."""
cache_path = get_cache_path(sha)
return cache_path.exists() and (cache_path / 'bin' / 'lean').exists()
def check_zstd_support() -> bool:
"""Check if tar supports zstd compression."""
try:
result = subprocess.run(
['tar', '--zstd', '--version'],
capture_output=True,
timeout=5
)
return result.returncode == 0
except (subprocess.TimeoutExpired, FileNotFoundError):
return False
def check_gh_available() -> bool:
"""Check if gh CLI is available and authenticated."""
try:
result = subprocess.run(
['gh', 'auth', 'status'],
capture_output=True,
timeout=10
)
return result.returncode == 0
except (subprocess.TimeoutExpired, FileNotFoundError):
return False
def download_ci_artifact(sha: str, quiet: bool = False):
"""
Try to download CI artifact for a commit.
Returns:
- Path to extracted toolchain directory if available
- CI_FAILED sentinel if CI run failed (don't bother building locally)
- None if no artifact available but local build might work
"""
# Check cache first
if is_cached(sha):
return get_cache_path(sha)
artifact_name = get_artifact_name()
if artifact_name is None:
return None # Unsupported platform
cache_path = get_cache_path(sha)
try:
# Query for CI workflow run for this commit, including status
# Note: Query parameters must be in the URL for GET requests
result = subprocess.run(
['gh', 'api', f'repos/{LEAN4_REPO}/actions/runs?head_sha={sha}&per_page=100',
'--jq', r'.workflow_runs[] | select(.name == "CI") | "\(.id) \(.conclusion // "null")"'],
capture_output=True,
text=True,
timeout=30
)
if result.returncode != 0 or not result.stdout.strip():
return None # No CI run found (old commit?)
# Parse "run_id conclusion" format
line = result.stdout.strip().split('\n')[0]
parts = line.split(' ', 1)
run_id = parts[0]
conclusion = parts[1] if len(parts) > 1 else "null"
# Check if the desired artifact exists for this run
result = subprocess.run(
['gh', 'api', f'repos/{LEAN4_REPO}/actions/runs/{run_id}/artifacts',
'--jq', f'.artifacts[] | select(.name == "{artifact_name}") | .id'],
capture_output=True,
text=True,
timeout=30
)
if result.returncode != 0 or not result.stdout.strip():
# No artifact available
# If CI failed and no artifact, the build itself likely failed - skip
if conclusion == "failure":
return CI_FAILED
# Otherwise (in progress, expired, etc.) - fall back to local build
return None
# Download artifact
cache_path.mkdir(parents=True, exist_ok=True)
if not quiet:
print("downloading CI artifact... ", end='', flush=True)
result = subprocess.run(
['gh', 'run', 'download', run_id,
'-n', artifact_name,
'-R', LEAN4_REPO,
'-D', str(cache_path)],
capture_output=True,
text=True,
timeout=600 # 10 minutes for large downloads
)
if result.returncode != 0:
shutil.rmtree(cache_path, ignore_errors=True)
return None
# Extract tar.zst - find the file (name varies by platform/version)
tar_files = list(cache_path.glob('*.tar.zst'))
if not tar_files:
shutil.rmtree(cache_path, ignore_errors=True)
return None
tar_file = tar_files[0]
if not quiet:
print("extracting... ", end='', flush=True)
result = subprocess.run(
['tar', '--zstd', '-xf', tar_file.name],
cwd=cache_path,
capture_output=True,
timeout=300
)
if result.returncode != 0:
shutil.rmtree(cache_path, ignore_errors=True)
return None
# Move contents up from lean-VERSION-PLATFORM/ to cache_path/
# The extracted directory name varies (e.g., lean-4.15.0-linux, lean-4.15.0-darwin_aarch64)
extracted_dirs = [d for d in cache_path.iterdir() if d.is_dir() and d.name.startswith('lean-')]
if extracted_dirs:
extracted = extracted_dirs[0]
for item in extracted.iterdir():
dest = cache_path / item.name
if dest.exists():
if dest.is_dir():
shutil.rmtree(dest)
else:
dest.unlink()
shutil.move(str(item), str(cache_path / item.name))
extracted.rmdir()
# Clean up tar file
tar_file.unlink()
# Verify the extraction worked
if not (cache_path / 'bin' / 'lean').exists():
shutil.rmtree(cache_path, ignore_errors=True)
return None
return cache_path
except (subprocess.TimeoutExpired, FileNotFoundError):
shutil.rmtree(cache_path, ignore_errors=True)
return None
# -----------------------------------------------------------------------------
# Git helpers
# -----------------------------------------------------------------------------
def get_current_commit() -> str:
"""Get the current git HEAD commit SHA."""
try:
result = subprocess.run(
['git', 'rev-parse', 'HEAD'],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
return result.stdout.strip()
error(f"Failed to get current commit: {result.stderr.strip()}")
except subprocess.TimeoutExpired:
error("Timeout getting current commit")
except FileNotFoundError:
error("git not found")
def resolve_sha(short_sha: str) -> str:
"""Resolve a (possibly short) SHA to full 40-character SHA using git rev-parse."""
if len(short_sha) == 40:
return short_sha
try:
result = subprocess.run(
['git', 'rev-parse', short_sha],
capture_output=True,
text=True,
timeout=5
)
if result.returncode == 0:
full_sha = result.stdout.strip()
if len(full_sha) == 40:
return full_sha
error(f"Cannot resolve SHA '{short_sha}': {result.stderr.strip() or 'not found in repository'}")
except subprocess.TimeoutExpired:
error(f"Timeout resolving SHA '{short_sha}'")
except FileNotFoundError:
error("git not found - required for SHA resolution")
# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(
description='Download pre-built CI artifacts for a Lean commit.',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
This script downloads pre-built binaries from GitHub Actions CI runs,
which is much faster than building from source (~30s vs 2-5min).
Artifacts are cached in ~/.cache/lean_build_artifact/ for reuse.
Examples:
build_artifact.py # Download for current HEAD
build_artifact.py --sha abc1234 # Download for specific commit
build_artifact.py --clear-cache # Clear cache to free disk space
"""
)
parser.add_argument('--sha', metavar='SHA',
help='Commit SHA to download artifact for (default: current HEAD)')
parser.add_argument('--clear-cache', action='store_true',
help='Clear artifact cache and exit')
parser.add_argument('--quiet', '-q', action='store_true',
help='Suppress progress messages (still prints result path)')
args = parser.parse_args()
# Handle cache clearing
if args.clear_cache:
if ARTIFACT_CACHE.exists():
size = sum(f.stat().st_size for f in ARTIFACT_CACHE.rglob('*') if f.is_file())
shutil.rmtree(ARTIFACT_CACHE)
info(f"Cleared cache at {ARTIFACT_CACHE} ({size / 1024 / 1024:.1f} MB)")
else:
info(f"Cache directory does not exist: {ARTIFACT_CACHE}")
return
# Get commit SHA
if args.sha:
sha = resolve_sha(args.sha)
else:
sha = get_current_commit()
if not args.quiet:
info(f"Commit: {sha[:12]}")
# Check prerequisites
if not check_gh_available():
error("gh CLI not available or not authenticated. Run 'gh auth login' first.")
if not check_zstd_support():
error("tar does not support zstd compression. Install zstd or a newer tar.")
artifact_name = get_artifact_name()
if artifact_name is None:
error(f"No CI artifacts available for this platform ({platform.system()} {platform.machine()})")
if not args.quiet:
info(f"Platform: {artifact_name}")
# Check cache
if is_cached(sha):
path = get_cache_path(sha)
if not args.quiet:
success("Using cached artifact")
print(path)
return
# Download artifact
result = download_ci_artifact(sha, quiet=args.quiet)
if result is CI_FAILED:
if not args.quiet:
print() # End the "downloading..." line
error(f"CI build failed for commit {sha[:12]}")
elif result is None:
if not args.quiet:
print() # End the "downloading..." line
error(f"No CI artifact available for commit {sha[:12]}")
else:
if not args.quiet:
print(color("done", Colors.GREEN))
print(result)
if __name__ == '__main__':
main()

View File

@@ -7,5 +7,3 @@ root = "Modulize"
[[lean_exe]]
name = "shake"
root = "Shake"
# needed by `Lake.loadWorkspace`
supportInterpreter = true

File diff suppressed because it is too large Load Diff

View File

@@ -1,307 +0,0 @@
/-
Copyright Strata Contributors
SPDX-License-Identifier: Apache-2.0 OR MIT
-/
namespace Strata
namespace Python
/-
Parser and translator for some basic regular expression patterns supported by
Python's `re` library
Ref.: https://docs.python.org/3/library/re.html
Also see
https://github.com/python/cpython/blob/759a048d4bea522fda2fe929be0fba1650c62b0e/Lib/re/_parser.py
for a reference implementation.
-/
-------------------------------------------------------------------------------
inductive ParseError where
/--
`patternError` is raised when Python's `re.patternError` exception is
raised.
[Reference: Python's re exceptions](https://docs.python.org/3/library/re.html#exceptions):
"Exception raised when a string passed to one of the functions here is not a
valid regular expression (for example, it might contain unmatched
parentheses) or when some other error occurs during compilation or matching.
It is never an error if a string contains no match for a pattern."
-/
| patternError (message : String) (pattern : String) (pos : String.Pos.Raw)
/--
`unimplemented` is raised whenever we don't support some regex operations
(e.g., lookahead assertions).
-/
| unimplemented (message : String) (pattern : String) (pos : String.Pos.Raw)
deriving Repr
def ParseError.toString : ParseError String
| .patternError msg pat pos => s!"Pattern error at position {pos.byteIdx}: {msg} in pattern '{pat}'"
| .unimplemented msg pat pos => s!"Unimplemented at position {pos.byteIdx}: {msg} in pattern '{pat}'"
instance : ToString ParseError where
toString := ParseError.toString
-------------------------------------------------------------------------------
/--
Regular Expression Nodes
-/
inductive RegexAST where
/-- Single literal character: `a` -/
| char : Char RegexAST
/-- Character range: `[a-z]` -/
| range : Char Char RegexAST
/-- Alternation: `a|b` -/
| union : RegexAST RegexAST RegexAST
/-- Concatenation: `ab` -/
| concat : RegexAST RegexAST RegexAST
/-- Any character: `.` -/
| anychar : RegexAST
/-- Zero or more: `a*` -/
| star : RegexAST RegexAST
/-- One or more: `a+` -/
| plus : RegexAST RegexAST
/-- Zero or one: `a?` -/
| optional : RegexAST RegexAST
/-- Bounded repetition: `a{n,m}` -/
| loop : RegexAST Nat Nat RegexAST
/-- Start of string: `^` -/
| anchor_start : RegexAST
/-- End of string: `$` -/
| anchor_end : RegexAST
/-- Grouping: `(abc)` -/
| group : RegexAST RegexAST
/-- Empty string: `()` or `""` -/
| empty : RegexAST
/-- Complement: `[^a-z]` -/
| complement : RegexAST RegexAST
deriving Inhabited, Repr
-------------------------------------------------------------------------------
/-- Parse character class like [a-z], [0-9], etc. into union of ranges and
chars. Note that this parses `|` as a character. -/
def parseCharClass (s : String) (pos : String.Pos.Raw) : Except ParseError (RegexAST × String.Pos.Raw) := do
if pos.get? s != some '[' then throw (.patternError "Expected '[' at start of character class" s pos)
let mut i := pos.next s
-- Check for complement (negation) with leading ^
let isComplement := !i.atEnd s && i.get? s == some '^'
if isComplement then
i := i.next s
let mut result : Option RegexAST := none
-- Process each element in the character class.
while !i.atEnd s && i.get? s != some ']' do
-- Uncommenting this makes the code stop
--dbg_trace "Working" (pure ())
let some c1 := i.get? s | throw (.patternError "Invalid character in class" s i)
let i1 := i.next s
-- Check for range pattern: c1-c2.
if !i1.atEnd s && i1.get? s == some '-' then
let i2 := i1.next s
if !i2.atEnd s && i2.get? s != some ']' then
let some c2 := i2.get? s | throw (.patternError "Invalid character in range" s i2)
if c1 > c2 then
throw (.patternError s!"Invalid character range [{c1}-{c2}]: \
start character '{c1}' is greater than end character '{c2}'" s i)
let r := RegexAST.range c1 c2
-- Union with previous elements.
result := some (match result with | none => r | some prev => RegexAST.union prev r)
i := i2.next s
continue
-- Single character.
let r := RegexAST.char c1
result := some (match result with | none => r | some prev => RegexAST.union prev r)
i := i.next s
let some ast := result | throw (.patternError "Unterminated character set" s pos)
let finalAst := if isComplement then RegexAST.complement ast else ast
pure (finalAst, i.next s)
-------------------------------------------------------------------------------
/-- Parse numeric repeats like `{10}` or `{1,10}` into min and max bounds. -/
def parseBounds (s : String) (pos : String.Pos.Raw) : Except ParseError (Nat × Nat × String.Pos.Raw) := do
if pos.get? s != some '{' then throw (.patternError "Expected '{' at start of bounds" s pos)
let mut i := pos.next s
let mut numStr := ""
-- Parse first number.
while !i.atEnd s && (i.get? s).any Char.isDigit do
numStr := numStr.push ((i.get? s).get!)
i := i.next s
let some n := numStr.toNat? | throw (.patternError "Invalid minimum bound" s pos)
-- Check for comma (range) or closing brace (exact count).
match i.get? s with
| some '}' => pure (n, n, i.next s) -- {n} means exactly n times.
| some ',' =>
i := i.next s
-- Parse maximum bound
numStr := ""
while !i.atEnd s && (i.get? s).any Char.isDigit do
numStr := numStr.push ((i.get? s).get!)
i := i.next s
let some max := numStr.toNat? | throw (.patternError "Invalid maximum bound" s i)
if i.get? s != some '}' then throw (.patternError "Expected '}' at end of bounds" s i)
-- Validate bounds order
if max < n then
throw (.patternError s!"Invalid repeat bounds \{{n},{max}}: \
maximum {max} is less than minimum {n}" s pos)
pure (n, max, i.next s)
| _ => throw (.patternError "Invalid bounds syntax" s i)
-------------------------------------------------------------------------------
mutual
/--
Parse atom: single element (char, class, anchor, group) with optional
quantifier. Stops at the first `|`.
-/
partial def parseAtom (s : String) (pos : String.Pos.Raw) : Except ParseError (RegexAST × String.Pos.Raw) := do
if pos.atEnd s then throw (.patternError "Unexpected end of regex" s pos)
let some c := pos.get? s | throw (.patternError "Invalid position" s pos)
-- Detect invalid quantifier at start
if c == '*' || c == '+' || c == '{' || c == '?' then
throw (.patternError s!"Quantifier '{c}' at position {pos} has nothing to quantify" s pos)
-- Detect unbalanced closing parenthesis
if c == ')' then
throw (.patternError "Unbalanced parenthesis" s pos)
-- Parse base element (anchor, char class, group, anychar, escape, or single char).
let (base, nextPos) match c with
| '^' => pure (RegexAST.anchor_start, pos.next s)
| '$' => pure (RegexAST.anchor_end, pos.next s)
| '[' => parseCharClass s pos
| '(' => parseExplicitGroup s pos
| '.' => pure (RegexAST.anychar, pos.next s)
| '\\' =>
-- Handle escape sequence.
-- Note: Python uses a single backslash as an escape character, but Lean
-- strings need to escape that. After DDMification, we will see two
-- backslashes in Strata for every Python backslash.
let nextPos := pos.next s
if nextPos.atEnd s then throw (.patternError "Incomplete escape sequence at end of regex" s pos)
let some escapedChar := nextPos.get? s | throw (.patternError "Invalid escape position" s nextPos)
-- Check for special sequences (unsupported right now).
match escapedChar with
| 'A' | 'b' | 'B' | 'd' | 'D' | 's' | 'S' | 'w' | 'W' | 'z' | 'Z' =>
throw (.unimplemented s!"Special sequence \\{escapedChar} is not supported" s pos)
| 'a' | 'f' | 'n' | 'N' | 'r' | 't' | 'u' | 'U' | 'v' | 'x' =>
throw (.unimplemented s!"Escape sequence \\{escapedChar} is not supported" s pos)
| c =>
if c.isDigit then
throw (.unimplemented s!"Backreference \\{c} is not supported" s pos)
else
pure (RegexAST.char escapedChar, nextPos.next s)
| _ => pure (RegexAST.char c, pos.next s)
-- Check for numeric repeat suffix on base element (but not on anchors)
match base with
| .anchor_start | .anchor_end => pure (base, nextPos)
| _ =>
if !nextPos.atEnd s then
match nextPos.get? s with
| some '{' =>
let (min, max, finalPos) parseBounds s nextPos
pure (RegexAST.loop base min max, finalPos)
| some '*' =>
let afterStar := nextPos.next s
if !afterStar.atEnd s then
match afterStar.get? s with
| some '?' => throw (.unimplemented "Non-greedy quantifier *? is not supported" s nextPos)
| some '+' => throw (.unimplemented "Possessive quantifier *+ is not supported" s nextPos)
| _ => pure (RegexAST.star base, afterStar)
else pure (RegexAST.star base, afterStar)
| some '+' =>
let afterPlus := nextPos.next s
if !afterPlus.atEnd s then
match afterPlus.get? s with
| some '?' => throw (.unimplemented "Non-greedy quantifier +? is not supported" s nextPos)
| some '+' => throw (.unimplemented "Possessive quantifier ++ is not supported" s nextPos)
| _ => pure (RegexAST.plus base, afterPlus)
else pure (RegexAST.plus base, afterPlus)
| some '?' =>
let afterQuestion := nextPos.next s
if !afterQuestion.atEnd s then
match afterQuestion.get? s with
| some '?' => throw (.unimplemented "Non-greedy quantifier ?? is not supported" s nextPos)
| some '+' => throw (.unimplemented "Possessive quantifier ?+ is not supported" s nextPos)
| _ => pure (RegexAST.optional base, afterQuestion)
else pure (RegexAST.optional base, afterQuestion)
| _ => pure (base, nextPos)
else
pure (base, nextPos)
/-- Parse explicit group with parentheses. -/
partial def parseExplicitGroup (s : String) (pos : String.Pos.Raw) : Except ParseError (RegexAST × String.Pos.Raw) := do
if pos.get? s != some '(' then throw (.patternError "Expected '(' at start of group" s pos)
let mut i := pos.next s
-- Check for extension notation (?...
if !i.atEnd s && i.get? s == some '?' then
let i1 := i.next s
if !i1.atEnd s then
match i1.get? s with
| some '=' => throw (.unimplemented "Positive lookahead (?=...) is not supported" s pos)
| some '!' => throw (.unimplemented "Negative lookahead (?!...) is not supported" s pos)
| _ => throw (.unimplemented "Extension notation (?...) is not supported" s pos)
let (inner, finalPos) parseGroup s i (some ')')
pure (.group inner, finalPos)
/-- Parse group: handles alternation and concatenation at current scope. -/
partial def parseGroup (s : String) (pos : String.Pos.Raw) (endChar : Option Char) :
Except ParseError (RegexAST × String.Pos.Raw) := do
let mut alternatives : List (List RegexAST) := [[]]
let mut i := pos
-- Parse until end of string or `endChar`.
while !i.atEnd s && (endChar.isNone || i.get? s != endChar) do
if i.get? s == some '|' then
-- Push a new scope to `alternatives`.
alternatives := [] :: alternatives
i := i.next s
else
let (ast, nextPos) parseAtom s i
alternatives := match alternatives with
| [] => [[ast]]
| head :: tail => (ast :: head) :: tail
i := nextPos
-- Check for expected end character.
if let some ec := endChar then
if i.get? s != some ec then
throw (.patternError s!"Expected '{ec}'" s i)
i := i.next s
-- Build result: concatenate each alternative, then union them.
let concatAlts := alternatives.reverse.filterMap fun alt =>
match alt.reverse with
| [] => -- Empty regex.
some (.empty)
| [single] => some single
| head :: tail => some (tail.foldl RegexAST.concat head)
match concatAlts with
| [] => pure (.empty, i)
| [single] => pure (single, i)
| head :: tail => pure (tail.foldl RegexAST.union head, i)
end
/-- info: Except.ok (Strata.Python.RegexAST.range 'A' 'z', { byteIdx := 5 }) -/
#guard_msgs in
#eval parseCharClass "[A-z]" 0
-- Test code: Print done
#print "Done!"

View File

@@ -58,11 +58,7 @@ OPTIONS=()
# 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'"
# these should also be used for cadical, so do not use `LEAN_EXTRA_CXX_FLAGS` here
echo -n " -DCMAKE_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/include ${EXTRA_FLAGS:-}'"
# the above does not include linker flags which will be added below based on context, so skip the
# generic check by cmake
echo -n " -DCMAKE_C_COMPILER_WORKS=1 -DCMAKE_CXX_COMPILER_WORKS=1"
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/include ${EXTRA_FLAGS:-}'"
# use target compiler directly when not cross-compiling
if [[ -L llvm-host ]]; then
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang"

View File

@@ -31,8 +31,6 @@ What this script does:
- Ensures tags are merged into stable branches (for non-RC releases)
- Verifies bump branches exist and are configured correctly
- Special handling for ProofWidgets4 release tags
- For mathlib4: runs verify_version_tags.py to validate the release tag
(checks git/GitHub consistency, toolchain, elan, cache, and build)
3. Optionally automates missing steps (when not in --dry-run mode):
- Creates missing release tags using push_repo_release_tag.py
@@ -501,57 +499,6 @@ def check_proofwidgets4_release(repo_url, target_toolchain, github_token):
print(f" You will need to create and push a tag v0.0.{next_version}")
return False
def run_mathlib_verify_version_tags(toolchain, verbose=False):
"""Run mathlib4's verify_version_tags.py script to validate the release tag.
This clones mathlib4 to a temp directory and runs the verification script.
Returns True if verification passes, False otherwise.
"""
import tempfile
print(f" ... Running mathlib4 verify_version_tags.py {toolchain}")
with tempfile.TemporaryDirectory() as tmpdir:
# Clone mathlib4 (shallow clone is sufficient for running the script)
clone_result = subprocess.run(
['git', 'clone', '--depth', '1', 'https://github.com/leanprover-community/mathlib4.git', tmpdir],
capture_output=True,
text=True
)
if clone_result.returncode != 0:
print(f" ❌ Failed to clone mathlib4: {clone_result.stderr.strip()[:200]}")
return False
# Run the verification script
script_path = os.path.join(tmpdir, 'scripts', 'verify_version_tags.py')
if not os.path.exists(script_path):
print(f" ❌ verify_version_tags.py not found in mathlib4 (expected at scripts/verify_version_tags.py)")
return False
# Run from the mathlib4 directory so git operations work
result = subprocess.run(
['python3', script_path, toolchain],
cwd=tmpdir,
capture_output=True,
text=True,
timeout=900 # 15 minutes timeout for cache download etc.
)
# Print output with indentation
if result.stdout:
for line in result.stdout.strip().split('\n'):
print(f" {line}")
if result.stderr:
for line in result.stderr.strip().split('\n'):
print(f" {line}")
if result.returncode != 0:
print(f" ❌ mathlib4 verify_version_tags.py failed")
return False
print(f" ✅ mathlib4 verify_version_tags.py passed")
return True
def main():
parser = argparse.ArgumentParser(description="Check release status of Lean4 repositories")
parser.add_argument("toolchain", help="The toolchain version to check (e.g., v4.6.0)")
@@ -816,12 +763,6 @@ def main():
repo_status[name] = False
continue
# For mathlib4, run verify_version_tags.py to validate the release tag
if name == "mathlib4":
if not run_mathlib_verify_version_tags(toolchain, verbose):
repo_status[name] = False
continue
repo_status[name] = success
# Final check for lean4 master branch

View File

@@ -50,26 +50,12 @@ repositories:
dependencies:
- lean4-cli
- name: lean4-unicode-basic
url: https://github.com/fgdorais/lean4-unicode-basic
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: BibtexQuery
url: https://github.com/dupuisf/BibtexQuery
toolchain-tag: true
stable-branch: false
branch: master
dependencies: [lean4-unicode-basic]
- name: doc-gen4
url: https://github.com/leanprover/doc-gen4
toolchain-tag: true
stable-branch: false
branch: main
dependencies: [lean4-cli, BibtexQuery]
dependencies: [lean4-cli]
- name: reference-manual
url: https://github.com/leanprover/reference-manual
@@ -127,18 +113,10 @@ repositories:
dependencies:
- mathlib4
- name: verso-web-components
url: https://github.com/leanprover/verso-web-components
toolchain-tag: true
stable-branch: false
branch: main
dependencies:
- verso
- name: lean-fro.org
url: https://github.com/leanprover/lean-fro.org
toolchain-tag: false
stable-branch: false
branch: master
dependencies:
- verso-web-components
- verso

View File

@@ -23,7 +23,6 @@ What this script does:
- Special merging strategies for repositories with nightly-testing branches
- Safety checks for repositories using bump branches
- Custom build and test procedures
- lean-fro.org: runs scripts/update.sh to regenerate site content
6. Commits the changes with message "chore: bump toolchain to {version}"
@@ -413,14 +412,20 @@ def execute_release_steps(repo, version, config):
run_command("lake update", cwd=repo_path, stream_output=True)
print(blue("Running `lake update` in examples/hero..."))
run_command("lake update", cwd=repo_path / "examples" / "hero", stream_output=True)
# Run scripts/update.sh to regenerate content
print(blue("Running `scripts/update.sh` to regenerate content..."))
run_command("scripts/update.sh", cwd=repo_path, stream_output=True)
print(green("Content regenerated successfully"))
elif repo_name == "cslib":
print(blue("Updating lakefile.toml..."))
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)
print(blue("Updating docs/lakefile.toml..."))
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path / "docs")
# Update lean-toolchain in docs
print(blue("Updating docs/lean-toolchain..."))
docs_toolchain = repo_path / "docs" / "lean-toolchain"
with open(docs_toolchain, "w") as f:
f.write(f"leanprover/lean4:{version}\n")
print(green(f"Updated docs/lean-toolchain to leanprover/lean4:{version}"))
run_command("lake update", cwd=repo_path, stream_output=True)
elif dependencies:
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)

View File

@@ -10,7 +10,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 28)
set(LEAN_VERSION_MINOR 27)
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'")
@@ -42,7 +42,7 @@ if(LLD_PATH)
endif()
set(LEAN_EXTRA_LINKER_FLAGS ${LEAN_EXTRA_LINKER_FLAGS_DEFAULT} CACHE STRING "Additional flags used by the linker")
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler. Unlike `CMAKE_CXX_FLAGS`, these will not be used to build e.g. cadical.")
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler")
set(LEAN_TEST_VARS "LEAN_CC=${CMAKE_C_COMPILER}" CACHE STRING "Additional environment variables used when running tests")
if (NOT CMAKE_BUILD_TYPE)
@@ -191,7 +191,7 @@ endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
# Initialize CXXFLAGS.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LEAN_EXTRA_CXX_FLAGS} -DLEAN_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\" -DLEAN_EXPORTING")
set(CMAKE_CXX_FLAGS "${LEAN_EXTRA_CXX_FLAGS} -DLEAN_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\" -DLEAN_EXPORTING")
set(CMAKE_CXX_FLAGS_DEBUG "-DLEAN_DEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG")
@@ -448,8 +448,8 @@ if(LLVM AND ${STAGE} GREATER 0)
# - In particular, `host/bin/llvm-config` produces flags like `-Lllvm-host/lib/libLLVM`, while
# we need the path to be `-Lllvm/lib/libLLVM`. Thus, we perform this replacement here.
string(REPLACE "llvm-host" "llvm" LEANSHARED_LINKER_FLAGS ${LEANSHARED_LINKER_FLAGS})
string(REPLACE "llvm-host" "llvm" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
message(VERBOSE "leanshared linker flags: '${LEANSHARED_LINKER_FLAGS}' | lean extra cxx flags '${CMAKE_CXX_FLAGS}'")
string(REPLACE "llvm-host" "llvm" LEAN_EXTRA_CXX_FLAGS ${LEAN_EXTRA_CXX_FLAGS})
message(VERBOSE "leanshared linker flags: '${LEANSHARED_LINKER_FLAGS}' | lean extra cxx flags '${LEAN_EXTR_CXX_FLAGS}'")
endif()
# get rid of unused parts of C++ stdlib

View File

@@ -15,7 +15,6 @@ public import Init.RCases
public import Init.Core
public import Init.Control
public import Init.WF
public import Init.WFComputable
public import Init.WFTactics
public import Init.Data
public import Init.System

View File

@@ -205,5 +205,3 @@ export Classical (imp_iff_right_iff imp_and_neg_imp_iff and_or_imp not_imp)
/-- Show that an element extracted from `P : ∃ a, p a` using `P.choose` satisfies `p`. -/
theorem Exists.choose_spec {p : α Prop} (P : a, p a) : p P.choose := Classical.choose_spec P
grind_pattern Exists.choose_spec => P.choose

View File

@@ -116,7 +116,7 @@ On top of these instances this file defines several auxiliary type classes:
* `CoeOTC := CoeOut* Coe*`
* `CoeHTC := CoeHead? CoeOut* Coe*`
* `CoeHTCT := CoeHead? CoeOut* Coe* CoeTail?`
* `CoeT := CoeHead? CoeOut* Coe* CoeTail? | CoeDep`
* `CoeDep := CoeHead? CoeOut* Coe* CoeTail? | CoeDep`
-/

View File

@@ -16,4 +16,3 @@ public import Init.Control.Option
public import Init.Control.Lawful
public import Init.Control.StateCps
public import Init.Control.ExceptCps
public import Init.Control.MonadAttach

View File

@@ -25,7 +25,7 @@ instances are provided for the same type.
instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α where
forIn x b f := forIn' x b fun a _ => f a
@[simp] theorem forIn'_eq_forIn [d : Membership α ρ] [ForIn' m ρ α d] {β} (x : ρ) (b : β)
@[simp] theorem forIn'_eq_forIn [d : Membership α ρ] [ForIn' m ρ α d] {β} [Monad m] (x : ρ) (b : β)
(f : (a : α) a x β m (ForInStep β)) (g : (a : α) β m (ForInStep β))
(h : a m b, f a m b = g a b) :
forIn' x b f = forIn x b g := by
@@ -40,7 +40,7 @@ instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α
simp [h]
rfl
@[wf_preprocess] theorem forIn_eq_forIn' [d : Membership α ρ] [ForIn' m ρ α d] {β}
@[wf_preprocess] theorem forIn_eq_forIn' [d : Membership α ρ] [ForIn' m ρ α d] {β} [Monad m]
(x : ρ) (b : β) (f : (a : α) β m (ForInStep β)) :
forIn x b f = forIn' x b (fun x h => binderNameHint x f <| binderNameHint h () <| f x) := by
rfl
@@ -403,7 +403,7 @@ class ForM (m : Type u → Type v) (γ : Type w₁) (α : outParam (Type w₂))
/--
Runs the monadic action `f` on each element of the collection `coll`.
-/
forM (coll : γ) (f : α m PUnit) : m PUnit
forM [Monad m] (coll : γ) (f : α m PUnit) : m PUnit
export ForM (forM)

View File

@@ -25,12 +25,6 @@ instance [Repr ε] [Repr α] : Repr (Result ε σ α) where
| Result.error e _, prec => Repr.addAppParen ("EStateM.Result.error " ++ reprArg e) prec
| Result.ok a _, prec => Repr.addAppParen ("EStateM.Result.ok " ++ reprArg a) prec
instance : MonadAttach (EStateM ε σ) where
CanReturn x a := Exists fun s => Exists fun s' => x.run s = .ok a s'
attach x s := match h : x s with
| .ok a s' => .ok a, s, s', h s'
| .error e s' => .error e s'
end EStateM
namespace EStateM

View File

@@ -329,8 +329,3 @@ instance ExceptT.finally {m : Type u → Type v} {ε : Type u} [MonadFinally m]
| (.ok a, .ok b) => pure (.ok (a, b))
| (_, .error e) => pure (.error e) -- second error has precedence
| (.error e, _) => pure (.error e)
instance [Monad m] [MonadAttach m] : MonadAttach (ExceptT ε m) where
CanReturn x a := MonadAttach.CanReturn (m := m) x (.ok a)
attach x := show m (Except ε _) from
(fun a, h => match a with | .ok a => .ok a, h | .error e => .error e) <$> MonadAttach.attach (m := m) x

View File

@@ -75,13 +75,6 @@ instance [Monad m] : MonadLift m (ExceptCpsT σ m) where
instance [Inhabited ε] : Inhabited (ExceptCpsT ε m α) where
default := fun _ _ k₂ => k₂ default
/--
For continuation monads, it is not possible to provide a computable `MonadAttach` instance that
actually adds information about the return value. Therefore, this instance always attaches a proof
of `True`.
-/
instance : MonadAttach (ExceptCpsT ε m) := .trivial
@[simp] theorem run_pure [Monad m] : run (pure x : ExceptCpsT ε m α) = pure (Except.ok x) := rfl
@[simp] theorem run_lift {α ε : Type u} [Monad m] (x : m α) : run (ExceptCpsT.lift x : ExceptCpsT ε m α) = (x >>= fun a => pure (Except.ok a) : m (Except ε α)) := rfl

View File

@@ -9,7 +9,6 @@ module
prelude
public import Init.Core
public import Init.Control.MonadAttach
public section
@@ -68,15 +67,4 @@ instance [OfNat α n] : OfNat (Id α) n :=
instance {m : Type u Type v} [Pure m] : MonadLiftT Id m where
monadLift x := pure x.run
instance : MonadAttach Id where
CanReturn x a := x.run = a
attach x := pure x.run, rfl
instance : LawfulMonadAttach Id where
map_attach := rfl
canReturn_map_imp := by
intro _ _ x _ h
cases h
exact x.run.2
end Id

View File

@@ -10,4 +10,3 @@ public import Init.Control.Lawful.Basic
public import Init.Control.Lawful.Instances
public import Init.Control.Lawful.Lemmas
public import Init.Control.Lawful.MonadLift
public import Init.Control.Lawful.MonadAttach

View File

@@ -248,10 +248,10 @@ namespace Id
instance : LawfulMonad Id := by
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl
@[simp, grind =] theorem run_map (x : Id α) (f : α β) : (f <$> x).run = f x.run := rfl
@[simp, grind =] theorem run_bind (x : Id α) (f : α Id β) : (x >>= f).run = (f x.run).run := rfl
@[simp, grind =] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl
@[simp, grind =] theorem pure_run (a : Id α) : pure a.run = a := rfl
@[simp] theorem run_map (x : Id α) (f : α β) : (f <$> x).run = f x.run := rfl
@[simp] theorem run_bind (x : Id α) (f : α Id β) : (x >>= f).run = (f x.run).run := rfl
@[simp] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl
@[simp] theorem pure_run (a : Id α) : pure a.run = a := rfl
@[simp] theorem run_seqRight (x y : Id α) : (x *> y).run = y.run := rfl
@[simp] theorem run_seqLeft (x y : Id α) : (x <* y).run = x.run := rfl
@[simp] theorem run_seq (f : Id (α β)) (x : Id α) : (f <*> x).run = f.run x.run := rfl

View File

@@ -17,9 +17,6 @@ public section
open Function
@[simp, grind =] theorem monadMap_refl {m : Type _ Type _} {α} (f : {α}, m α m α) :
monadMap @f = @f α := rfl
/-! # ExceptT -/
namespace ExceptT
@@ -28,8 +25,6 @@ namespace ExceptT
simp [run] at h
assumption
@[simp, grind =] theorem run_mk (x : m (Except ε α)) : run (mk x : ExceptT ε m α) = x := rfl
@[simp, grind =] theorem run_pure [Monad m] (x : α) : run (pure x : ExceptT ε m α) = pure (Except.ok x) := rfl
@[simp, grind =] theorem run_lift [Monad.{u, v} m] (x : m α) : run (ExceptT.lift x : ExceptT ε m α) = (Except.ok <$> x : m (Except ε α)) := rfl
@@ -60,9 +55,6 @@ theorem run_bind [Monad m] (x : ExceptT ε m α) (f : α → ExceptT ε m β)
apply bind_congr
intro a; cases a <;> simp [Except.map]
@[simp, grind =] theorem run_monadMap [MonadFunctorT n m] (f : {β : Type u} n β n β) (x : ExceptT ε m α)
: (monadMap @f x : ExceptT ε m α).run = monadMap @f (x.run) := rfl
protected theorem seq_eq {α β ε : Type u} [Monad m] (mf : ExceptT ε m (α β)) (x : ExceptT ε m α) : mf <*> x = mf >>= fun f => f <$> x :=
rfl
@@ -105,22 +97,6 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (ExceptT ε m) where
simp only [ExceptT.instMonad, ExceptT.map, ExceptT.mk, throw, throwThe, MonadExceptOf.throw,
pure_bind]
/-! Note that the `MonadControl` instance for `ExceptT` is not monad-generic. -/
@[simp] theorem run_restoreM [Monad m] (x : stM m (ExceptT ε m) α) :
ExceptT.run (restoreM x) = pure x := rfl
@[simp] theorem run_liftWith [Monad m] (f : ({β : Type u} ExceptT ε m β m (stM m (ExceptT ε m) β)) m α) :
ExceptT.run (liftWith f) = Except.ok <$> (f fun x => x.run) :=
rfl
@[simp] theorem run_controlAt [Monad m] [LawfulMonad m] (f : ({β : Type u} ExceptT ε m β m (stM m (ExceptT ε m) β)) m (stM m (ExceptT ε m) α)) :
ExceptT.run (controlAt m f) = f fun x => x.run := by
simp [controlAt, run_bind, bind_map_left]
@[simp] theorem run_control [Monad m] [LawfulMonad m] (f : ({β : Type u} ExceptT ε m β m (stM m (ExceptT ε m) β)) m (stM m (ExceptT ε m) α)) :
ExceptT.run (control f) = f fun x => x.run := run_controlAt f
end ExceptT
/-! # Except -/
@@ -174,9 +150,6 @@ namespace OptionT
apply bind_congr
intro a; cases a <;> simp [OptionT.pure, OptionT.mk]
@[simp, grind =] theorem run_monadMap [MonadFunctorT n m] (f : {β : Type u} n β n β) (x : OptionT m α)
: (monadMap @f x : OptionT m α).run = monadMap @f (x.run) := rfl
protected theorem seq_eq {α β : Type u} [Monad m] (mf : OptionT m (α β)) (x : OptionT m α) : mf <*> x = mf >>= fun f => f <$> x :=
rfl
@@ -238,24 +211,6 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (OptionT m) where
(x <|> y).run = Option.elimM x.run y.run (fun x => pure (some x)) :=
bind_congr fun | some _ => by rfl | none => by rfl
/-! Note that the `MonadControl` instance for `OptionT` is not monad-generic. -/
@[simp] theorem run_restoreM [Monad m] (x : stM m (OptionT m) α) :
OptionT.run (restoreM x) = pure x := rfl
@[simp] theorem run_liftWith [Monad m] [LawfulMonad m] (f : ({β : Type u} OptionT m β m (stM m (OptionT m) β)) m α) :
OptionT.run (liftWith f) = Option.some <$> (f fun x => x.run) := by
dsimp [liftWith]
rw [ bind_pure_comp]
rfl
@[simp] theorem run_controlAt [Monad m] [LawfulMonad m] (f : ({β : Type u} OptionT m β m (stM m (OptionT m) β)) m (stM m (OptionT m) α)) :
OptionT.run (controlAt m f) = f fun x => x.run := by
simp [controlAt, Option.elimM, Option.elim]
@[simp] theorem run_control [Monad m] [LawfulMonad m] (f : ({β : Type u} OptionT m β m (stM m (OptionT m) β)) m (stM m (OptionT m) α)) :
OptionT.run (control f) = f fun x => x.run := run_controlAt f
end OptionT
/-! # Option -/
@@ -277,9 +232,6 @@ namespace ReaderT
simp [run] at h
exact funext h
@[simp, grind =] theorem run_mk (x : ρ m α) (ctx : ρ) : run (.mk x : ReaderT ρ m α) ctx = x ctx :=
rfl
@[simp, grind =] theorem run_pure [Monad m] (a : α) (ctx : ρ) : (pure a : ReaderT ρ m α).run ctx = pure a := rfl
@[simp, grind =] theorem run_bind [Monad m] (x : ReaderT ρ m α) (f : α ReaderT ρ m β) (ctx : ρ)
@@ -327,22 +279,6 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (ReaderT ρ m) where
pure_bind := by intros; apply ext; intros; simp
bind_assoc := by intros; apply ext; intros; simp
/-! Note that the `MonadControl` instance for `ReaderT` is not monad-generic. -/
@[simp] theorem run_restoreM [Monad m] (x : stM m (ReaderT ρ m) α) (ctx : ρ) :
ReaderT.run (restoreM x) ctx = pure x := rfl
@[simp] theorem run_liftWith [Monad m] (f : ({β : Type u} ReaderT ρ m β m (stM m (ReaderT ρ m) β)) m α) (ctx : ρ) :
ReaderT.run (liftWith f) ctx = (f fun x => x.run ctx) :=
rfl
@[simp] theorem run_controlAt [Monad m] [LawfulMonad m] (f : ({β : Type u} ReaderT ρ m β m (stM m (ReaderT ρ m) β)) m (stM m (ReaderT ρ m) α)) (ctx : ρ) :
ReaderT.run (controlAt m f) ctx = f fun x => x.run ctx := by
simp [controlAt]
@[simp] theorem run_control [Monad m] [LawfulMonad m] (f : ({β : Type u} ReaderT ρ m β m (stM m (ReaderT ρ m) β)) m (stM m (ReaderT ρ m) α)) (ctx : ρ) :
ReaderT.run (control f) ctx = f fun x => x.run ctx := run_controlAt f ctx
end ReaderT
/-! # StateRefT -/
@@ -357,20 +293,17 @@ namespace StateT
@[ext, grind ext] theorem ext {x y : StateT σ m α} (h : s, x.run s = y.run s) : x = y :=
funext h
@[simp, grind =] theorem run_mk [Monad m] (x : σ m (α × σ)) (s : σ) : run (.mk x) s = x s :=
rfl
@[simp, grind =] theorem run'_eq [Monad m] (x : StateT σ m α) (s : σ) : run' x s = (·.1) <$> run x s :=
rfl
@[simp, grind =] theorem run_pure [Monad m] (a : α) (s : σ) : (pure a : StateT σ m α).run s = pure (a, s) := rfl
@[simp, grind =] theorem run_bind [Monad m] (x : StateT σ m α) (f : α StateT σ m β) (s : σ)
: (x >>= f).run s = x.run s >>= λ p => (f p.1).run p.2 := rfl
: (x >>= f).run s = x.run s >>= λ p => (f p.1).run p.2 := by
simp [bind, StateT.bind, run]
@[simp, grind =] theorem run_map {α β σ : Type u} [Monad m] [LawfulMonad m] (f : α β) (x : StateT σ m α) (s : σ) : (f <$> x).run s = (fun (p : α × σ) => (f p.1, p.2)) <$> x.run s := by
rw [ bind_pure_comp (m := m)]
rfl
simp [Functor.map, StateT.map, run, bind_pure_comp]
@[simp, grind =] theorem run_get [Monad m] (s : σ) : (get : StateT σ m σ).run s = pure (s, s) := rfl
@@ -379,13 +312,13 @@ namespace StateT
@[simp, grind =] theorem run_modify [Monad m] (f : σ σ) (s : σ) : (modify f : StateT σ m PUnit).run s = pure (, f s) := rfl
@[simp, grind =] theorem run_modifyGet [Monad m] (f : σ α × σ) (s : σ) : (modifyGet f : StateT σ m α).run s = pure ((f s).1, (f s).2) := by
rfl
simp [modifyGet, MonadStateOf.modifyGet, StateT.modifyGet, run]
@[simp, grind =] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl
@[grind =]
theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
simp
simp [StateT.lift, StateT.run, bind, StateT.bind]
@[simp, grind =] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
@@ -425,48 +358,10 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
pure_bind := by intros; apply ext; intros; simp
bind_assoc := by intros; apply ext; intros; simp
/-! Note that the `MonadControl` instance for `StateT` is not monad-generic. -/
@[simp] theorem run_restoreM [Monad m] [LawfulMonad m] (x : stM m (StateT σ m) α) (s : σ) :
StateT.run (restoreM x) s = pure x := by
simp [restoreM, MonadControl.restoreM]
rfl
@[simp] theorem run_liftWith [Monad m] [LawfulMonad m] (f : ({β : Type u} StateT σ m β m (stM m (StateT σ m) β)) m α) (s : σ) :
StateT.run (liftWith f) s = ((·, s) <$> f fun x => x.run s) := by
simp [liftWith, MonadControl.liftWith, Function.comp_def]
@[simp] theorem run_controlAt [Monad m] [LawfulMonad m] (f : ({β : Type u} StateT σ m β m (stM m (StateT σ m) β)) m (stM m (StateT σ m) α)) (s : σ) :
StateT.run (controlAt m f) s = f fun x => x.run s := by
simp [controlAt]
@[simp] theorem run_control [Monad m] [LawfulMonad m] (f : ({β : Type u} StateT σ m β m (stM m (StateT σ m) β)) m (stM m (StateT σ m) α)) (s : σ) :
StateT.run (control f) s = f fun x => x.run s := run_controlAt f s
end StateT
/-! # EStateM -/
namespace EStateM
@[simp, grind =] theorem run_pure (a : α) (s : σ) :
EStateM.run (pure a : EStateM ε σ α) s = .ok a s := rfl
@[simp, grind =] theorem run_get (s : σ) :
EStateM.run (get : EStateM ε σ σ) s = .ok s s := rfl
@[simp, grind =] theorem run_set (s₁ s₂ : σ) :
EStateM.run (set s₁ : EStateM ε σ PUnit) s₂ = .ok .unit s₁ := rfl
@[simp, grind =] theorem run_modify (f : σ σ) (s : σ) :
EStateM.run (modify f : EStateM ε σ PUnit) s = .ok .unit (f s) := rfl
@[simp, grind =] theorem run_modifyGet (f : σ α × σ) (s : σ) :
EStateM.run (modifyGet f : EStateM ε σ α) s = .ok (f s).1 (f s).2 := rfl
@[simp, grind =] theorem run_throw (e : ε) (s : σ):
EStateM.run (throw e : EStateM ε σ PUnit) s = .error e s := rfl
instance : LawfulMonad (EStateM ε σ) := .mk'
(id_map := fun x => funext <| fun s => by
dsimp only [EStateM.instMonad, EStateM.map]
@@ -480,5 +375,3 @@ instance : LawfulMonad (EStateM ε σ) := .mk'
| .ok _ _ => rfl
| .error _ _ => rfl)
(map_const := fun _ _ => rfl)
end EStateM

View File

@@ -1,10 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Control.Lawful.MonadAttach.Lemmas
public import Init.Control.Lawful.MonadAttach.Instances

View File

@@ -1,86 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Control.Reader
public import Init.Control.Lawful.Instances
import Init.Control.Lawful.MonadAttach.Lemmas
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [WeaklyLawfulMonadAttach m] :
WeaklyLawfulMonadAttach (ReaderT ρ m) where
map_attach := by
simp only [Functor.map, MonadAttach.attach, Functor.map_map, WeaklyLawfulMonadAttach.map_attach]
intros; rfl
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [LawfulMonadAttach m] :
LawfulMonadAttach (ReaderT ρ m) where
canReturn_map_imp := by
simp only [Functor.map, MonadAttach.CanReturn, ReaderT.run]
rintro _ _ x a r, h
apply LawfulMonadAttach.canReturn_map_imp h
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [WeaklyLawfulMonadAttach m] :
WeaklyLawfulMonadAttach (StateT σ m) where
map_attach := by
intro α x
simp only [Functor.map, StateT, funext_iff, StateT.map, bind_pure_comp, MonadAttach.attach,
Functor.map_map]
exact fun s => WeaklyLawfulMonadAttach.map_attach
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [LawfulMonadAttach m] :
LawfulMonadAttach (StateT σ m) where
canReturn_map_imp := by
simp only [Functor.map, MonadAttach.CanReturn, StateT.run, StateT.map, bind_pure_comp]
rintro _ _ x a s, s', h
obtain a, h, h' := LawfulMonadAttach.canReturn_map_imp' h
cases h'
exact a.1.2
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [WeaklyLawfulMonadAttach m] :
WeaklyLawfulMonadAttach (ExceptT ε m) where
map_attach {α} x := by
simp only [Functor.map, MonadAttach.attach, ExceptT.map]
simp
conv => rhs; rw [ WeaklyLawfulMonadAttach.map_attach (m := m) (x := x)]
simp only [map_eq_pure_bind]
apply bind_congr; intro a
match a with
| .ok _, _ => simp
| .error _, _ => simp
public instance [Monad m] [LawfulMonad m] [MonadAttach m] [LawfulMonadAttach m] :
LawfulMonadAttach (ExceptT ε m) where
canReturn_map_imp {α P x a} := by
simp only [Functor.map, MonadAttach.CanReturn, ExceptT.map, ExceptT.mk]
let x' := (fun a => show Subtype (fun a : Except _ _ => match a with | .ok a => P a | .error e => True) from match a with | .ok a => .ok a.1 | .error e => .error e, by cases a <;> simp [Subtype.property]) <$> show m _ from x
have := LawfulMonadAttach.canReturn_map_imp (m := m) (x := x') (a := .ok a)
simp only at this
intro h
apply this
simp only [x', map_eq_pure_bind, bind_assoc]
refine cast ?_ h
congr 1
apply bind_congr; intro a
split <;> simp
public instance [Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] :
WeaklyLawfulMonadAttach (StateRefT' ω σ m) :=
inferInstanceAs (WeaklyLawfulMonadAttach (ReaderT _ _))
public instance [Monad m] [MonadAttach m] [LawfulMonad m] [LawfulMonadAttach m] :
LawfulMonadAttach (StateRefT' ω σ m) :=
inferInstanceAs (LawfulMonadAttach (ReaderT _ _))
section
attribute [local instance] MonadAttach.trivial
public instance [Monad m] [LawfulMonad m] :
WeaklyLawfulMonadAttach m where
map_attach := by simp [MonadAttach.attach]
end

View File

@@ -1,90 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Control.MonadAttach
import all Init.Control.MonadAttach
public import Init.Control.Lawful.Lemmas
public import Init.Control.Lawful.MonadLift.Lemmas
public theorem LawfulMonadAttach.canReturn_bind_imp' [Monad m] [LawfulMonad m]
[MonadAttach m] [LawfulMonadAttach m]
{x : m α} {f : α m β} :
MonadAttach.CanReturn (x >>= f) b Exists fun a => MonadAttach.CanReturn x a MonadAttach.CanReturn (f a) b := by
intro h
let P (b : β) := Exists fun a => MonadAttach.CanReturn x a MonadAttach.CanReturn (f a) b
have h' : (x >>= f) = Subtype.val <$> (MonadAttach.attach x >>= (fun a => (do
let b MonadAttach.attach (f a)
return b.1, a.1, a.2, b.2 : m (Subtype P)))) := by
simp only [map_bind, map_pure]
simp only [bind_pure_comp, WeaklyLawfulMonadAttach.map_attach]
rw (occs := [1]) [ WeaklyLawfulMonadAttach.map_attach (x := x)]
simp
rw [h'] at h
have := LawfulMonadAttach.canReturn_map_imp h
exact this
public theorem LawfulMonadAttach.eq_of_canReturn_pure [Monad m] [MonadAttach m]
[LawfulMonad m] [LawfulMonadAttach m] {a b : α}
(h : MonadAttach.CanReturn (m := m) (pure a) b) :
a = b := by
let x : m (Subtype (a = ·)) := pure a, rfl
have : pure a = Subtype.val <$> x := by simp [x]
rw [this] at h
exact LawfulMonadAttach.canReturn_map_imp h
public theorem LawfulMonadAttach.canReturn_map_imp' [Monad m] [LawfulMonad m]
[MonadAttach m] [LawfulMonadAttach m]
{x : m α} {f : α β} :
MonadAttach.CanReturn (f <$> x) b Exists fun a => MonadAttach.CanReturn x a f a = b := by
rw [map_eq_pure_bind]
intro h
obtain a, h, h' := canReturn_bind_imp' h
exact a, h, eq_of_canReturn_pure h'
public theorem LawfulMonadAttach.canReturn_liftM_imp'
[Monad m] [MonadAttach m] [LawfulMonad m] [LawfulMonadAttach m]
[Monad n] [MonadAttach n] [LawfulMonad n] [LawfulMonadAttach n]
[MonadLiftT m n] [LawfulMonadLiftT m n] {x : m α} {a : α} :
MonadAttach.CanReturn (liftM (n := n) x) a MonadAttach.CanReturn x a := by
intro h
simp only [ WeaklyLawfulMonadAttach.map_attach (x := x), liftM_map] at h
exact canReturn_map_imp h
public theorem WeaklyLawfulMonadAttach.attach_bind_val
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{x : m α} {f : α m β} :
MonadAttach.attach x >>= (fun a => f a.val) = x >>= f := by
conv => rhs; simp only [ map_attach (x := x), bind_map_left]
public theorem WeaklyLawfulMonadAttach.bind_attach_of_nonempty
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m] [Nonempty (m β)]
{x : m α} {f : Subtype (MonadAttach.CanReturn x) m β} :
open scoped Classical in
MonadAttach.attach x >>= f = x >>= (fun a => if ha : MonadAttach.CanReturn x a then f a, ha else Classical.ofNonempty) := by
conv => rhs; simp +singlePass only [ map_attach (x := x)]
simp [Subtype.property]
public theorem MonadAttach.attach_bind_eq_pbind
[Monad m] [MonadAttach m]
{x : m α} {f : Subtype (MonadAttach.CanReturn x) m β} :
MonadAttach.attach x >>= f = MonadAttach.pbind x (fun a ha => f a, ha) := by
simp [MonadAttach.pbind]
public theorem WeaklyLawfulMonadAttach.pbind_eq_bind
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{x : m α} {f : α m β} :
MonadAttach.pbind x (fun a _ => f a) = x >>= f := by
conv => rhs; rw [ map_attach (x := x)]
simp [MonadAttach.pbind]
public theorem WeaklyLawfulMonadAttach.pbind_eq_bind'
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
{x : m α} {f : α m β} :
MonadAttach.pbind x (fun a _ => f a) = x >>= f := by
conv => rhs; rw [ map_attach (x := x)]
simp [MonadAttach.pbind]

View File

@@ -6,7 +6,6 @@ Authors: Quang Dao
module
prelude
public import Init.Control.Id
public import Init.Control.Lawful.Basic
public import Init.Control.Lawful.MonadLift.Basic
@@ -14,14 +13,6 @@ public section
universe u v w
theorem instMonadLiftTOfMonadLift_instMonadLiftTOfPure [Monad m] [Monad n] {_ : MonadLift m n}
[LawfulMonadLift m n] : instMonadLiftTOfMonadLift Id m n = Id.instMonadLiftTOfPure := by
have hext {a b : MonadLiftT Id n} (h : @a.monadLift = @b.monadLift) : a = b := by
cases a <;> cases b <;> simp_all
apply hext
ext α x
simp [monadLift, LawfulMonadLift.monadLift_pure]
variable {m : Type u Type v} {n : Type u Type w} [Monad m] [Monad n] [MonadLiftT m n]
[LawfulMonadLiftT m n] {α β : Type u}

View File

@@ -1,126 +0,0 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul Reichert
-/
module
prelude
public import Init.Control.Basic
set_option linter.all true
set_option doc.verso true
/-!
# {name (scope := "Init.Control.MonadAttach")}`MonadAttach`
This module provides a mechanism for attaching proofs to the return values of monadic computations,
producing a new monadic computation returning a {name}`Subtype`.
This function is primarily used to allow definitions by [well-founded
recursion](lean-manual://section/well-founded-recursion) that sequence computations using
{name}`Bind.bind` (`>>=`) to prove properties about the return values of prior computations when
a recursive call happens.
This allows the well-founded recursion mechanism to prove that the function terminates.
-/
-- verso docstring is added below
set_option linter.missingDocs false in
public class MonadAttach (m : Type u Type v) where
/--
A predicate that can be assumed to be true for all return values {name}`a` of actions {name}`x`
in {name}`m`, in all situations.
-/
CanReturn {α : Type u} : (x : m α) (a : α) Prop
/--
Attaches a proof of {name}`MonadAttach.CanReturn` to the return value of {name}`x`. This proof
can be used to prove the termination of well-founded recursive functions.
-/
attach {α : Type u} (x : m α) : m (Subtype (CanReturn x))
-- verso docstring is added below
set_option linter.missingDocs false in
public class WeaklyLawfulMonadAttach (m : Type u Type v) [Monad m] [MonadAttach m] where
map_attach {α : Type u} {x : m α} : Subtype.val <$> MonadAttach.attach x = x
/--
This type class ensures that {name}`MonadAttach.CanReturn` is the unique strongest possible
postcondition.
-/
public class LawfulMonadAttach (m : Type u Type v) [Monad m] [MonadAttach m] extends
WeaklyLawfulMonadAttach m where
canReturn_map_imp {α : Type u} {P : α Prop} {x : m (Subtype P)} {a : α} :
MonadAttach.CanReturn (Subtype.val <$> x) a P a
/--
Like {name}`Bind.bind`, {name}`pbind` sequences two computations {lean}`x : m α` and {lean}`f`,
allowing the second to depend on the value computed by the first.
But other than with {name}`Bind.bind`, the second computation can also depend on a proof that
the return value {given}`a` of {name}`x` satisfies {lean}`MonadAttach.CanReturn x a`.
-/
public def MonadAttach.pbind [Monad m] [MonadAttach m]
(x : m α) (f : (a : α) MonadAttach.CanReturn x a m β) : m β :=
MonadAttach.attach x >>= (fun a, ha => f a ha)
/--
A {lean}`MonadAttach` instance where all return values are possible and {name}`attach` adds no
information to the return value, except a trivial proof of {name}`True`.
This instance is used whenever no more useful {name}`MonadAttach` instance can be implemented.
It always has a {name}`WeaklyLawfulMonadAttach`, but usually no {name}`LawfulMonadAttach` instance.
-/
@[expose]
public protected def MonadAttach.trivial {m : Type u Type v} [Monad m] : MonadAttach m where
CanReturn _ _ := True
attach x := (·, .intro) <$> x
section
variable (α : Type u) [ m, Monad m] [ m, MonadAttach m]
set_option doc.verso true
/--
For every {given}`x : m α`, this type class provides a predicate {lean}`MonadAttach.CanReturn x`
and a way to attach a proof of this predicate to the return values of {name}`x` by providing
an element {lean}`MonadAttach.attach x` of {lean}`m { a : α // MonadAttach.CanReturn x a }`.
Instances should abide the law {lean}`Subtype.val <$> MonadAttach.attach x = x`, which is encoded by
the {name}`WeaklyLawfulMonadAttach` type class. The stronger type class {name}`LawfulMonadAttach`
ensures that {lean}`MonadAttach.CanReturn x` is the _unique_ strongest possible predicate.
Similarly to {name (scope := "Init.Data.List.Attach")}`List.attach`, the purpose of
{name}`MonadAttach` is to attach proof terms necessary for well-founded termination proofs.
The iterator library relies on {name}`MonadAttach` for combinators such as
{name (scope := "Init.Data.Iterators")}`Std.Iter.filterM` in order to automatically attach
information about the monadic predicate's behavior that could be relevant for the termination
behavior of the iterator.
*Limitations*:
For many monads, there is a strongly lawful {lean}`MonadAttach` instance, but there are exceptions.
For example, there is no way to provide a computable {lean}`MonadAttach` instance for the CPS monad
transformers
{name (scope := "Init.Control.StateCps")}`StateCpsT` and
{name (scope := "Init.Control.StateCps")}`ExceptCpsT` with a predicate that is not always
{name}`True`. Therefore, such CPS monads only provide the trivial {lean}`MonadAttach` instance
{lean}`MonadAttach.trivial` together with {name}`WeaklyLawfulMonadAttach`, but without
{name}`LawfulMonadAttach`.
For most monads with side effects, {lean}`MonadAttach` is too weak to fully capture the behavior of
computations because the postcondition represented by {name}`MonadAttach.CanReturn` neither depends
on the prior internal state of the monad, nor does it contain information about how the state of the
monad changes with the computation.
-/
add_decl_doc MonadAttach
/--
This type class ensures that every monadic action {given}`x : m α` can be recovered by stripping the
proof component from the subtypes returned by
{lean}`(MonadAttach.attach x) : m { a : α // MonadAttach.CanReturn x a }` . In other words,
the type class ensures that {lean}`Subtype.val <$> MonadAttach.attach x = x`.
-/
add_decl_doc WeaklyLawfulMonadAttach
end

View File

@@ -112,12 +112,6 @@ instance (ε : Type u) [MonadExceptOf ε m] : MonadExceptOf ε (OptionT m) where
throw e := OptionT.mk <| throwThe ε e
tryCatch x handle := OptionT.mk <| tryCatchThe ε x handle
instance [MonadAttach m] : MonadAttach (OptionT m) where
CanReturn x a := MonadAttach.CanReturn x.run (some a)
attach x := .mk ((fun
| some a, h => some a, h
| none, _ => none) <$> MonadAttach.attach x.run)
end OptionT
instance [Monad m] : MonadControl m (OptionT m) where

View File

@@ -51,7 +51,3 @@ A monad with access to a read-only value of type `ρ`. The value can be locally
`withReader`, but it cannot be mutated.
-/
abbrev ReaderM (ρ : Type u) := ReaderT ρ Id
instance [Monad m] [MonadAttach m] : MonadAttach (ReaderT ρ m) where
CanReturn x a := Exists (fun r => MonadAttach.CanReturn (x.run r) a)
attach x := fun r => (fun a, h => a, r, h) <$> MonadAttach.attach (x.run r)

View File

@@ -25,12 +25,6 @@ of a value and a state.
@[expose] def StateT (σ : Type u) (m : Type u Type v) (α : Type u) : Type (max u v) :=
σ m (α × σ)
/--
Interpret `σ → m (α × σ)` as an element of `StateT σ m α`.
-/
@[always_inline, inline, expose]
def StateT.mk {σ : Type u} {m : Type u Type v} {α : Type u} (x : σ m (α × σ)) : StateT σ m α := x
/--
Executes an action from a monad with added state in the underlying monad `m`. Given an initial
state, it returns a value paired with the final state.
@@ -204,7 +198,3 @@ instance StateT.tryFinally {m : Type u → Type v} {σ : Type u} [MonadFinally m
| some (a, s') => h (some a) s'
| none => h none s
pure ((a, b), s'')
instance [Monad m] [MonadAttach m] : MonadAttach (StateT σ m) where
CanReturn x a := Exists fun s => Exists fun s' => MonadAttach.CanReturn (x.run s) (a, s')
attach x := fun s => (fun a, s', h => a, s, s', h, s') <$> MonadAttach.attach (x.run s)

View File

@@ -68,13 +68,6 @@ instance : MonadStateOf σ (StateCpsT σ m) where
set s := fun _ _ k => k s
modifyGet f := fun _ s k => let (a, s) := f s; k a s
/--
For continuation monads, it is not possible to provide a computable `MonadAttach` instance that
actually adds information about the return value. Therefore, this instance always attaches a proof
of `True`.
-/
instance : MonadAttach (StateCpsT ε m) := .trivial
/--
Runs an action from the underlying monad in the monad with state. The state is not modified.

View File

@@ -64,7 +64,6 @@ instance [Monad m] : Monad (StateRefT' ω σ m) := inferInstanceAs (Monad (Reade
instance : MonadLift m (StateRefT' ω σ m) := StateRefT'.lift
instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _))
instance [Monad m] [MonadAttach m] : MonadAttach (StateRefT' ω σ m) := inferInstanceAs (MonadAttach (ReaderT _ _))
/--
Retrieves the current value of the monad's mutable state.

View File

@@ -201,7 +201,6 @@ An element of `α ⊕ β` is either an `a : α` wrapped in `Sum.inl` or a `b :
indication of which of the two types was chosen. The union of a singleton set with itself contains
one element, while `Unit ⊕ Unit` contains distinct values `inl ()` and `inr ()`.
-/
@[suggest_for Either]
inductive Sum (α : Type u) (β : Type v) where
/-- Left injection into the sum type `α ⊕ β`. -/
| inl (val : α) : Sum α β
@@ -378,7 +377,7 @@ class ForIn (m : Type u₁ → Type u₂) (ρ : Type u) (α : outParam (Type v))
More information about the translation of `for` loops into `ForIn.forIn` is available in [the Lean
reference manual](lean-manual://section/monad-iteration-syntax).
-/
forIn {β} (xs : ρ) (b : β) (f : α β m (ForInStep β)) : m β
forIn {β} [Monad m] (xs : ρ) (b : β) (f : α β m (ForInStep β)) : m β
export ForIn (forIn)
@@ -406,7 +405,7 @@ class ForIn' (m : Type u₁ → Type u₂) (ρ : Type u) (α : outParam (Type v)
More information about the translation of `for` loops into `ForIn'.forIn'` is available in [the
Lean reference manual](lean-manual://section/monad-iteration-syntax).
-/
forIn' {β} (x : ρ) (b : β) (f : (a : α) a x β m (ForInStep β)) : m β
forIn' {β} [Monad m] (x : ρ) (b : β) (f : (a : α) a x β m (ForInStep β)) : m β
export ForIn' (forIn')
@@ -940,7 +939,9 @@ theorem HEq.subst {p : (T : Sort u) → T → Prop} (h₁ : a ≍ b) (h₂ : p
@[symm] theorem HEq.symm (h : a b) : b a :=
h.rec (HEq.refl a)
/-- Propositionally equal terms are also heterogeneously equal. -/
theorem heq_of_eq (h : a = a') : a a' :=
Eq.subst h (HEq.refl a)
/-- Heterogeneous equality is transitive. -/
theorem HEq.trans (h₁ : a b) (h₂ : b c) : a c :=
@@ -1369,7 +1370,7 @@ instance {α : Type u} {p : α → Prop} [BEq α] [LawfulBEq α] : LawfulBEq {x
instance {α : Sort u} {p : α Prop} [DecidableEq α] : DecidableEq {x : α // p x} :=
fun a, h₁ b, h₂ =>
if h : a = b then isTrue (by subst h; exact rfl)
else isFalse (fun h' => Subtype.noConfusion rfl .rfl (heq_of_eq h') (fun h' => absurd (eq_of_heq h') h))
else isFalse (fun h' => Subtype.noConfusion h' (fun h' => absurd h' h))
end Subtype
@@ -1428,8 +1429,8 @@ instance [DecidableEq α] [DecidableEq β] : DecidableEq (α × β) :=
| isTrue e₁ =>
match decEq b b' with
| isTrue e₂ => isTrue (e₁ e₂ rfl)
| isFalse n₂ => isFalse fun h => Prod.noConfusion rfl rfl (heq_of_eq h) fun _ e₂' => absurd (eq_of_heq e₂') n₂
| isFalse n₁ => isFalse fun h => Prod.noConfusion rfl rfl (heq_of_eq h) fun e₁' _ => absurd (eq_of_heq e₁') n₁
| isFalse n₂ => isFalse fun h => Prod.noConfusion h fun _ e₂' => absurd e₂' n₂
| isFalse n₁ => isFalse fun h => Prod.noConfusion h fun e₁' _ => absurd e₁' n₁
instance [BEq α] [BEq β] : BEq (α × β) where
beq := fun (a₁, b₁) (a₂, b₂) => a₁ == a₂ && b₁ == b₂

View File

@@ -572,6 +572,9 @@ def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array
@[simp] theorem unattach_empty {p : α Prop} : (#[] : Array { x // p x }).unattach = #[] := by
simp [unattach]
@[deprecated unattach_empty (since := "2025-05-26")]
abbrev unattach_nil := @unattach_empty
@[simp] theorem unattach_push {p : α Prop} {a : { x // p x }} {xs : Array { x // p x }} :
(xs.push a).unattach = xs.unattach.push a.1 := by
simp only [unattach, Array.map_push]

View File

@@ -242,7 +242,7 @@ Examples:
* `#["red", "green", "blue", "brown"].swapIfInBounds 0 4 = #["red", "green", "blue", "brown"]`
* `#["red", "green", "blue", "brown"].swapIfInBounds 9 2 = #["red", "green", "blue", "brown"]`
-/
@[extern "lean_array_swap", expose]
@[extern "lean_array_swap", grind]
def swapIfInBounds (xs : Array α) (i j : @& Nat) : Array α :=
if h₁ : i < xs.size then
if h₂ : j < xs.size then swap xs i j
@@ -570,7 +570,7 @@ protected def forIn' {α : Type u} {β : Type v} {m : Type v → Type w} [Monad
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
loop as.size (Nat.le_refl _) b
instance [Monad m] : ForIn' m (Array α) α inferInstance where
instance : ForIn' m (Array α) α inferInstance where
forIn' := Array.forIn'
-- No separate `ForIn` instance is required because it can be derived from `ForIn'`.
@@ -589,8 +589,6 @@ unsafe def foldlMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Mon
if start < stop then
if stop as.size then
fold (USize.ofNat start) (USize.ofNat stop) init
else if start < as.size then
fold (USize.ofNat start) (USize.ofNat as.size) init
else
pure init
else
@@ -1003,7 +1001,7 @@ unless `start < stop`. By default, the entire array is used.
protected def forM {α : Type u} {m : Type v Type w} [Monad m] (f : α m PUnit) (as : Array α) (start := 0) (stop := as.size) : m PUnit :=
as.foldlM (fun _ => f) start stop
instance [Monad m] : ForM m (Array α) α where
instance : ForM m (Array α) α where
forM xs f := Array.forM f xs
-- We simplify `Array.forM` to `forM`.
@@ -1350,7 +1348,7 @@ Examples:
* `#[2, 4, 5, 6].any (· % 2 = 0) = true`
* `#[2, 4, 5, 6].any (· % 2 = 1) = true`
-/
@[inline, expose, suggest_for Array.some]
@[inline, expose]
def any (as : Array α) (p : α Bool) (start := 0) (stop := as.size) : Bool :=
Id.run <| as.anyM (pure <| p ·) start stop
@@ -1368,7 +1366,7 @@ Examples:
* `#[2, 4, 6].all (· % 2 = 0) = true`
* `#[2, 4, 5, 6].all (· % 2 = 0) = false`
-/
@[inline, suggest_for Array.every]
@[inline]
def all (as : Array α) (p : α Bool) (start := 0) (stop := as.size) : Bool :=
Id.run <| as.allM (pure <| p ·) start stop

View File

@@ -73,6 +73,9 @@ theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α → β → m β} {init
rcases xs with xs
simp [push, List.concat_eq_append]
@[deprecated toList_push (since := "2025-05-26")]
abbrev push_toList := @toList_push
@[simp, grind =] theorem toListAppend_eq {xs : Array α} {l : List α} : xs.toListAppend l = xs.toList ++ l := by
simp [toListAppend, foldr_toList]

View File

@@ -62,12 +62,12 @@ theorem size_eq_countP_add_countP {xs : Array α} : xs.size = countP p xs + coun
rcases xs with xs
simp [List.length_eq_countP_add_countP (p := p)]
@[grind =]
theorem countP_eq_size_filter {xs : Array α} : countP p xs = (filter p xs).size := by
rcases xs with xs
simp [List.countP_eq_length_filter]
grind_pattern countP_eq_size_filter => xs.countP p, xs.filter p
@[grind =]
theorem countP_eq_size_filter' : countP p = size filter p := by
funext xs
apply countP_eq_size_filter

View File

@@ -89,41 +89,11 @@ theorem isEqv_self_beq [BEq α] [ReflBEq α] (xs : Array α) : Array.isEqv xs xs
theorem isEqv_self [DecidableEq α] (xs : Array α) : Array.isEqv xs xs (· = ·) = true := by
simp [isEqv, isEqvAux_self]
def instDecidableEqImpl [DecidableEq α] : DecidableEq (Array α) := fun xs ys =>
match h:isEqv xs ys (fun a b => a = b) with
| true => isTrue (eq_of_isEqv xs ys h)
| false => isFalse (by subst ·; rw [isEqv_self] at h; contradiction)
instance instDecidableEq [DecidableEq α] : DecidableEq (Array α) := fun xs ys =>
match xs with
| [] =>
match ys with
| [] => isTrue rfl
| _ :: _ => isFalse (fun h => Array.noConfusion rfl (heq_of_eq h) (fun h => List.noConfusion rfl h))
| a :: as =>
match ys with
| [] => isFalse (fun h => Array.noConfusion rfl (heq_of_eq h) (fun h => List.noConfusion rfl h))
| b :: bs => instDecidableEqImpl a :: as b :: bs
@[csimp]
theorem instDecidableEq_csimp : @instDecidableEq = @instDecidableEqImpl :=
Subsingleton.allEq _ _
/--
Equality with `#[]` is decidable even if the underlying type does not have decidable equality.
-/
instance instDecidableEqEmp (xs : Array α) : Decidable (xs = #[]) :=
match xs with
| [] => isTrue rfl
| _ :: _ => isFalse (fun h => Array.noConfusion rfl (heq_of_eq h) (fun h => List.noConfusion rfl h))
/--
Equality with `#[]` is decidable even if the underlying type does not have decidable equality.
-/
instance instDecidableEmpEq (ys : Array α) : Decidable (#[] = ys) :=
match ys with
| [] => isTrue rfl
| _ :: _ => isFalse (fun h => Array.noConfusion rfl (heq_of_eq h) (fun h => List.noConfusion rfl h))
instance [DecidableEq α] : DecidableEq (Array α) :=
fun xs ys =>
match h:isEqv xs ys (fun a b => a = b) with
| true => isTrue (eq_of_isEqv xs ys h)
| false => isFalse fun h' => by subst h'; rw [isEqv_self] at h; contradiction
theorem beq_eq_decide [BEq α] (xs ys : Array α) :
(xs == ys) = if h : xs.size = ys.size then

View File

@@ -389,6 +389,9 @@ theorem eraseIdx_append_of_size_le {xs : Array α} {k : Nat} (hk : xs.size ≤ k
simp at hk
simp [List.eraseIdx_append_of_length_le, *]
@[deprecated eraseIdx_append_of_size_le (since := "2025-06-11")]
abbrev eraseIdx_append_of_length_le := @eraseIdx_append_of_size_le
@[grind =]
theorem eraseIdx_append {xs ys : Array α} (h : k < (xs ++ ys).size) :
eraseIdx (xs ++ ys) k =

View File

@@ -409,6 +409,8 @@ theorem popWhile_append {xs ys : Array α} :
rcases ys with ys
simp only [List.append_toArray, List.popWhile_toArray, List.reverse_append, List.dropWhile_append,
List.isEmpty_iff, List.isEmpty_toArray, List.isEmpty_reverse]
-- Why do these not fire with `simp`?
rw [List.popWhile_toArray, List.isEmpty_toArray, List.isEmpty_reverse]
split
· rfl
· simp

View File

@@ -159,6 +159,9 @@ theorem find?_singleton {a : α} {p : α → Bool} :
findRev? p (xs.push a) = findRev? p xs := by
cases xs; simp [h]
@[deprecated findRev?_push_of_neg (since := "2025-06-12")]
abbrev findRev?_cons_of_neg := @findRev?_push_of_neg
@[grind =]
theorem finRev?_push {xs : Array α} :
findRev? p (xs.push a) = (Option.guard p a).or (xs.findRev? p) := by
@@ -168,6 +171,9 @@ theorem finRev?_push {xs : Array α} :
· rw [findRev?_push_of_pos, Option.guard_eq_some_iff.mpr rfl, h]
all_goals simp [h]
@[deprecated finRev?_push (since := "2025-06-12")]
abbrev findRev?_cons := @finRev?_push
@[simp, grind =] theorem find?_eq_none : find? p xs = none x xs, ¬ p x := by
cases xs; simp

View File

@@ -53,6 +53,11 @@ theorem eraseIdx_insertIdx_self {i : Nat} {xs : Array α} (h : i ≤ xs.size) :
rcases xs with xs
simp_all
@[deprecated eraseIdx_insertIdx_self (since := "2025-06-15")]
theorem eraseIdx_insertIdx {i : Nat} {xs : Array α} (h : i xs.size) :
(xs.insertIdx i a).eraseIdx i (by simp; omega) = xs := by
simp [eraseIdx_insertIdx_self]
theorem insertIdx_eraseIdx_of_ge {as : Array α}
(w₁ : i < as.size) (w₂ : j (as.eraseIdx i).size) (h : i j) :
(as.eraseIdx i).insertIdx j a =

View File

@@ -62,9 +62,6 @@ theorem eq_empty_of_size_eq_zero (h : xs.size = 0) : xs = #[] := by
cases xs
simp_all
grind_pattern eq_empty_of_size_eq_zero => xs.size where
guard xs.size = 0
theorem ne_empty_of_size_eq_add_one (h : xs.size = n + 1) : xs #[] := by
cases xs
simpa using List.ne_nil_of_length_eq_add_one h
@@ -1761,6 +1758,11 @@ theorem toArray_append {xs : List α} {ys : Array α} :
theorem singleton_eq_toArray_singleton {a : α} : #[a] = [a].toArray := rfl
@[deprecated empty_append (since := "2025-05-26")]
theorem empty_append_fun : ((#[] : Array α) ++ ·) = id := by
funext l
simp
@[simp, grind =] theorem mem_append {a : α} {xs ys : Array α} : a xs ++ ys a xs a ys := by
simp only [mem_def, toList_append, List.mem_append]
@@ -3246,6 +3248,14 @@ rather than `(arr.push a).size` as the argument.
l.foldl (fun xs x => xs.push x) xs = xs ++ l.toArray := by
simpa using List.foldl_push_eq_append (f := id)
@[deprecated _root_.List.foldl_push_eq_append' (since := "2025-05-18")]
theorem _root_.List.foldl_push {l : List α} {as : Array α} : l.foldl Array.push as = as ++ l.toArray := by
induction l generalizing as <;> simp [*]
@[deprecated _root_.List.foldr_push_eq_append' (since := "2025-05-18")]
theorem _root_.List.foldr_push {l : List α} {as : Array α} : l.foldr (fun a bs => push bs a) as = as ++ l.reverse.toArray := by
rw [List.foldr_eq_foldl_reverse, List.foldl_push_eq_append']
-- TODO: a multi-pattern is being selected there because E-matching does not go inside lambdas.
@[simp, grind! ] theorem foldr_append_eq_append {xs : Array α} {f : α Array β} {ys : Array β} :
xs.foldr (f · ++ ·) ys = (xs.map f).flatten ++ ys := by
@@ -3545,6 +3555,11 @@ theorem mem_of_back? {xs : Array α} {a : α} (h : xs.back? = some a) : a ∈ xs
rcases ys with ys
simp only [List.append_toArray, List.back_toArray, List.getLast_append, List.isEmpty_iff,
List.isEmpty_toArray]
split
· rw [dif_pos]
simpa only [List.isEmpty_toArray]
· rw [dif_neg]
simpa only [List.isEmpty_toArray]
theorem back_append_right {xs ys : Array α} (h : 0 < ys.size) :
(xs ++ ys).back (by simp; omega) = ys.back h := by
@@ -3956,29 +3971,28 @@ theorem getElem_modify_of_ne {xs : Array α} {i : Nat} (h : i ≠ j)
/-! ### swap -/
@[grind =]
theorem getElem_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} (hk : k < (xs.swap i j hi hj).size) :
(xs.swap i j hi hj)[k] = if k = i then xs[j] else if k = j then xs[i] else xs[k]'(by simp_all) := by
simp only [swap_def, getElem_set, eq_comm (a := k)]
split <;> split <;> simp_all
@[simp] theorem getElem_swap_right {xs : Array α} {i j : Nat} {hi hj} :
(xs.swap i j hi hj)[j]'(by simpa using hj) = xs[i] := by
simp +contextual [getElem_swap]
simp [swap_def]
@[simp] theorem getElem_swap_left {xs : Array α} {i j : Nat} {hi hj} :
(xs.swap i j hi hj)[i]'(by simpa using hi) = xs[j] := by
simp [getElem_swap]
simp +contextual [swap_def, getElem_set]
@[simp] theorem getElem_swap_of_ne {xs : Array α} {i j : Nat} {hi hj}
{h : k < (xs.swap i j hi hj).size} (hi' : k i) (hj' : k j) :
(xs.swap i j hi hj)[k] = xs[k]'(by simp_all) := by
simp [getElem_swap, hi', hj']
@[simp] theorem getElem_swap_of_ne {xs : Array α} {i j : Nat} {hi hj} (hp : k < xs.size)
(hi' : k i) (hj' : k j) : (xs.swap i j hi hj)[k]'(xs.size_swap .. |>.symm hp) = xs[k] := by
simp [swap_def, getElem_set, hi'.symm, hj'.symm]
@[deprecated getElem_swap (since := "2025-10-10")]
theorem getElem_swap' {xs : Array α} {i j : Nat} {hi hj} {k : Nat} (hk : k < xs.size) :
(xs.swap i j hi hj)[k]'(by simp_all) = if k = i then xs[j] else if k = j then xs[i] else xs[k] :=
getElem_swap _ _ _
(xs.swap i j hi hj)[k]'(by simp_all) = if k = i then xs[j] else if k = j then xs[i] else xs[k] := by
split
· simp_all only [getElem_swap_left]
· split <;> simp_all
@[grind =]
theorem getElem_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} (hk : k < (xs.swap i j hi hj).size) :
(xs.swap i j hi hj)[k] = if k = i then xs[j] else if k = j then xs[i] else xs[k]'(by simp_all) := by
apply getElem_swap'
@[simp] theorem swap_swap {xs : Array α} {i j : Nat} (hi hj) :
(xs.swap i j hi hj).swap i j ((xs.size_swap ..).symm hi) ((xs.size_swap ..).symm hj) = xs := by
@@ -3999,66 +4013,8 @@ theorem swap_comm {xs : Array α} {i j : Nat} (hi hj) : xs.swap i j hi hj = xs.s
· split <;> simp_all
· split <;> simp_all
/-! ### swapIfInBounds -/
@[grind =] theorem swapIfInBounds_def {xs : Array α} {i j : Nat} :
xs.swapIfInBounds i j = if h₁ : i < xs.size then
if h₂ : j < xs.size then swap xs i j else xs else xs := rfl
@[simp, grind =] theorem size_swapIfInBounds {xs : Array α} {i j : Nat} :
(xs.swapIfInBounds i j).size = xs.size := by
unfold swapIfInBounds; split <;> (try split) <;> simp [size_swap]
@[grind =] theorem getElem_swapIfInBounds {xs : Array α} {i j k : Nat}
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] =
if h₁ : k = i j < xs.size then xs[j]'h₁.2 else if h₂ : k = j i < xs.size then xs[i]'h₂.2
else xs[k]'(by simp_all) := by
rw [size_swapIfInBounds] at hk
unfold swapIfInBounds
split <;> rename_i hi
· split <;> rename_i hj
· simp only [hi, hj, and_true]
exact getElem_swap _ _ _
· simp only [hi, hj, and_true, and_false, dite_false]
split <;> simp_all
· simp only [hi, and_false, dite_false]
split <;> simp_all
@[simp]
theorem getElem_swapIfInBounds_of_size_le_left {xs : Array α} {i j k : Nat} (hi : xs.size i)
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] = xs[k]'(Nat.lt_of_lt_of_eq hk size_swapIfInBounds) := by
have h₁ : k i := Nat.ne_of_lt <| Nat.lt_of_lt_of_le hk <|
Nat.le_trans (Nat.le_of_eq (size_swapIfInBounds)) hi
have h₂ : ¬ (i < xs.size) := Nat.not_lt_of_le hi
simp [getElem_swapIfInBounds, h₁, h₂]
@[simp]
theorem getElem_swapIfInBounds_of_size_le_right {xs : Array α} {i j k : Nat} (hj : xs.size j)
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] = xs[k]'(Nat.lt_of_lt_of_eq hk size_swapIfInBounds) := by
have h₁ : ¬ (j < xs.size) := Nat.not_lt_of_le hj
have h₂ : k j := Nat.ne_of_lt <| Nat.lt_of_lt_of_le hk <|
Nat.le_trans (Nat.le_of_eq (size_swapIfInBounds)) hj
simp [getElem_swapIfInBounds, h₁, h₂]
@[simp]
theorem getElem_swapIfInBounds_left {xs : Array α} {i j : Nat} (hj : j < xs.size)
(hi : i < (xs.swapIfInBounds i j).size) : (xs.swapIfInBounds i j)[i] = xs[j] := by
simp [getElem_swapIfInBounds, hj]
@[simp]
theorem getElem_swapIfInBounds_right {xs : Array α} {i j : Nat} (hi : i < xs.size)
(hj : j < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[j] = xs[i] := by
simp +contextual [getElem_swapIfInBounds, hi]
@[simp]
theorem getElem_swapIfInBounds_of_ne_of_ne {xs : Array α} {i j k : Nat} (hi : k i) (hj : k j)
(hk : k < (xs.swapIfInBounds i j).size) :
(xs.swapIfInBounds i j)[k] = xs[k]'(Nat.lt_of_lt_of_eq hk size_swapIfInBounds) := by
simp [getElem_swapIfInBounds, hi, hj]
(xs.swapIfInBounds i j).size = xs.size := by unfold swapIfInBounds; split <;> (try split) <;> simp [size_swap]
/-! ### swapAt -/
@@ -4320,9 +4276,10 @@ theorem size_uset {xs : Array α} {v : α} {i : USize} (h : i.toNat < xs.size) :
theorem getElem!_eq_getD [Inhabited α] {xs : Array α} {i} : xs[i]! = xs.getD i default := by
rfl
theorem getElem_eq_getD {xs : Array α} {i} {h : i < xs.size} (fallback : α) :
xs[i]'h = xs.getD i fallback := by
rw [getD_eq_getD_getElem?, getElem_eq_getElem?_get, Option.get_eq_getD]
/-! # mem -/
@[deprecated mem_toList_iff (since := "2025-05-26")]
theorem mem_toList {a : α} {xs : Array α} : a xs.toList a xs := mem_def.symm
/-! # get lemmas -/

View File

@@ -73,11 +73,19 @@ private theorem cons_lex_cons [BEq α] {lt : αα → Bool} {a b : α} {xs
(lt a b || a == b && xs.lex ys lt) := by
simp only [lex, size_append, List.size_toArray, List.length_cons, List.length_nil, Nat.zero_add,
Nat.add_min_add_left, Nat.add_lt_add_iff_left, Std.Rco.forIn'_eq_forIn'_toList]
rw [cons_lex_cons.forIn'_congr_aux (Nat.toList_rco_eq_cons (by omega)) rfl (fun _ _ _ => rfl)]
simp only [bind_pure_comp, map_pure, Nat.toList_rco_succ_succ, Nat.add_comm 1]
cases h : lt a b
· cases h' : a == b <;> simp [bne, *]
· simp [*]
conv =>
lhs; congr; congr
rw [cons_lex_cons.forIn'_congr_aux Std.Rco.toList_eq_if_roo rfl (fun _ _ _ => rfl)]
simp only [bind_pure_comp, map_pure]
rw [cons_lex_cons.forIn'_congr_aux (if_pos (by omega)) rfl (fun _ _ _ => rfl)]
simp only [Std.toList_roo_eq_toList_rco_of_isSome_succ? (lo := 0) (h := rfl),
Std.PRange.UpwardEnumerable.succ?, Nat.add_comm 1, Std.PRange.Nat.toList_rco_succ_succ,
Option.get_some, List.forIn'_cons, List.size_toArray, List.length_cons, List.length_nil,
Nat.lt_add_one, getElem_append_left, List.getElem_toArray, List.getElem_cons_zero]
cases lt a b
· rw [bne]
cases a == b <;> simp
· simp
@[simp, grind =] theorem _root_.List.lex_toArray [BEq α] {lt : α α Bool} {l₁ l₂ : List α} :
l₁.toArray.lex l₂.toArray lt = l₁.lex l₂ lt := by

View File

@@ -39,6 +39,10 @@ theorem map_toList_inj [Monad m] [LawfulMonad m]
@[simp, grind =] theorem idRun_mapM {xs : Array α} {f : α Id β} : (xs.mapM f).run = xs.map (f · |>.run) :=
mapM_pure
@[deprecated idRun_mapM (since := "2025-05-21")]
theorem mapM_id {xs : Array α} {f : α Id β} : xs.mapM f = xs.map f :=
mapM_pure
@[simp, grind =] theorem mapM_map [Monad m] [LawfulMonad m] {f : α β} {g : β m γ} {xs : Array α} :
(xs.map f).mapM g = xs.mapM (g f) := by
rcases xs with xs
@@ -197,6 +201,13 @@ theorem idRun_forIn'_yield_eq_foldl
xs.attach.foldl (fun b a, h => f a h b |>.run) init := by
simp
@[deprecated idRun_forIn'_yield_eq_foldl (since := "2025-05-21")]
theorem forIn'_yield_eq_foldl
{xs : Array α} (f : (a : α) a xs β β) (init : β) :
forIn' (m := Id) xs init (fun a m b => .yield (f a m b)) =
xs.attach.foldl (fun b a, h => f a h b) init :=
forIn'_pure_yield_eq_foldl _ _
@[simp, grind =] theorem forIn'_map [Monad m] [LawfulMonad m]
{xs : Array α} (g : α β) (f : (b : β) b xs.map g γ m (ForInStep γ)) :
forIn' (xs.map g) init f = forIn' xs init fun a h y => f (g a) (mem_map_of_mem h) y := by
@@ -238,6 +249,13 @@ theorem idRun_forIn_yield_eq_foldl
xs.foldl (fun b a => f a b |>.run) init := by
simp
@[deprecated idRun_forIn_yield_eq_foldl (since := "2025-05-21")]
theorem forIn_yield_eq_foldl
{xs : Array α} (f : α β β) (init : β) :
forIn (m := Id) xs init (fun a b => .yield (f a b)) =
xs.foldl (fun b a => f a b) init :=
forIn_pure_yield_eq_foldl _ _
@[simp, grind =] theorem forIn_map [Monad m] [LawfulMonad m]
{xs : Array α} {g : α β} {f : β γ m (ForInStep γ)} :
forIn (xs.map g) init f = forIn xs init fun a y => f (g a) y := by

View File

@@ -280,7 +280,7 @@ Checks whether any of the elements in a subarray satisfy a Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that satisfies the predicate is found.
-/
@[inline, suggest_for Subarray.some]
@[inline]
def any {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.anyM (pure <| p ·)
@@ -290,7 +290,7 @@ Checks whether all of the elements in a subarray satisfy a Boolean predicate.
The elements are tested starting at the lowest index and moving up. The search terminates as soon as
an element that does not satisfy the predicate is found.
-/
@[inline, suggest_for Subarray.every]
@[inline]
def all {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.allM (pure <| p ·)

View File

@@ -353,6 +353,14 @@ theorem zipWithM_eq_mapM_id_zipWith {m : Type v → Type w} [Monad m] [LawfulMon
/-! ### unzip -/
@[deprecated fst_unzip (since := "2025-05-26")]
theorem unzip_fst : (unzip l).fst = l.map Prod.fst := by
simp
@[deprecated snd_unzip (since := "2025-05-26")]
theorem unzip_snd : (unzip l).snd = l.map Prod.snd := by
simp
@[grind =]
theorem unzip_eq_map {xs : Array (α × β)} : unzip xs = (xs.map Prod.fst, xs.map Prod.snd) := by
cases xs

View File

@@ -77,6 +77,9 @@ Returns the `i`th least significant bit.
-/
@[inline, expose] def getLsb (x : BitVec w) (i : Fin w) : Bool := x.toNat.testBit i
@[deprecated getLsb (since := "2025-06-17"), inherit_doc getLsb]
abbrev getLsb' := @getLsb
/-- Returns the `i`th least significant bit, or `none` if `i ≥ w`. -/
@[inline, expose] def getLsb? (x : BitVec w) (i : Nat) : Option Bool :=
if h : i < w then some (getLsb x i, h) else none
@@ -86,6 +89,9 @@ Returns the `i`th most significant bit.
-/
@[inline] def getMsb (x : BitVec w) (i : Fin w) : Bool := x.getLsb w-1-i, by omega
@[deprecated getMsb (since := "2025-06-17"), inherit_doc getMsb]
abbrev getMsb' := @getMsb
/-- Returns the `i`th most significant bit or `none` if `i ≥ w`. -/
@[inline] def getMsb? (x : BitVec w) (i : Nat) : Option Bool :=
if h : i < w then some (getMsb x i, h) else none
@@ -290,7 +296,7 @@ Lean convention that division by zero returns zero.
Examples:
* `(7#4).sdiv 2 = 3#4`
* `(-8#4).sdiv 2 = -4#4`
* `(-9#4).sdiv 2 = -4#4`
* `(5#4).sdiv -2 = -2#4`
* `(-7#4).sdiv (-2) = 3#4`
-/
@@ -864,17 +870,4 @@ def clz (x : BitVec w) : BitVec w := clzAuxRec x (w - 1)
/-- Count the number of trailing zeros. -/
def ctz (x : BitVec w) : BitVec w := (x.reverse).clz
/-- Count the number of bits with value `1` downward from the `pos`-th bit to the
`0`-th bit of `x`, storing the result in `acc`. -/
def cpopNatRec (x : BitVec w) (pos acc : Nat) : Nat :=
match pos with
| 0 => acc
| n + 1 => x.cpopNatRec n (acc + (x.getLsbD n).toNat)
/-- Population count operation, to count the number of bits with value `1` in `x`.
Also known as `popcount`, `popcnt`.
-/
@[suggest_for BitVec.popcount BitVec.popcnt]
def cpop (x : BitVec w) : BitVec w := BitVec.ofNat w (cpopNatRec x w 0)
end BitVec

View File

@@ -835,7 +835,7 @@ execution. -/
structure DivModArgs (w : Nat) where
/-- the numerator (aka, dividend) -/
n : BitVec w
/-- the denominator (aka, divisor)-/
/-- the denumerator (aka, divisor)-/
d : BitVec w
/-- A `DivModState` is lawful if the remainder width `wr` plus the numerator width `wn` equals `w`,

View File

@@ -145,6 +145,10 @@ theorem two_pow_le_toNat_of_getElem_eq_true {i : Nat} {x : BitVec w}
@[grind =] theorem getMsbD_eq_getLsbD (x : BitVec w) (i : Nat) : x.getMsbD i = (decide (i < w) && x.getLsbD (w - 1 - i)) := by
rw [getMsbD, getLsbD]
@[deprecated getMsb_eq_getLsb (since := "2025-06-17")]
theorem getMsb'_eq_getLsb' (x : BitVec w) (i : Nat) : x.getMsbD i = (decide (i < w) && x.getLsbD (w - 1 - i)) := by
rw [getMsbD, getLsbD]
theorem getLsbD_eq_getMsbD (x : BitVec w) (i : Nat) : x.getLsbD i = (decide (i < w) && x.getMsbD (w - 1 - i)) := by
rw [getMsbD]
by_cases h₁ : i < w <;> by_cases h₂ : w - 1 - i < w <;>
@@ -1019,14 +1023,6 @@ theorem setWidth_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
rw [Nat.mod_mod_of_dvd]
exact Nat.pow_dvd_pow_iff_le_right'.mpr h
@[simp]
theorem setWidth_ofNat_of_le_of_lt {x : Nat} (h : w v) (h' : x < 2 ^ w) :
setWidth v (BitVec.ofNat w x) = BitVec.ofNat v x := by
apply BitVec.eq_of_toNat_eq
have := Nat.pow_le_pow_of_le (a := 2) (m := v) (n := w) (by omega) h
simp only [toNat_setWidth, toNat_ofNat]
rw [Nat.mod_eq_of_lt (by omega), Nat.mod_eq_of_lt (by omega), Nat.mod_eq_of_lt (by omega)]
/--
Iterated `setWidth` agrees with the second `setWidth`
except in the case the first `setWidth` is a non-trivial truncation,
@@ -1060,7 +1056,7 @@ theorem toInt_setWidth' {m n : Nat} (p : m ≤ n) {x : BitVec m} :
@[simp, grind =] theorem toFin_setWidth' {m n : Nat} (p : m n) (x : BitVec m) :
(setWidth' p x).toFin = x.toFin.castLE (Nat.pow_le_pow_right (by omega) (by omega)) := by
ext
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.val_castLE, val_toFin,
rw [setWidth'_eq, toFin_setWidth, Fin.val_ofNat, Fin.coe_castLE, val_toFin,
Nat.mod_eq_of_lt (by apply BitVec.toNat_lt_twoPow_of_le p)]
theorem toNat_setWidth_of_le {w w' : Nat} {b : BitVec w} (h : w w') : (b.setWidth w').toNat = b.toNat := by
@@ -1260,31 +1256,11 @@ theorem extractLsb'_setWidth_of_le {b : BitVec w} {start len w' : Nat} (h : star
simp
omega
@[simp]
theorem extractLsb_setWidth_of_lt {x : BitVec w} {hi lo v : Nat} (h : lo + hi < v) :
(x.setWidth v).extractLsb hi lo = x.extractLsb hi lo := by
simp only [BitVec.extractLsb]
ext k hk
simp
omega
theorem setWidth_extractLsb'_of_le {c : BitVec w} (h : len₁ len₂) :
(c.extractLsb' start len₂).setWidth len₁ = c.extractLsb' start len₁ := by
ext i hi
simp [show i < len₂ by omega]
theorem extractLsb'_cast {x : BitVec w} :
(x.cast hcast).extractLsb' start len = x.extractLsb' start len := by
ext k hk
simp
@[simp]
theorem extractLsb'_extractLsb'_of_le {x : BitVec w} (hlt : start + len len') :
(x.extractLsb' 0 len').extractLsb' start len = x.extractLsb' start len := by
ext k hk
simp
omega
/-! ### allOnes -/
@[simp, grind =] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
@@ -2941,15 +2917,6 @@ theorem setWidth_eq_append {v : Nat} {x : BitVec v} {w : Nat} (h : v ≤ w) :
omega
· simp [hiv, getLsbD_of_ge x i (by omega)]
@[simp]
theorem extractLsb'_append_extractLsb' {x : BitVec (w + len)} :
(x.extractLsb' len w ++ x.extractLsb' 0 len) = x := by
ext i hi
simp only [getElem_append, getElem_extractLsb', Nat.zero_add, dite_eq_ite]
split
· rw [ getLsbD_eq_getElem]
· simp [show len + (i - len) = i by omega, getLsbD_eq_getElem]
theorem setWidth_eq_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} (h : w v) :
x.setWidth w = x.extractLsb' 0 w := by
rw [setWidth_eq_append_extractLsb']
@@ -3247,11 +3214,6 @@ theorem cons_append_append (x : BitVec w₁) (y : BitVec w₂) (z : BitVec w₃)
· simp [h₂]; omega
· simp [h₂]; omega
@[simp]
theorem extractLsb'_cons {x : BitVec w} :
(x.cons y).extractLsb' 0 w = x := by
simp [BitVec.toNat_eq, Nat.or_mod_two_pow, Nat.shiftLeft_eq]
/-! ### concat -/
@[simp, grind =] theorem toNat_concat (x : BitVec w) (b : Bool) :
@@ -3350,15 +3312,6 @@ theorem msb_concat {w : Nat} {b : Bool} {x : BitVec w} :
ext
simp [getElem_concat]
theorem extractLsb'_concat {x : BitVec (w + 1)} {y : Bool} :
(x.concat y).extractLsb' 0 (t + 1) = (x.extractLsb' 0 t).concat y := by
ext i hi
simp only [ getLsbD_eq_getElem, getLsbD_extractLsb', hi, decide_true, Nat.zero_add,
getLsbD_concat, Bool.true_and]
split
· simp
· simp [show i - 1 < t by omega]
/-! ### shiftConcat -/
@[grind =]
@@ -5648,7 +5601,7 @@ theorem msb_eq_toNat {x : BitVec w}:
simp only [msb_eq_decide, ge_iff_le]
/-- Negating a bitvector created from a natural number equals
creating a bitvector from the negative of that number.
creating a bitvector from the the negative of that number.
-/
theorem neg_ofNat_eq_ofInt_neg {w : Nat} {x : Nat} :
- BitVec.ofNat w x = BitVec.ofInt w (- x) := by
@@ -5863,16 +5816,6 @@ theorem reverse_reverse_eq {x : BitVec w} :
ext k hk
rw [getElem_reverse, getMsbD_reverse, getLsbD_eq_getElem]
@[simp]
theorem concat_reverse_setWidth_msb_eq_reverse {x : BitVec (w + 1)} :
concat ((x.setWidth w).reverse) x.msb = x.reverse := by
ext i hi
simp only [getElem_reverse, BitVec.msb, getElem_concat, getMsbD_setWidth, Nat.le_add_right,
Nat.sub_eq_zero_of_le, Nat.zero_le, decide_true, Bool.true_and, dite_eq_ite]
by_cases hzero : i = 0
· simp [hzero]
· simp [hzero, show i - 1 + (w + 1) - w = i by omega]
/-! ### Inequalities (le / lt) -/
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
@@ -6348,241 +6291,4 @@ theorem two_pow_ctz_le_toNat_of_ne_zero {x : BitVec w} (hx : x ≠ 0#w) :
have hclz := getLsbD_true_ctz_of_ne_zero (x := x) hx
exact Nat.ge_two_pow_of_testBit hclz
/-! ### Population Count -/
@[simp]
theorem cpopNatRec_zero_self {x : BitVec w} :
x.cpopNatRec 0 acc = acc := rfl
@[simp]
theorem cpopNatRec_succ {n : Nat} {x : BitVec w} :
x.cpopNatRec (n + 1) acc = x.cpopNatRec n (acc + (x.getLsbD n).toNat) := rfl
@[simp]
theorem cpopNatRec_zero :
(0#w).cpopNatRec n acc = acc := by
induction n
· case zero =>
simp
· case succ n ihn =>
simp [ihn]
theorem cpopNatRec_eq {x : BitVec w} {n : Nat} (acc : Nat):
x.cpopNatRec n acc = x.cpopNatRec n 0 + acc := by
induction n generalizing acc
· case zero =>
simp
· case succ n ihn =>
simp [ihn (acc := acc + (x.getLsbD n).toNat), ihn (acc := (x.getLsbD n).toNat)]
omega
theorem cpopNatRec_add {x : BitVec w} {acc n : Nat} :
x.cpopNatRec n (acc + acc') = x.cpopNatRec n acc + acc' := by
rw [cpopNatRec_eq (acc := acc + acc'), cpopNatRec_eq (acc := acc), Nat.add_assoc]
theorem cpopNatRec_le {x : BitVec w} (n : Nat) :
x.cpopNatRec n acc acc + n := by
induction n generalizing acc
· case zero =>
simp
· case succ n ihn =>
have : (x.getLsbD n).toNat 1 := by cases x.getLsbD n <;> simp
specialize ihn (acc := acc + (x.getLsbD n).toNat)
simp
omega
@[simp]
theorem cpopNatRec_of_le {x : BitVec w} (k n : Nat) (hn : w n) :
x.cpopNatRec (n + k) acc = x.cpopNatRec n acc := by
induction k
· case zero =>
simp
· case succ k ihk =>
simp [show n + (k + 1) = (n + k) + 1 by omega, ihk, show w n + k by omega]
theorem cpopNatRec_zero_le (x : BitVec w) (n : Nat) :
x.cpopNatRec n 0 w := by
induction n
· case zero =>
simp
· case succ n ihn =>
by_cases hle : n w
· by_cases hx : x.getLsbD n
· have := cpopNatRec_le (x := x) (acc := 1) (by omega)
have := lt_of_getLsbD hx
simp [hx]
omega
· have := cpopNatRec_le (x := x) (acc := 0) (by omega)
simp [hx]
omega
· simp [show w n by omega]
omega
@[simp]
theorem cpopNatRec_allOnes (h : n w) :
(allOnes w).cpopNatRec n acc = acc + n := by
induction n
· case zero =>
simp
· case succ n ihn =>
specialize ihn (by omega)
simp [show n < w by omega, ihn,
cpopNatRec_add (acc := acc) (acc' := 1)]
omega
@[simp]
theorem cpop_allOnes :
(allOnes w).cpop = BitVec.ofNat w w := by
simp [cpop, cpopNatRec_allOnes]
@[simp]
theorem cpop_zero :
(0#w).cpop = 0#w := by
simp [cpop]
theorem toNat_cpop_le (x : BitVec w) :
x.cpop.toNat w := by
have hlt := Nat.lt_two_pow_self (n := w)
have hle := cpopNatRec_zero_le (x := x) (n := w)
simp only [cpop, toNat_ofNat, ge_iff_le]
rw [Nat.mod_eq_of_lt (by omega)]
exact hle
@[simp]
theorem cpopNatRec_cons_of_le {x : BitVec w} {b : Bool} (hn : n w) :
(cons b x).cpopNatRec n acc = x.cpopNatRec n acc := by
induction n generalizing acc
· case zero =>
simp
· case succ n ihn =>
specialize ihn (acc := acc + ((cons b x).getLsbD n).toNat) (by omega)
rw [cpopNatRec_succ, ihn, getLsbD_cons]
simp [show ¬ n = w by omega]
@[simp]
theorem cpopNatRec_cons_of_lt {x : BitVec w} {b : Bool} (hn : w < n) :
(cons b x).cpopNatRec n acc = b.toNat + x.cpopNatRec n acc := by
induction n generalizing acc
· case zero =>
omega
· case succ n ihn =>
by_cases hlt : w < n
· rw [cpopNatRec_succ, ihn (acc := acc + ((cons b x).getLsbD n).toNat) (by omega)]
simp [getLsbD_cons, show ¬ n = w by omega]
· simp [show w = n by omega, getElem_cons,
cpopNatRec_add (acc := acc) (acc' := b.toNat), Nat.add_comm]
theorem cpopNatRec_concat_of_lt {x : BitVec w} {b : Bool} (hn : 0 < n) :
(concat x b).cpopNatRec n acc = b.toNat + x.cpopNatRec (n - 1) acc := by
induction n generalizing acc
· case zero =>
omega
· case succ n ihn =>
by_cases hn0 : 0 < n
· specialize ihn (acc := (acc + ((x.concat b).getLsbD n).toNat)) (by omega)
rw [cpopNatRec_succ, ihn, cpopNatRec_add (acc := acc)]
simp [getLsbD_concat, show ¬ n = 0 by omega, show n + 1 - 1 = n - 1 + 1 by omega, cpopNatRec_add]
· simp [show n = 0 by omega]
omega
theorem toNat_cpop (x : BitVec w) :
x.cpop.toNat = x.cpopNatRec w 0 := by
have := cpopNatRec_zero_le x w
have := toNat_cpop_le x
have := Nat.lt_two_pow_self (n := w)
rw [cpop, toNat_ofNat, Nat.mod_eq_of_lt]
omega
@[simp]
theorem toNat_cpop_cons {x : BitVec w} {b : Bool} :
(x.cons b).cpop.toNat = b.toNat + x.cpop.toNat := by
simp [toNat_cpop, getElem_cons, cpopNatRec_eq (acc := b.toNat), Nat.add_comm]
@[simp]
theorem cpopNatRec_setWidth_of_le (x : BitVec w) (h : pos v) :
(setWidth v x).cpopNatRec pos acc = x.cpopNatRec pos acc := by
induction pos generalizing acc
· case zero =>
simp
· case succ pos ih =>
simp only [cpopNatRec_succ, getLsbD_setWidth]
rw [ih]
· congr
by_cases h : pos < v
<;> simp [h]
omega
· omega
theorem cpop_cons {x : BitVec w} {b : Bool} :
(x.cons b).cpop = b.toNat + x.cpop.setWidth (w + 1) := by
have := toNat_cpop_le x
have := Bool.toNat_lt b
simp only [natCast_eq_ofNat, toNat_eq, toNat_add, toNat_ofNat, toNat_setWidth, Nat.lt_add_one,
toNat_mod_cancel_of_lt, Nat.mod_add_mod]
rw [toNat_cpop_cons, Nat.mod_eq_of_lt]
omega
theorem cpop_concat {x : BitVec w} {b : Bool} :
(x.concat b).cpop = b.toNat + x.cpop.setWidth (w + 1) := by
have := cpopNatRec_zero_le (x := x) (n := w)
have := Nat.lt_two_pow_self (n := w)
rw [cpop, cpop, cpopNatRec_concat_of_lt,
Nat.add_one_sub_one, natCast_eq_ofNat, ofNat_add]
congr
rw [setWidth_ofNat_of_le_of_lt (x := x.cpopNatRec w 0) (by omega) (by omega)]
omega
@[simp]
theorem toNat_cpop_concat {x : BitVec w} {b : Bool} :
(x.concat b).cpop.toNat = b.toNat + x.cpop.toNat := by
have := toNat_cpop_le (x := x)
have := Nat.lt_two_pow_self (n := w + 1)
simp only [cpop_concat, natCast_eq_ofNat, toNat_add, toNat_ofNat, toNat_setWidth, Nat.lt_add_one,
toNat_mod_cancel_of_lt, Nat.mod_add_mod]
rw [Nat.mod_eq_of_lt]
cases b <;> (simp; omega)
theorem cpop_cons_eq_cpop_concat (x : BitVec w) :
(x.cons y).cpop = (x.concat y).cpop := by
rw [cpop_cons, cpop_concat]
@[simp]
theorem cpop_reverse (x : BitVec w) :
x.reverse.cpop = x.cpop := by
induction w
· case zero =>
simp [cpop, reverse]
· case succ w ihw =>
rw [ concat_reverse_setWidth_msb_eq_reverse, cpop_concat, ihw, cpop_cons]
simp
@[simp]
theorem cpopNatRec_cast_eq_of_eq {x : BitVec w} (p : w = v) :
(x.cast p).cpopNatRec n = x.cpopNatRec n := by
subst p; simp
@[simp]
theorem cpop_cast (x : BitVec w) (h : w = v) :
(x.cast h).cpop = x.cpop.cast h := by
simp [cpop, cpopNatRec_cast_eq_of_eq, h]
@[simp]
theorem toNat_cpop_append {x : BitVec w} {y : BitVec u} :
(x ++ y).cpop.toNat = x.cpop.toNat + y.cpop.toNat := by
induction w generalizing u
· case zero =>
simp [cpop]
· case succ w ihw =>
rw [ cons_msb_setWidth x, toNat_cpop_cons, cons_append, cpop_cast, toNat_cast,
toNat_cpop_cons, ihw, Nat.add_assoc]
theorem cpop_append {x : BitVec w} {y : BitVec u} :
(x ++ y).cpop = x.cpop.setWidth (w + u) + y.cpop.setWidth (w + u) := by
apply eq_of_toNat_eq
have := toNat_cpop_le x
have := toNat_cpop_le y
have := Nat.lt_two_pow_self (n := w + u)
simp only [toNat_cpop_append, toNat_add, toNat_setWidth, Nat.add_mod_mod, Nat.mod_add_mod]
rw [Nat.mod_eq_of_lt (by omega)]
end BitVec

View File

@@ -260,7 +260,7 @@ instance : Std.Associative (· != ·) := ⟨bne_assoc⟩
theorem eq_not_of_ne : {x y : Bool}, x y x = !y := by decide
/-! ### coercion related normal forms -/
/-! ### coercision related normal forms -/
theorem beq_eq_decide_eq [BEq α] [LawfulBEq α] [DecidableEq α] (a b : α) :
(a == b) = decide (a = b) := by

View File

@@ -132,11 +132,6 @@ Copies the bytes with indices {name}`b` (inclusive) to {name}`e` (exclusive) to
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
a.copySlice b empty 0 (e - b)
/--
Appends two byte arrays using fast array primitives instead of converting them into lists and back.
In compiled code, this function replaces calls to {name}`ByteArray.append`.
-/
@[inline]
protected def fastAppend (a : ByteArray) (b : ByteArray) : ByteArray :=
-- we assume that `append`s may be repeated, so use asymptotic growing; use `copySlice` directly to customize
@@ -248,7 +243,7 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : ByteAr
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
loop as.size (Nat.le_refl _) b
instance [Monad m] : ForIn m ByteArray UInt8 where
instance : ForIn m ByteArray UInt8 where
forIn := ByteArray.forIn
/--
@@ -269,8 +264,6 @@ unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β
if start < stop then
if stop as.size then
fold (USize.ofNat start) (USize.ofNat stop) init
else if start < as.size then
fold (USize.ofNat start) (USize.ofNat as.size) init
else
pure init
else

View File

@@ -102,7 +102,7 @@ Returns `true` if the character is a uppercase ASCII letter.
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
-/
@[inline] def isUpper (c : Char) : Bool :=
c.val 'A'.val c.val 'Z'.val
c.val 65 && c.val 90
/--
Returns `true` if the character is a lowercase ASCII letter.
@@ -110,7 +110,7 @@ Returns `true` if the character is a lowercase ASCII letter.
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
-/
@[inline] def isLower (c : Char) : Bool :=
c.val 'a'.val && c.val 'z'.val
c.val 97 && c.val 122
/--
Returns `true` if the character is an ASCII letter.
@@ -126,7 +126,7 @@ Returns `true` if the character is an ASCII digit.
The ASCII digits are the following: `0123456789`.
-/
@[inline] def isDigit (c : Char) : Bool :=
c.val '0'.val && c.val '9'.val
c.val 48 && c.val 57
/--
Returns `true` if the character is an ASCII letter or digit.
@@ -143,16 +143,9 @@ alphabet are returned unchanged.
The uppercase ASCII letters are the following: `ABCDEFGHIJKLMNOPQRSTUVWXYZ`.
-/
@[inline]
def toLower (c : Char) : Char :=
if h : c.val 'A'.val c.val 'Z'.val then
c.val + ('a'.val - 'A'.val), ?_
else
c
where finally
have h : c.val.toBitVec.toNat + ('a'.val - 'A'.val).toBitVec.toNat < 0xd800 :=
Nat.add_lt_add_right (Nat.lt_of_le_of_lt h.2 (by decide)) _
exact .inl (lt_of_eq_of_lt (Nat.mod_eq_of_lt (Nat.lt_trans h (by decide))) h)
let n := toNat c;
if n >= 65 n <= 90 then ofNat (n + 32) else c
/--
Converts a lowercase ASCII letter to the corresponding uppercase letter. Letters outside the ASCII
@@ -160,20 +153,8 @@ alphabet are returned unchanged.
The lowercase ASCII letters are the following: `abcdefghijklmnopqrstuvwxyz`.
-/
@[inline]
def toUpper (c : Char) : Char :=
if h : c.val 'a'.val c.val 'z'.val then
c.val + ('A'.val - 'a'.val), ?_
else
c
where finally
have h₁ : 2^32 c.val.toNat + ('A'.val - 'a'.val).toNat :=
@Nat.add_le_add 'a'.val.toNat _ (2^32 - 'a'.val.toNat) _ h.1 (by decide)
have h₂ : c.val.toBitVec.toNat + ('A'.val - 'a'.val).toNat < 2^32 + 0xd800 :=
Nat.add_lt_add_right (Nat.lt_of_le_of_lt h.2 (by decide)) _
have add_eq {x y : UInt32} : (x + y).toNat = (x.toNat + y.toNat) % 2^32 := rfl
replace h₂ := Nat.sub_lt_left_of_lt_add h₁ h₂
exact .inl <| lt_of_eq_of_lt (add_eq.trans (Nat.mod_eq_sub_mod h₁) |>.trans
(Nat.mod_eq_of_lt (Nat.lt_trans h₂ (by decide)))) h₂
let n := toNat c;
if n >= 97 n <= 122 then ofNat (n - 32) else c
end Char

View File

@@ -56,6 +56,10 @@ theorem Internal.ofNat_eq_ofNat {n : Nat} {hn} {a : Nat} :
letI : NeZero n := Nat.pos_iff_ne_zero.1 hn
Fin.Internal.ofNat n hn a = Fin.ofNat n a := rfl
@[deprecated Fin.ofNat (since := "2025-05-28")]
protected def ofNat' (n : Nat) [NeZero n] (a : Nat) : Fin n :=
Fin.ofNat n a
-- We provide this because other similar types have a `toNat` function, but `simp` rewrites
-- `i.toNat` to `i.val`.
/--
@@ -242,11 +246,6 @@ instance neg (n : Nat) : Neg (Fin n) :=
theorem neg_def (a : Fin n) : -a = (n - a) % n, Nat.mod_lt _ a.pos := rfl
-- Later we give another version called `Fin.val_neg` that splits on `a = 0`.
protected theorem val_neg' (a : Fin n) : ((-a : Fin n) : Nat) = (n - a) % n :=
rfl
@[deprecated Fin.val_neg' (since := "2025-11-21")]
protected theorem coe_neg (a : Fin n) : ((-a : Fin n) : Nat) = (n - a) % n :=
rfl

View File

@@ -16,23 +16,17 @@ open Std
namespace Fin
@[simp, grind =] theorem ofNat_zero (n : Nat) [NeZero n] : Fin.ofNat n 0 = 0 := rfl
@[simp] theorem ofNat_zero (n : Nat) [NeZero n] : Fin.ofNat n 0 = 0 := rfl
@[deprecated ofNat_zero (since := "2025-05-28")] abbrev ofNat'_zero := @ofNat_zero
theorem mod_def (a m : Fin n) : a % m = Fin.mk (a.val % m.val) (Nat.lt_of_le_of_lt (Nat.mod_le _ _) a.2) :=
rfl
theorem val_mod (a m : Fin n) : (a % m).val = a.val % m.val := rfl
theorem mul_def (a b : Fin n) : a * b = Fin.mk ((a.val * b.val) % n) (Nat.mod_lt _ a.pos) := rfl
theorem val_mul (a b : Fin n) : (a * b).val = (a.val * b.val) % n := rfl
theorem sub_def (a b : Fin n) : a - b = Fin.mk (((n - b.val) + a.val) % n) (Nat.mod_lt _ a.pos) := rfl
@[grind =]
theorem val_sub (a b : Fin n) : (a - b).val = ((n - b.val) + a.val) % n := rfl
@[grind ]
theorem pos' : [Nonempty (Fin n)], 0 < n | i => i.pos
@[simp] theorem is_lt (a : Fin n) : (a : Nat) < n := a.2
@@ -44,8 +38,7 @@ theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
@[simp] protected theorem eta (a : Fin n) (h : a < n) : (a, h : Fin n) = a := rfl
@[ext, grind ext]
protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
@[ext] protected theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem val_ne_iff {a b : Fin n} : a.1 b.1 a b := not_congr val_inj
@@ -74,23 +67,29 @@ theorem mk_val (i : Fin n) : (⟨i, i.isLt⟩ : Fin n) = i := Fin.eta ..
@[simp, grind =] theorem val_ofNat (n : Nat) [NeZero n] (a : Nat) :
(Fin.ofNat n a).val = a % n := rfl
@[simp, grind =] theorem ofNat_self {n : Nat} [NeZero n] : Fin.ofNat n n = 0 := by
@[deprecated val_ofNat (since := "2025-05-28")] abbrev val_ofNat' := @val_ofNat
@[simp] theorem ofNat_self {n : Nat} [NeZero n] : Fin.ofNat n n = 0 := by
ext
simp
congr
@[deprecated ofNat_self (since := "2025-05-28")] abbrev ofNat'_self := @ofNat_self
@[simp] theorem ofNat_val_eq_self [NeZero n] (x : Fin n) : (Fin.ofNat n x.val) = x := by
ext
rw [val_ofNat, Nat.mod_eq_of_lt]
exact x.2
@[deprecated ofNat_val_eq_self (since := "2025-05-28")] abbrev ofNat'_val_eq_self := @ofNat_val_eq_self
@[simp] theorem mod_val (a b : Fin n) : (a % b).val = a.val % b.val :=
rfl
@[simp] theorem div_val (a b : Fin n) : (a / b).val = a.val / b.val :=
rfl
@[simp, grind =] theorem modn_val (a : Fin n) (b : Nat) : (a.modn b).val = a.val % b :=
@[simp] theorem modn_val (a : Fin n) (b : Nat) : (a.modn b).val = a.val % b :=
rfl
@[simp] theorem val_eq_zero (a : Fin 1) : a.val = 0 :=
@@ -260,9 +259,7 @@ instance : LawfulOrderLT (Fin n) where
lt_iff := by
simp [ Fin.not_le, Decidable.imp_iff_not_or, Std.Total.total]
@[simp] theorem val_rev (i : Fin n) : (rev i).val = n - (i + 1) := rfl
grind_pattern val_rev => i.rev
@[simp, grind =] theorem val_rev (i : Fin n) : (rev i).val = n - (i + 1) := rfl
@[simp] theorem rev_rev (i : Fin n) : rev (rev i) = i := Fin.ext <| by
rw [val_rev, val_rev, Nat.sub_sub, Nat.sub_sub_self (by exact i.2), Nat.add_sub_cancel]
@@ -287,8 +284,6 @@ theorem rev_eq {n a : Nat} (i : Fin (n + 1)) (h : n = a + i) :
@[simp] theorem val_last (n : Nat) : (last n).1 = n := rfl
grind_pattern val_last => last n
@[simp] theorem last_zero : (Fin.last 0 : Fin 1) = 0 := by
ext
simp
@@ -398,8 +393,6 @@ theorem zero_ne_one : (0 : Fin (n + 2)) ≠ 1 := Fin.ne_of_lt zero_lt_one
@[simp] theorem val_succ (j : Fin n) : (j.succ : Nat) = j + 1 := rfl
grind_pattern val_succ => j.succ
@[simp] theorem succ_pos (a : Fin n) : (0 : Fin (n + 1)) < a.succ := by
simp [Fin.lt_def]
@@ -460,18 +453,12 @@ theorem one_lt_succ_succ (a : Fin n) : (1 : Fin (n + 2)) < a.succ.succ := by
theorem succ_succ_ne_one (a : Fin n) : Fin.succ (Fin.succ a) 1 :=
Fin.ne_of_gt (one_lt_succ_succ a)
@[simp, grind =] theorem val_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
@[deprecated val_castLT (since := "2025-11-21")]
theorem coe_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
@[simp] theorem coe_castLT (i : Fin m) (h : i.1 < n) : (castLT i h : Nat) = i := rfl
@[simp] theorem castLT_mk (i n m : Nat) (hn : i < n) (hm : i < m) : castLT i, hn hm = i, hm :=
rfl
@[simp, grind =] theorem val_castLE (h : n m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[deprecated val_castLE (since := "2025-11-21")]
theorem coe_castLE (h : n m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[simp, grind =] theorem coe_castLE (h : n m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[simp] theorem castLE_mk (i n m : Nat) (hn : i < n) (h : n m) :
castLE h i, hn = i, Nat.lt_of_lt_of_le hn h := rfl
@@ -483,16 +470,13 @@ theorem coe_castLE (h : n ≤ m) (i : Fin n) : (castLE h i : Nat) = i := rfl
@[simp] theorem castLE_castLE {k m n} (km : k m) (mn : m n) (i : Fin k) :
Fin.castLE mn (Fin.castLE km i) = Fin.castLE (Nat.le_trans km mn) i :=
Fin.ext (by simp only [val_castLE])
Fin.ext (by simp only [coe_castLE])
@[simp] theorem castLE_comp_castLE {k m n} (km : k m) (mn : m n) :
Fin.castLE mn Fin.castLE km = Fin.castLE (Nat.le_trans km mn) :=
funext (castLE_castLE km mn)
@[simp, grind =] theorem val_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[deprecated val_cast (since := "2025-11-21")]
theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp] theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp] theorem cast_castLE {k m n} (km : k m) (mn : m = n) (i : Fin k) :
Fin.cast mn (i.castLE km) = i.castLE (mn km) :=
@@ -505,7 +489,7 @@ theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
@[simp] theorem cast_zero [NeZero n] [NeZero m] (h : n = m) : Fin.cast h 0 = 0 := rfl
@[simp] theorem cast_last {n' : Nat} {h : n + 1 = n' + 1} : (last n).cast h = last n' :=
Fin.ext (by rw [val_cast, val_last, val_last, Nat.succ.inj h])
Fin.ext (by rw [coe_cast, val_last, val_last, Nat.succ.inj h])
@[simp] theorem cast_mk (h : n = m) (i : Nat) (hn : i < n) : Fin.cast h i, hn = i, h hn := rfl
@@ -520,10 +504,7 @@ theorem coe_cast (h : n = m) (i : Fin n) : (i.cast h : Nat) = i := rfl
theorem castLE_of_eq {m n : Nat} (h : m = n) {h' : m n} : castLE h' = Fin.cast h := rfl
@[simp, grind =] theorem val_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[deprecated val_castAdd (since := "2025-11-21")]
theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[simp] theorem coe_castAdd (m : Nat) (i : Fin n) : (castAdd m i : Nat) = i := rfl
@[simp] theorem castAdd_zero : (castAdd 0 : Fin n Fin (n + 0)) = Fin.cast rfl := rfl
@@ -559,10 +540,7 @@ the reverse direction. -/
theorem succ_cast_eq {n' : Nat} (i : Fin n) (h : n = n') :
(i.cast h).succ = i.succ.cast (by rw [h]) := rfl
@[simp, grind =] theorem val_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[deprecated val_castSucc (since := "2025-11-21")]
theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[simp] theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
@[simp] theorem castSucc_mk (n i : Nat) (h : i < n) : castSucc i, h = i, Nat.lt_succ_of_lt h := rfl
@@ -570,7 +548,7 @@ theorem coe_castSucc (i : Fin n) : (i.castSucc : Nat) = i := rfl
i.castSucc.cast h = (i.cast (Nat.succ.inj h)).castSucc := rfl
theorem castSucc_lt_succ {i : Fin n} : i.castSucc < i.succ :=
lt_def.2 <| by simp only [val_castSucc, val_succ, Nat.lt_succ_self]
lt_def.2 <| by simp only [coe_castSucc, val_succ, Nat.lt_succ_self]
theorem le_castSucc_iff {i : Fin (n + 1)} {j : Fin n} : i j.castSucc i < j.succ := by
simpa only [lt_def, le_def] using Nat.add_one_le_add_one_iff.symm
@@ -624,7 +602,7 @@ theorem coeSucc_eq_succ {a : Fin n} : a.castSucc + 1 = a.succ := by
@[deprecated castSucc_lt_succ (since := "2025-10-29")]
theorem lt_succ {a : Fin n} : a.castSucc < a.succ := by
rw [castSucc, lt_def, val_castAdd, val_succ]; exact Nat.lt_succ_self a.val
rw [castSucc, lt_def, coe_castAdd, val_succ]; exact Nat.lt_succ_self a.val
theorem exists_castSucc_eq {n : Nat} {i : Fin (n + 1)} : ( j, castSucc j = i) i last n :=
fun j, hj => hj Fin.ne_of_lt j.castSucc_lt_last,
@@ -632,10 +610,7 @@ theorem exists_castSucc_eq {n : Nat} {i : Fin (n + 1)} : (∃ j, castSucc j = i)
theorem succ_castSucc {n : Nat} (i : Fin n) : i.castSucc.succ = i.succ.castSucc := rfl
@[simp, grind =] theorem val_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@[deprecated val_addNat (since := "2025-11-21")]
theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@[simp] theorem coe_addNat (m : Nat) (i : Fin n) : (addNat i m : Nat) = i + m := rfl
@[simp] theorem addNat_zero (n : Nat) (i : Fin n) : addNat i 0 = i := by
ext
@@ -663,10 +638,7 @@ theorem cast_addNat_left {n n' m : Nat} (i : Fin n') (h : n' + m = n + m) :
(addNat i m').cast h = addNat i m :=
Fin.ext <| (congrArg ((· + ·) (i : Nat)) (Nat.add_left_cancel h) : _)
@[simp, grind =] theorem val_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@[deprecated val_natAdd (since := "2025-11-21")]
theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@[simp] theorem coe_natAdd (n : Nat) {m : Nat} (i : Fin m) : (natAdd n i : Nat) = n + i := rfl
@[simp] theorem natAdd_mk (n i : Nat) (hi : i < m) :
natAdd n i, hi = n + i, Nat.add_lt_add_left hi n := rfl
@@ -723,7 +695,7 @@ theorem natAdd_castSucc {m n : Nat} {i : Fin m} : natAdd n (castSucc i) = castSu
omega
theorem rev_castAdd (k : Fin n) (m : Nat) : rev (castAdd m k) = addNat (rev k) m := Fin.ext <| by
rw [val_rev, val_castAdd, val_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
rw [val_rev, coe_castAdd, coe_addNat, val_rev, Nat.sub_add_comm (Nat.succ_le_of_lt k.is_lt)]
theorem rev_addNat (k : Fin n) (m : Nat) : rev (addNat k m) = castAdd m (rev k) := by
rw [ rev_rev (castAdd ..), rev_castAdd, rev_rev]
@@ -745,12 +717,7 @@ theorem castSucc_natAdd (n : Nat) (i : Fin k) :
/-! ### pred -/
@[simp] theorem val_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
grind_pattern val_pred => j.pred h
@[deprecated val_pred (since := "2025-11-21")]
theorem coe_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
@[simp] theorem coe_pred (j : Fin (n + 1)) (h : j 0) : (j.pred h : Nat) = j - 1 := rfl
@[simp] theorem succ_pred : (i : Fin (n + 1)) (h : i 0), (i.pred h).succ = i
| 0, _, hi => by simp only [mk_zero, ne_eq, not_true] at hi
@@ -768,7 +735,7 @@ theorem pred_eq_iff_eq_succ {n : Nat} {i : Fin (n + 1)} (hi : i ≠ 0) {j : Fin
theorem pred_mk_succ (i : Nat) (h : i < n + 1) :
Fin.pred i + 1, Nat.add_lt_add_right h 1 (ne_of_val_ne (Nat.ne_of_gt (mk_succ_pos i h))) =
i, h := by
simp only [Fin.ext_iff, val_pred, Nat.add_sub_cancel]
simp only [Fin.ext_iff, coe_pred, Nat.add_sub_cancel]
@[simp] theorem pred_mk_succ' (i : Nat) (h₁ : i + 1 < n + 1 + 1) (h₂) :
Fin.pred i + 1, h₁ h₂ = i, Nat.lt_of_succ_lt_succ h₁ := pred_mk_succ i _
@@ -795,13 +762,10 @@ theorem pred_mk {n : Nat} (i : Nat) (h : i < n + 1) (w) : Fin.pred ⟨i, h⟩ w
theorem pred_add_one (i : Fin (n + 2)) (h : (i : Nat) < n + 1) :
pred (i + 1) (Fin.ne_of_gt (add_one_pos _ (lt_def.2 h))) = castLT i h := by
rw [Fin.ext_iff, val_pred, val_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
rw [Fin.ext_iff, coe_pred, coe_castLT, val_add, val_one, Nat.mod_eq_of_lt, Nat.add_sub_cancel]
exact Nat.add_lt_add_right h 1
@[simp, grind =] theorem val_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@[deprecated val_subNat (since := "2025-11-21")]
theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@[simp] theorem coe_subNat (i : Fin (n + m)) (h : m i) : (i.subNat m h : Nat) = i - m := rfl
@[simp] theorem subNat_mk {i : Nat} (h₁ : i < n + m) (h₂ : m i) :
subNat m i, h₁ h₂ = i - m, Nat.sub_lt_right_of_lt_add h₂ h₁ := rfl
@@ -866,11 +830,11 @@ step. `Fin.succRec` is a version of this induction principle that takes the `Fin
(zero : n, motive (n + 1) 0) (succ : n i, motive n i motive (Nat.succ n) i.succ) :
motive n i := i.succRec zero succ
@[simp, grind =] theorem succRecOn_zero {motive : n, Fin n Sort _} {zero succ} (n) :
@[simp] theorem succRecOn_zero {motive : n, Fin n Sort _} {zero succ} (n) :
@Fin.succRecOn (n + 1) 0 motive zero succ = zero n := by
cases n <;> rfl
@[simp, grind =] theorem succRecOn_succ {motive : n, Fin n Sort _} {zero succ} {n} (i : Fin n) :
@[simp] theorem succRecOn_succ {motive : n, Fin n Sort _} {zero succ} {n} (i : Fin n) :
@Fin.succRecOn (n + 1) i.succ motive zero succ = succ n i (Fin.succRecOn i zero succ) := by
cases i; rfl
@@ -898,11 +862,11 @@ where
| 0, hi => by rwa [Fin.mk_zero]
| i+1, hi => succ i, Nat.lt_of_succ_lt_succ hi (go i (Nat.lt_of_succ_lt hi))
@[simp, grind =] theorem induction_zero {motive : Fin (n + 1) Sort _} (zero : motive 0)
@[simp] theorem induction_zero {motive : Fin (n + 1) Sort _} (zero : motive 0)
(hs : i : Fin n, motive (castSucc i) motive i.succ) :
(induction zero hs : i : Fin (n + 1), motive i) 0 = zero := rfl
@[simp, grind =] theorem induction_succ {motive : Fin (n + 1) Sort _} (zero : motive 0)
@[simp] theorem induction_succ {motive : Fin (n + 1) Sort _} (zero : motive 0)
(succ : i : Fin n, motive (castSucc i) motive i.succ) (i : Fin n) :
induction (motive := motive) zero succ i.succ = succ i (induction zero succ (castSucc i)) := rfl
@@ -934,13 +898,13 @@ The corresponding induction principle is `Fin.induction`.
(zero : motive 0) (succ : i : Fin n, motive i.succ) :
i : Fin (n + 1), motive i := induction zero fun i _ => succ i
@[simp, grind =] theorem cases_zero {n} {motive : Fin (n + 1) Sort _} {zero succ} :
@[simp] theorem cases_zero {n} {motive : Fin (n + 1) Sort _} {zero succ} :
@Fin.cases n motive zero succ 0 = zero := rfl
@[simp, grind =] theorem cases_succ {n} {motive : Fin (n + 1) Sort _} {zero succ} (i : Fin n) :
@[simp] theorem cases_succ {n} {motive : Fin (n + 1) Sort _} {zero succ} (i : Fin n) :
@Fin.cases n motive zero succ i.succ = succ i := rfl
@[simp, grind =] theorem cases_succ' {n} {motive : Fin (n + 1) Sort _} {zero succ}
@[simp] theorem cases_succ' {n} {motive : Fin (n + 1) Sort _} {zero succ}
{i : Nat} (h : i + 1 < n + 1) :
@Fin.cases n motive zero succ i.succ, h = succ i, Nat.lt_of_succ_lt_succ h := rfl
@@ -990,7 +954,7 @@ For the induction:
| j + 1 => go j (by omega) (by omega) (cast j, by omega x)
go _ _ (by omega) last
@[simp, grind =] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ} :
@[simp] theorem reverseInduction_last {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ} :
(reverseInduction zero succ (Fin.last n) : motive (Fin.last n)) = zero := by
rw [reverseInduction, reverseInduction.go]; simp
@@ -1007,7 +971,7 @@ private theorem reverseInduction_castSucc_aux {n : Nat} {motive : Fin (n + 1)
dsimp only
rw [ih _ _ (by omega), eq_comm, reverseInduction.go, dif_neg (by change i.1 + 1 _; omega)]
@[simp, grind =] theorem reverseInduction_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ}
@[simp] theorem reverseInduction_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {zero succ}
(i : Fin n) : reverseInduction (motive := motive) zero succ (castSucc i) =
succ i (reverseInduction zero succ i.succ) := by
rw [reverseInduction, reverseInduction_castSucc_aux _ _ _ i.isLt, reverseInduction]
@@ -1026,11 +990,11 @@ The corresponding induction principle is `Fin.reverseInduction`.
(cast : i : Fin n, motive (castSucc i)) (i : Fin (n + 1)) : motive i :=
reverseInduction last (fun i _ => cast i) i
@[simp, grind =] theorem lastCases_last {n : Nat} {motive : Fin (n + 1) Sort _} {last cast} :
@[simp] theorem lastCases_last {n : Nat} {motive : Fin (n + 1) Sort _} {last cast} :
(Fin.lastCases last cast (Fin.last n) : motive (Fin.last n)) = last :=
reverseInduction_last ..
@[simp, grind =] theorem lastCases_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {last cast}
@[simp] theorem lastCases_castSucc {n : Nat} {motive : Fin (n + 1) Sort _} {last cast}
(i : Fin n) : (Fin.lastCases last cast (Fin.castSucc i) : motive (Fin.castSucc i)) = cast i :=
reverseInduction_castSucc ..
@@ -1050,11 +1014,11 @@ as `Fin.natAdd m (j : Fin n)`.
if hi : (i : Nat) < m then (castAdd_castLT n i hi) (left (castLT i hi))
else (natAdd_subNat_cast (Nat.le_of_not_lt hi)) (right _)
@[simp, grind =] theorem addCases_left {m n : Nat} {motive : Fin (m + n) Sort _} {left right} (i : Fin m) :
@[simp] theorem addCases_left {m n : Nat} {motive : Fin (m + n) Sort _} {left right} (i : Fin m) :
addCases (motive := motive) left right (Fin.castAdd n i) = left i := by
rw [addCases, dif_pos (castAdd_lt _ _)]; rfl
@[simp, grind =]
@[simp]
theorem addCases_right {m n : Nat} {motive : Fin (m + n) Sort _} {left right} (i : Fin n) :
addCases (motive := motive) left right (natAdd m i) = right i := by
have : ¬(natAdd m i : Nat) < m := Nat.not_lt.2 (le_coe_natAdd ..)
@@ -1076,14 +1040,17 @@ theorem ofNat_add [NeZero n] (x : Nat) (y : Fin n) :
apply Fin.eq_of_val_eq
simp [Fin.ofNat, Fin.add_def]
@[deprecated ofNat_add (since := "2025-05-28")] abbrev ofNat_add' := @ofNat_add
theorem add_ofNat [NeZero n] (x : Fin n) (y : Nat) :
x + Fin.ofNat n y = Fin.ofNat n (x.val + y) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat, Fin.add_def]
@[deprecated add_ofNat (since := "2025-05-28")] abbrev add_ofNat' := @add_ofNat
/-! ### sub -/
@[deprecated val_sub (since := "2025-11-21")]
protected theorem coe_sub (a b : Fin n) : ((a - b : Fin n) : Nat) = ((n - b) + a) % n := by
cases a; cases b; rfl
@@ -1092,11 +1059,15 @@ theorem ofNat_sub [NeZero n] (x : Nat) (y : Fin n) :
apply Fin.eq_of_val_eq
simp [Fin.ofNat, Fin.sub_def]
@[deprecated ofNat_sub (since := "2025-05-28")] abbrev ofNat_sub' := @ofNat_sub
theorem sub_ofNat [NeZero n] (x : Fin n) (y : Nat) :
x - Fin.ofNat n y = Fin.ofNat n ((n - y % n) + x.val) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat, Fin.sub_def]
@[deprecated sub_ofNat (since := "2025-05-28")] abbrev sub_ofNat' := @sub_ofNat
@[simp] protected theorem sub_self [NeZero n] {x : Fin n} : x - x = 0 := by
ext
rw [Fin.sub_def]
@@ -1131,7 +1102,6 @@ theorem coe_sub_iff_lt {a b : Fin n} : (↑(a - b) : Nat) = n + a - b ↔ a < b
/-! ### neg -/
@[grind =]
theorem val_neg {n : Nat} [NeZero n] (x : Fin n) :
(-x).val = if x = 0 then 0 else n - x.val := by
change (n - x) % n = _
@@ -1147,7 +1117,7 @@ protected theorem sub_eq_add_neg {n : Nat} (x y : Fin n) : x - y = x + -y := by
apply elim0 x
· replace h : NeZero n := h
ext
rw [Fin.val_sub, Fin.val_add, val_neg]
rw [Fin.coe_sub, Fin.val_add, val_neg]
split
· simp_all
· simp [Nat.add_comm]
@@ -1159,11 +1129,18 @@ theorem ofNat_mul [NeZero n] (x : Nat) (y : Fin n) :
apply Fin.eq_of_val_eq
simp [Fin.ofNat, Fin.mul_def]
@[deprecated ofNat_mul (since := "2025-05-28")] abbrev ofNat_mul' := @ofNat_mul
theorem mul_ofNat [NeZero n] (x : Fin n) (y : Nat) :
x * Fin.ofNat n y = Fin.ofNat n (x.val * y) := by
apply Fin.eq_of_val_eq
simp [Fin.ofNat, Fin.mul_def]
@[deprecated mul_ofNat (since := "2025-05-28")] abbrev mul_ofNat' := @mul_ofNat
theorem val_mul {n : Nat} : a b : Fin n, (a * b).val = a.val * b.val % n
| _, _, _, _ => rfl
@[deprecated val_mul (since := "2025-10-26")]
theorem coe_mul {n : Nat} : a b : Fin n, ((a * b : Fin n) : Nat) = a * b % n
| _, _, _, _ => rfl

View File

@@ -42,7 +42,7 @@ instance : EmptyCollection FloatArray where
def push : FloatArray Float FloatArray
| ds, b => ds.push b
@[extern "lean_float_array_size", tagged_return]
@[extern "lean_float_array_size"]
def size : (@& FloatArray) Nat
| ds => ds.size
@@ -129,7 +129,7 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : FloatA
| ForInStep.yield b => loop i (Nat.le_of_lt h') b
loop as.size (Nat.le_refl _) b
instance [Monad m] : ForIn m FloatArray Float where
instance : ForIn m FloatArray Float where
forIn := FloatArray.forIn
/-- See comment at `forInUnsafe` -/
@@ -144,8 +144,6 @@ unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β
if start < stop then
if stop as.size then
fold (USize.ofNat start) (USize.ofNat stop) init
else if start < as.size then
fold (USize.ofNat start) (USize.ofNat as.size) init
else
pure init
else

View File

@@ -7,7 +7,7 @@ module
prelude
public import Init.Data.Array.Basic
import Init.Data.String.Search
import Init.Data.String.Basic
public section
@@ -47,7 +47,7 @@ Converts a string to a pretty-printer document, replacing newlines in the string
`Std.Format.line`.
-/
def String.toFormat (s : String) : Std.Format :=
Std.Format.joinSep (s.split '\n').toList Std.Format.line
Std.Format.joinSep (s.splitOn "\n") Std.Format.line
instance : ToFormat String.Pos.Raw where
format p := format p.byteIdx

View File

@@ -42,7 +42,6 @@ larger numbers use a fast arbitrary-precision arithmetic library (usually
than the platform's pointer size (i.e. 63 bits on 64-bit architectures and 31 bits on 32-bit
architectures).
-/
@[suggest_for ]
inductive Int : Type where
/--
A natural number is an integer.
@@ -279,11 +278,7 @@ set_option bootstrap.genMatcherCode false in
def decNonneg (m : @& Int) : Decidable (NonNeg m) :=
match m with
| ofNat m => isTrue <| NonNeg.mk m
| -[i +1] => isFalse <| fun h =>
have : j, (j = -[i +1]) NonNeg j False := fun _ hj hnn =>
Int.NonNeg.casesOn (motive := fun j _ => j = -[i +1] False) hnn
(fun _ h => Int.noConfusion h) hj
this -[i +1] rfl h
| -[_ +1] => isFalse <| fun h => nomatch h
/-- Decides whether `a ≤ b`.

View File

@@ -1781,16 +1781,6 @@ theorem ediv_lt_ediv_iff_of_dvd_of_neg_of_neg {a b c d : Int} (hb : b < 0) (hd :
obtain x, rfl, y, rfl := hba, hdc
simp [*, Int.ne_of_lt, d.mul_assoc, b.mul_comm]
theorem ediv_lt_ediv_of_lt {a b c : Int} (h : a < b) (hcb : c b) (hc : 0 < c) :
a / c < b / c :=
Int.lt_ediv_of_mul_lt (Int.le_of_lt hc) hcb
(Int.lt_of_le_of_lt (Int.ediv_mul_le _ (Int.ne_of_gt hc)) h)
theorem ediv_lt_ediv_of_lt_of_neg {a b c : Int} (h : b < a) (hca : c a) (hc : c < 0) :
a / c < b / c :=
(Int.ediv_lt_iff_of_dvd_of_neg hc hca).2
(Int.lt_of_le_of_lt (Int.mul_ediv_self_le (Int.ne_of_lt hc)) h)
/-! ### `tdiv` and ordering -/
-- Theorems about `tdiv` and ordering, whose `ediv` analogues are in `Bootstrap.lean`.

View File

@@ -29,6 +29,13 @@ theorem subNatNat_of_sub_eq_succ {m n k : Nat} (h : n - m = succ k) : subNatNat
@[norm_cast] theorem natCast_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
@[norm_cast] theorem natCast_add_one (n : Nat) : ((n + 1 : Nat) : Int) = n + 1 := rfl
@[deprecated natCast_add (since := "2025-04-17")]
theorem ofNat_add (n m : Nat) : ((n + m) : Int) = n + m := rfl
@[deprecated natCast_mul (since := "2025-04-17")]
theorem ofNat_mul (n m : Nat) : ((n * m) : Int) = n * m := rfl
@[deprecated natCast_succ (since := "2025-04-17")]
theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
theorem neg_ofNat_succ (n : Nat) : -(succ n : Int) = -[n+1] := rfl
@[simp] theorem neg_negSucc (n : Nat) : -(-[n+1]) = ((n + 1 : Nat) : Int) := rfl
@@ -333,12 +340,6 @@ protected theorem sub_sub_self (a b : Int) : a - (a - b) = b := by
@[simp] protected theorem add_sub_cancel (a b : Int) : a + b - b = a :=
Int.add_neg_cancel_right a b
protected theorem add_sub_add_right (n k m : Int) : (n + k) - (m + k) = n - m := by
rw [Int.add_comm m, Int.sub_sub, Int.add_sub_cancel]
protected theorem add_sub_add_left (k n m : Int) : (k + n) - (k + m) = n - m := by
rw [Int.add_comm k, Int.add_comm k, Int.add_sub_add_right]
protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.add_neg_eq_sub]
@@ -552,7 +553,6 @@ protected theorem mul_eq_zero {a b : Int} : a * b = 0 ↔ a = 0 b = 0 := by
| .ofNat 0, _, _ => by simp
| _, .ofNat 0, _ => by simp
| .ofNat (_+1), .negSucc _, h => by cases h
| .negSucc _, .negSucc _, h => by cases h
protected theorem mul_ne_zero {a b : Int} (a0 : a 0) (b0 : b 0) : a * b 0 :=
Or.rec a0 b0 Int.mul_eq_zero.mp

View File

@@ -81,7 +81,10 @@ theorem lt.dest {a b : Int} (h : a < b) : ∃ n : Nat, a + Nat.succ n = b :=
@[simp, norm_cast] theorem ofNat_lt {n m : Nat} : (n : Int) < m n < m := by
rw [lt_iff_add_one_le, natCast_succ, ofNat_le]; rfl
@[simp high, norm_cast] theorem natCast_pos {n : Nat} : (0 : Int) < n 0 < n := ofNat_lt
@[simp, norm_cast] theorem natCast_pos {n : Nat} : (0 : Int) < n 0 < n := ofNat_lt
@[deprecated natCast_pos (since := "2025-05-13"), simp high]
theorem ofNat_pos {n : Nat} : 0 < (n : Int) 0 < n := ofNat_lt
@[simp]
theorem natCast_nonneg (n : Nat) : 0 (n : Int) := _
@@ -89,8 +92,6 @@ theorem natCast_nonneg (n : Nat) : 0 ≤ (n : Int) := ⟨_⟩
@[deprecated natCast_nonneg (since := "2025-10-26")]
theorem ofNat_zero_le (n : Nat) : 0 (n : Int) := ofNat_le.2 n.zero_le
-- This was still being used in `omega` as of 2025-12-12,
-- so we're keeping this for another month.
@[deprecated natCast_nonneg (since := "2025-05-13")]
theorem ofNat_nonneg (n : Nat) : 0 (n : Int) := _
@@ -376,15 +377,6 @@ protected theorem le_iff_lt_add_one {a b : Int} : a ≤ b ↔ a < b + 1 := by
@[grind =] protected theorem max_def (n m : Int) : max n m = if n m then m else n := rfl
end Int
namespace Lean.Meta.Grind.Lia
scoped grind_pattern Int.min_def => min n m
scoped grind_pattern Int.max_def => max n m
end Lean.Meta.Grind.Lia
namespace Int
@[simp] protected theorem neg_min_neg (a b : Int) : min (-a) (-b) = -max a b := by
rw [Int.min_def, Int.max_def]
simp
@@ -474,20 +466,6 @@ protected theorem max_lt {a b c : Int} : max a b < c ↔ a < c ∧ b < c := by
simp only [Int.lt_iff_add_one_le]
simpa using Int.max_le (a := a + 1) (b := b + 1) (c := c)
protected theorem max_eq_right_iff {a b : Int} : max a b = b a b := by
apply Iff.intro
· intro h
rw [ h]
apply Int.le_max_left
· apply Int.max_eq_right
protected theorem max_eq_left_iff {a b : Int} : max a b = a b a := by
apply Iff.intro
· intro h
rw [ h]
apply Int.le_max_right
· apply Int.max_eq_left
@[simp] theorem ofNat_max_zero (n : Nat) : (max (n : Int) 0) = n := by
rw [Int.max_eq_left (natCast_nonneg n)]
@@ -926,16 +904,6 @@ protected theorem sub_right_le_of_le_add {a b c : Int} (h : a ≤ b + c) : a - c
have h := Int.add_le_add_right h (-c)
rwa [Int.add_neg_cancel_right] at h
protected theorem sub_right_le_iff_le_add {a b c : Int} : a - c b a b + c :=
Int.le_add_of_sub_right_le, Int.sub_right_le_of_le_add
theorem toNat_sub_eq_zero_iff (m n : Int) : toNat (m - n) = 0 m n := by
rw [ ofNat_inj, ofNat_toNat, cast_ofNat_Int, Int.max_eq_right_iff, Int.sub_right_le_iff_le_add,
Int.zero_add]
theorem zero_eq_toNat_sub_iff (m n : Int) : 0 = toNat (m - n) m n := by
rw [eq_comm (a := 0), toNat_sub_eq_zero_iff]
protected theorem le_add_of_neg_add_le_left {a b c : Int} (h : -b + a c) : a b + c := by
rw [Int.add_comm] at h
exact Int.le_add_of_sub_left_le h
@@ -1013,10 +981,6 @@ protected theorem lt_sub_right_of_add_lt {a b c : Int} (h : a + b < c) : a < c -
have h := Int.add_lt_add_right h (-b)
rwa [Int.add_neg_cancel_right] at h
protected theorem lt_sub_right_iff_add_lt {a b c : Int} :
a < c - b a + b < c :=
Int.add_lt_of_lt_sub_right, Int.lt_sub_right_of_add_lt
protected theorem lt_add_of_neg_add_lt {a b c : Int} (h : -b + a < c) : a < b + c := by
have h := Int.add_lt_add_left h b
rwa [Int.add_neg_cancel_left] at h

View File

@@ -77,6 +77,8 @@ public theorem Shrink.deflate_inj {α} {x y : α} :
· rintro rfl
rfl
namespace Iterators
-- It is not fruitful to move the following docstrings to verso right now because there are lots of
-- forward references that cannot be realized nicely.
set_option doc.verso false
@@ -122,7 +124,6 @@ def x := ([1, 2, 3].iterM IO : IterM IO Nat)
-/
@[ext]
structure IterM {α : Type w} (m : Type w Type w') (β : Type w) where
mk' ::
/-- Internal implementation detail of the iterator. -/
internalState : α
@@ -292,11 +293,6 @@ theorem IterStep.mapIterator_id {step : IterStep α β} :
step.mapIterator id = step := by
cases step <;> rfl
@[simp]
theorem IterStep.mapIterator_id' {step : IterStep α β} :
step.mapIterator (fun x => x) = step := by
cases step <;> rfl
/--
A variant of `IterStep` that bundles the step together with a proof that it is "plausible".
The plausibility predicate will later be chosen to assert that a state is a plausible successor
@@ -310,7 +306,7 @@ def PlausibleIterStep (IsPlausibleStep : IterStep α β → Prop) := Subtype IsP
/--
Match pattern for the `yield` case. See also `IterStep.yield`.
-/
@[match_pattern, simp, spec, expose]
@[match_pattern, simp, expose]
def PlausibleIterStep.yield {IsPlausibleStep : IterStep α β Prop}
(it' : α) (out : β) (h : IsPlausibleStep (.yield it' out)) :
PlausibleIterStep IsPlausibleStep :=
@@ -319,7 +315,7 @@ def PlausibleIterStep.yield {IsPlausibleStep : IterStep α β → Prop}
/--
Match pattern for the `skip` case. See also `IterStep.skip`.
-/
@[match_pattern, simp, grind =, expose]
@[match_pattern, simp, expose]
def PlausibleIterStep.skip {IsPlausibleStep : IterStep α β Prop}
(it' : α) (h : IsPlausibleStep (.skip it')) : PlausibleIterStep IsPlausibleStep :=
.skip it', h
@@ -327,7 +323,7 @@ def PlausibleIterStep.skip {IsPlausibleStep : IterStep α β → Prop}
/--
Match pattern for the `done` case. See also `IterStep.done`.
-/
@[match_pattern, simp, grind =, expose]
@[match_pattern, simp, expose]
def PlausibleIterStep.done {IsPlausibleStep : IterStep α β Prop}
(h : IsPlausibleStep .done) : PlausibleIterStep IsPlausibleStep :=
.done, h
@@ -362,27 +358,21 @@ class Iterator (α : Type w) (m : Type w → Type w') (β : outParam (Type w)) w
section Monadic
/--
Wraps the state of an iterator into an `IterM` object.
Converts wraps the state of an iterator into an `IterM` object.
-/
@[always_inline, inline, expose]
def IterM.mk {α : Type w} (it : α) (m : Type w Type w') (β : Type w) :
def toIterM {α : Type w} (it : α) (m : Type w Type w') (β : Type w) :
IterM (α := α) m β :=
it
@[deprecated IterM.mk (since := "2025-12-01"), inline, expose]
def Iterators.toIterM := @IterM.mk
@[simp]
theorem IterM.mk_internalState {α m β} (it : IterM (α := α) m β) :
.mk it.internalState m β = it :=
theorem toIterM_internalState {α m β} (it : IterM (α := α) m β) :
toIterM it.internalState m β = it :=
rfl
@[deprecated IterM.mk_internalState (since := "2025-12-01")]
def Iterators.toIterM_internalState := @IterM.mk_internalState
@[simp]
theorem internalState_toIterM {α m β} (it : α) :
(IterM.mk it m β).internalState = it :=
(toIterM it m β).internalState = it :=
rfl
/--
@@ -403,16 +393,6 @@ abbrev IterM.Step {α : Type w} {m : Type w → Type w'} {β : Type w} [Iterator
(it : IterM (α := α) m β) :=
PlausibleIterStep it.IsPlausibleStep
/--
Makes a single step with the given iterator `it`, potentially emitting a value and providing a
succeeding iterator. If this function is used recursively, termination can sometimes be proved with
the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
-/
@[always_inline, inline, expose]
def IterM.step {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
(it : IterM (α := α) m β) : m (Shrink it.Step) :=
Iterator.step it
/--
Asserts that a certain output value could plausibly be emitted by the given iterator in its next
step.
@@ -440,6 +420,16 @@ def IterM.IsPlausibleSkipSuccessorOf {α : Type w} {m : Type w → Type w'} {β
[Iterator α m β] (it' it : IterM (α := α) m β) : Prop :=
it.IsPlausibleStep (.skip it')
/--
Makes a single step with the given iterator `it`, potentially emitting a value and providing a
succeeding iterator. If this function is used recursively, termination can sometimes be proved with
the termination measures `it.finitelyManySteps` and `it.finitelyManySkips`.
-/
@[always_inline, inline, expose]
def IterM.step {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
(it : IterM (α := α) m β) : m (Shrink it.Step) :=
Iterator.step it
end Monadic
section Pure
@@ -687,11 +677,10 @@ this means that the relation of plausible successors is well-founded.
Given this typeclass, termination proofs for well-founded recursion over an iterator `it` can use
`it.finitelyManySteps` as a termination measure.
-/
class Iterators.Finite (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] : Prop where
/-- The relation of plausible successors is well-founded. -/
class Finite (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] : Prop where
wf : WellFounded (IterM.IsPlausibleSuccessorOf (α := α) (m := m))
theorem Iterators.Finite.wf_of_id {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] :
theorem Finite.wf_of_id {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] :
WellFounded (Iter.IsPlausibleSuccessorOf (α := α)) := by
simpa [Iter.isPlausibleSuccessorOf_eq_invImage] using InvImage.wf _ Finite.wf
@@ -713,11 +702,10 @@ def IterM.TerminationMeasures.Finite.Rel
TerminationMeasures.Finite α m TerminationMeasures.Finite α m Prop :=
Relation.TransGen <| InvImage IterM.IsPlausibleSuccessorOf IterM.TerminationMeasures.Finite.it
instance IterM.TerminationMeasures.instWellFoundedRelationFinite {α : Type w} {m : Type w Type w'}
{β : Type w} [Iterator α m β] [Iterators.Finite α m] :
WellFoundedRelation (IterM.TerminationMeasures.Finite α m) where
instance {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Finite α m] : WellFoundedRelation (IterM.TerminationMeasures.Finite α m) where
rel := IterM.TerminationMeasures.Finite.Rel
wf := by exact (InvImage.wf _ Iterators.Finite.wf).transGen
wf := by exact (InvImage.wf _ Finite.wf).transGen
/--
Termination measure to be used in well-founded recursive functions recursing over a finite iterator
@@ -725,16 +713,7 @@ Termination measure to be used in well-founded recursive functions recursing ove
-/
@[expose]
def IterM.finitelyManySteps {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Iterators.Finite α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Finite α m :=
it
/--
Termination measure to be used in well-founded recursive functions recursing over a finite iterator
(see also `Finite`).
-/
@[expose]
def IterM.finitelyManySteps! {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
(it : IterM (α := α) m β) : IterM.TerminationMeasures.Finite α m :=
[Finite α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Finite α m :=
it
/--
@@ -767,7 +746,7 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic|
| fail)
@[inherit_doc IterM.finitelyManySteps, expose]
def Iter.finitelyManySteps {α : Type w} {β : Type w} [Iterator α Id β] [Iterators.Finite α Id]
def Iter.finitelyManySteps {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Finite α Id :=
it.toIterM.finitelyManySteps
@@ -817,8 +796,7 @@ well-founded.
Given this typeclass, termination proofs for well-founded recursion over an iterator `it` can use
`it.finitelyManySkips` as a termination measure.
-/
class Iterators.Productive (α m) {β} [Iterator α m β] : Prop where
/-- The relation of plausible successors during skips is well-founded. -/
class Productive (α m) {β} [Iterator α m β] : Prop where
wf : WellFounded (IterM.IsPlausibleSkipSuccessorOf (α := α) (m := m))
/--
@@ -857,11 +835,10 @@ theorem IterM.TerminationMeasures.Finite.Rel.of_productive
refine .trans ih ?_
exact .single _, rfl, hab
instance IterM.TerminationMeasures.instWellFoundedRelationProductive {α : Type w}
{m : Type w Type w'} {β : Type w} [Iterator α m β] [Iterators.Productive α m] :
WellFoundedRelation (IterM.TerminationMeasures.Productive α m) where
instance {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Productive α m] : WellFoundedRelation (IterM.TerminationMeasures.Productive α m) where
rel := IterM.TerminationMeasures.Productive.Rel
wf := by exact (InvImage.wf _ Iterators.Productive.wf).transGen
wf := by exact (InvImage.wf _ Productive.wf).transGen
/--
Termination measure to be used in well-founded recursive functions recursing over a productive
@@ -869,7 +846,7 @@ iterator (see also `Productive`).
-/
@[expose]
def IterM.finitelyManySkips {α : Type w} {m : Type w Type w'} {β : Type w} [Iterator α m β]
[Iterators.Productive α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Productive α m :=
[Productive α m] (it : IterM (α := α) m β) : IterM.TerminationMeasures.Productive α m :=
it
/--
@@ -888,7 +865,7 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic|
| fail)
@[inherit_doc IterM.finitelyManySkips, expose]
def Iter.finitelyManySkips {α : Type w} {β : Type w} [Iterator α Id β] [Iterators.Productive α Id]
def Iter.finitelyManySkips {α : Type w} {β : Type w} [Iterator α Id β] [Productive α Id]
(it : Iter (α := α) β) : IterM.TerminationMeasures.Productive α Id :=
it.toIterM.finitelyManySkips
@@ -907,13 +884,12 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic|
| exact Iter.TerminationMeasures.Productive.rel_of_skip _
| fail)
instance Iterators.instProductiveOfFinte [Iterator α m β] [Iterators.Finite α m] :
Iterators.Productive α m where
instance [Iterator α m β] [Finite α m] : Productive α m where
wf := by
apply Subrelation.wf (r := IterM.IsPlausibleSuccessorOf)
· intro it' it h
exact IterM.isPlausibleSuccessorOf_of_skip h
· exact Iterators.Finite.wf
· exact Finite.wf
end Productive
@@ -932,60 +908,8 @@ class LawfulDeterministicIterator (α : Type w) (m : Type w → Type w') [Iterat
where
isPlausibleStep_eq_eq : it : IterM (α := α) m β, step, it.IsPlausibleStep = (· = step)
namespace Iterators
end Iterators
/--
This structure provides a more convenient way to define `Finite α m` instances using
`Finite.of_finitenessRelation : FinitenessRelation α m → Finite α m`.
-/
structure FinitenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
/-
A well-founded relation such that if `it'` is a successor iterator of `it`, then
`Rel it' it`.
-/
Rel (it' it : IterM (α := α) m β) : Prop
/- A proof that `Rel` is well-founded. -/
wf : WellFounded Rel
/- A proof that if `it'` is a successor iterator of `it`, then `Rel it' it`. -/
subrelation : {it it'}, it'.IsPlausibleSuccessorOf it Rel it' it
export Iterators (Iter IterM)
theorem Finite.of_finitenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : FinitenessRelation α m) : Finite α m where
wf := by
refine Subrelation.wf (r := r.Rel) ?_ ?_
· intro x y h
apply FinitenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
/--
This structure provides a more convenient way to define `Productive α m` instances using
`Productive.of_productivenessRelation : ProductivenessRelation α m → Productive α m`.
-/
structure ProductivenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
/-
A well-founded relation such that if `it'` is obtained from `it` by skipping, then
`Rel it' it`.
-/
Rel : (IterM (α := α) m β) (IterM (α := α) m β) Prop
/- A proof that `Rel` is well-founded. -/
wf : WellFounded Rel
/- A proof that if `it'` is obtained from `it` by skipping, then `Rel it' it`. -/
subrelation : {it it'}, it'.IsPlausibleSkipSuccessorOf it Rel it' it
theorem Productive.of_productivenessRelation
{α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (r : ProductivenessRelation α m) : Productive α m where
wf := by
refine Subrelation.wf (r := r.Rel) ?_ ?_
· intro x y h
apply ProductivenessRelation.subrelation
exact h
· apply InvImage.wf
exact r.wf
end Std.Iterators
end Std

View File

@@ -11,8 +11,7 @@ public import Init.Data.Iterators.Combinators.FilterMap
public section
namespace Std
open Std.Iterators
namespace Std.Iterators
@[always_inline, inline, expose, inherit_doc IterM.attachWith]
def Iter.attachWith {α β : Type w}
@@ -25,4 +24,4 @@ where finally
simp only [ isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM]
exact h
end Std
end Std.Iterators

View File

@@ -30,8 +30,7 @@ Several variants of these combinators are provided:
iterator, and particularly for specialized termination proofs. If possible, avoid this.
-/
namespace Std
open Std.Iterators
namespace Std.Iterators
-- We cannot use `inherit_doc` because the docstring for `IterM` states that a `MonadLiftT` instance
-- is needed.
@@ -198,8 +197,12 @@ it.filterMapM ---a'-----c'-------⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` never returns `none`, then
this combinator will preserve productiveness. If `f` is an `ExceptT` monad and will always fail,
then `it.filterMapM` will be finite even if `it` isn't. In such cases, the termination proof needs
to be done manually.
then `it.filterMapM` will be finite even if `it` isn't. In the first case, consider
using the `map`/`mapM`/`mapWithPostcondition` combinators instead, which provide more instances out
of the box.
If that does not help, the more general combinator `it.filterMapWithPostcondition f` makes it
possible to manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
**Performance:**
@@ -208,7 +211,7 @@ returned `Option` value.
-/
@[always_inline, inline, expose]
def Iter.filterMapM {α β γ : Type w} [Iterator α Id β] {m : Type w Type w'}
[Monad m] [MonadAttach m] (f : β m (Option γ)) (it : Iter (α := α) β) :=
[Monad m] (f : β m (Option γ)) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := pure; it.toIterM.filterMapM f : IterM m γ)
/--
@@ -234,7 +237,10 @@ it.filterM ---a-----c-------⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` is an `ExceptT` monad and
will always fail, then `it.filterWithPostcondition` will be finite -- and productive -- even if `it`
isn't. In such cases, the termination proof needs to be done manually.
isn't.
In such situations, the more general combinator `it.filterWithPostcondition f` makes it possible to
manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
**Performance:**
@@ -242,7 +248,7 @@ For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline, expose]
def Iter.filterM {α β : Type w} [Iterator α Id β] {m : Type w Type w'}
[Monad m] [MonadAttach m] (f : β m (ULift Bool)) (it : Iter (α := α) β) :=
[Monad m] (f : β m (ULift Bool)) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := pure; it.toIterM.filterM f : IterM m β)
/--
@@ -270,8 +276,10 @@ it.mapM ---a'--b'--c'--d'-e'----⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` is an `ExceptT` monad and
will always fail, then `it.mapM` will be finite even if `it` isn't. In such cases, the termination
proof needs to be done manually.
will always fail, then `it.mapM` will be finite even if `it` isn't.
If that does not help, the more general combinator `it.mapWithPostcondition f` makes it possible to
manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
**Performance:**
@@ -279,7 +287,7 @@ For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline, expose]
def Iter.mapM {α β γ : Type w} [Iterator α Id β] {m : Type w Type w'}
[Monad m] [MonadAttach m] (f : β m γ) (it : Iter (α := α) β) :=
[Monad m] (f : β m γ) (it : Iter (α := α) β) :=
(letI : MonadLift Id m := pure; it.toIterM.mapM f : IterM m γ)
@[always_inline, inline, inherit_doc IterM.filterMap, expose]
@@ -297,4 +305,4 @@ def Iter.map {α : Type w} {β : Type w} {γ : Type w} [Iterator α Id β]
(f : β γ) (it : Iter (α := α) β) :=
((it.toIterM.map f).toIter : Iter γ)
end Std
end Std.Iterators

View File

@@ -24,17 +24,17 @@ and so on. In other words, {lit}`it` flattens the iterator of iterators obtained
{lit}`f`.
-/
namespace Std
namespace Std.Iterators
@[always_inline, inherit_doc IterM.flatMapAfterM]
public def Iter.flatMapAfterM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β] [Iterator α₂ m γ]
(f : β m (IterM (α := α₂) m γ)) (it₁ : Iter (α := α) β) (it₂ : Option (IterM (α := α₂) m γ)) :=
((it₁.mapWithPostcondition pure).flatMapAfterM f it₂ : IterM m γ)
((it₁.mapM pure).flatMapAfterM f it₂ : IterM m γ)
@[always_inline, expose, inherit_doc IterM.flatMapM]
public def Iter.flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [Iterator α Id β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β] [Iterator α₂ m γ]
(f : β m (IterM (α := α₂) m γ)) (it : Iter (α := α) β) :=
(it.flatMapAfterM f none : IterM m γ)
@@ -49,3 +49,5 @@ public def Iter.flatMap {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
(f : β Iter (α := α₂) γ) (it : Iter (α := α) β) :=
(it.flatMapAfter f none : Iter γ)
end Std.Iterators

View File

@@ -6,6 +6,7 @@ Authors: Paul Reichert
module
prelude
public import Init.Data.Iterators.Internal.Termination
public import Init.Data.Iterators.Consumers.Loop
public section
@@ -46,7 +47,7 @@ instance Attach.instIterator {α β : Type w} {m : Type w → Type w'} [Monad m]
def Attach.instFinitenessRelation {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [Finite α m] {P : β Prop} :
FinitenessRelation (Attach α m P) m where
Rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySteps
rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySteps
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
apply Relation.TransGen.single
@@ -67,7 +68,7 @@ instance Attach.instFinite {α β : Type w} {m : Type w → Type w'} [Monad m]
def Attach.instProductivenessRelation {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] [Productive α m] {P : β Prop} :
ProductivenessRelation (Attach α m P) m where
Rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySkips
rel := InvImage WellFoundedRelation.rel fun it => it.internalState.inner.finitelyManySkips
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation {it it'} h := by
apply Relation.TransGen.single
@@ -85,12 +86,27 @@ instance Attach.instProductive {α β : Type w} {m : Type w → Type w'} [Monad
Productive (Attach α m P) m :=
.of_productivenessRelation instProductivenessRelation
instance Attach.instIteratorCollect {α β : Type w} {m : Type w Type w'} [Monad m] [Monad n]
{P : β Prop} [Iterator α m β] :
IteratorCollect (Attach α m P) m n :=
.defaultImplementation
instance Attach.instIteratorCollectPartial {α β : Type w} {m : Type w Type w'} [Monad m]
[Monad n] {P : β Prop} [Iterator α m β] :
IteratorCollectPartial (Attach α m P) m n :=
.defaultImplementation
instance Attach.instIteratorLoop {α β : Type w} {m : Type w Type w'} [Monad m]
{n : Type x Type x'} [Monad n] {P : β Prop} [Iterator α m β] :
IteratorLoop (Attach α m P) m n :=
.defaultImplementation
end Iterators.Types
instance Attach.instIteratorLoopPartial {α β : Type w} {m : Type w Type w'} [Monad m]
{n : Type x Type x'} [Monad n] {P : β Prop} [Iterator α m β] :
IteratorLoopPartial (Attach α m P) m n :=
.defaultImplementation
end Types
/--
“Attaches” individual proofs to an iterator of values that satisfy a predicate `P`, returning an
@@ -105,7 +121,7 @@ iterator with values in the corresponding subtype `{ x // P x }`.
def IterM.attachWith {α β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m β] (it : IterM (α := α) m β) (P : β Prop)
(h : out, it.IsPlausibleIndirectOutput out P out) :
IterM (α := Iterators.Types.Attach α m P) m { out : β // P out } :=
IterM (α := Types.Attach α m P) m { out : β // P out } :=
it, h
end Std
end Std.Iterators

View File

@@ -8,6 +8,7 @@ module
prelude
public import Init.Data.Iterators.Consumers.Loop
public import Init.Data.Iterators.PostconditionMonad
public import Init.Data.Iterators.Internal.Termination
public section
@@ -31,9 +32,7 @@ Several variants of these combinators are provided:
iterator, and particularly for specialized termination proofs. If possible, avoid this.
-/
namespace Std
namespace Iterators.Types
namespace Std.Iterators
/--
Internal state of the `filterMap` combinator. Do not depend on its internals.
@@ -54,23 +53,19 @@ def Map (α : Type w) {β γ : Type w} (m : Type w → Type w') (n : Type w →
(f : β PostconditionT n γ) :=
FilterMap α m n lift (fun b => PostconditionT.map some (f b))
end Iterators.Types
open Std.Iterators Std.Iterators.Types
@[always_inline, inline, expose]
def IterM.InternalCombinators.filterMap {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} (lift : α : Type w m α n α)
[Iterator α m β] (f : β PostconditionT n (Option γ))
(it : IterM (α := α) m β) : IterM (α := FilterMap α m n lift f) n γ :=
.mk it n γ
toIterM it n γ
@[always_inline, inline, expose]
def IterM.InternalCombinators.map {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} [Monad n] (lift : α : Type w m α n α)
[Iterator α m β] (f : β PostconditionT n γ)
(it : IterM (α := α) m β) : IterM (α := Map α m n lift f) n γ :=
.mk it n γ
toIterM it n γ
/--
*Note: This is a very general combinator that requires an advanced understanding of monads,
@@ -122,18 +117,16 @@ returned `Option` value.
def IterM.filterMapWithPostcondition {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''}
[MonadLiftT m n] [Iterator α m β] (f : β PostconditionT n (Option γ))
(it : IterM (α := α) m β) : IterM (α := FilterMap α m n (fun _ => monadLift) f) n γ :=
IterM.InternalCombinators.filterMap (n := n) (fun _ => monadLift) f it
namespace Iterators.Types
IterM.InternalCombinators.filterMap (fun _ => monadLift) f it
/--
`it.PlausibleStep step` is the proposition that `step` is a possible next step from the
`filterMap` iterator `it`. This is mostly internally relevant, except if one needs to manually
prove termination (`Finite` or `Productive` instances, for example) of a `filterMap` iterator.
-/
inductive FilterMap.PlausibleStep {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {lift : α : Type w m α n α} {f : β PostconditionT n (Option γ)}
[Iterator α m β] (it : IterM (α := FilterMap α m n lift f) n γ) :
inductive FilterMap.PlausibleStep {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''}
{lift : α : Type w m α n α} {f : β PostconditionT n (Option γ)} [Iterator α m β]
(it : IterM (α := FilterMap α m n lift f) n γ) :
IterStep (IterM (α := FilterMap α m n lift f) n γ) γ Prop where
| yieldNone : {it' out},
it.internalState.inner.IsPlausibleStep (.yield it' out)
@@ -146,8 +139,8 @@ inductive FilterMap.PlausibleStep {α β γ : Type w} {m : Type w → Type w'}
PlausibleStep it (.skip (IterM.InternalCombinators.filterMap lift f it'))
| done : it.internalState.inner.IsPlausibleStep .done PlausibleStep it .done
instance FilterMap.instIterator {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {lift : α : Type w m α n α} {f : β PostconditionT n (Option γ)}
instance FilterMap.instIterator {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''}
{lift : α : Type w m α n α} {f : β PostconditionT n (Option γ)}
[Iterator α m β] [Monad n] :
Iterator (FilterMap α m n lift f) n γ where
IsPlausibleStep := FilterMap.PlausibleStep (m := m) (n := n)
@@ -162,8 +155,9 @@ instance FilterMap.instIterator {α β γ : Type w} {m : Type w → Type w'}
| .skip it' h => pure <| .deflate <| .skip (it'.filterMapWithPostcondition f) (by exact .skip h)
| .done h => pure <| .deflate <| .done (.done h)
instance Map.instIterator {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Monad n]
[Iterator α m β] {lift : α : Type w m α n α} {f : β PostconditionT n γ} :
instance {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Monad n] [Iterator α m β]
{lift : α : Type w m α n α}
{f : β PostconditionT n γ} :
Iterator (Map α m n lift f) n γ :=
inferInstanceAs <| Iterator (FilterMap α m n lift _) n γ
@@ -171,7 +165,7 @@ private def FilterMap.instFinitenessRelation {α β γ : Type w} {m : Type w →
{n : Type w Type w''} [Monad n] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} [Finite α m] :
FinitenessRelation (FilterMap α m n lift f) n where
Rel := InvImage IterM.IsPlausibleSuccessorOf (FilterMap.inner IterM.internalState)
rel := InvImage IterM.IsPlausibleSuccessorOf (FilterMap.inner IterM.internalState)
wf := InvImage.wf _ Finite.wf
subrelation {it it'} h := by
obtain step, h, h' := h
@@ -195,8 +189,8 @@ instance FilterMap.instFinite {α β γ : Type w} {m : Type w → Type w'}
Finite.of_finitenessRelation FilterMap.instFinitenessRelation
@[no_expose]
instance Map.instFinite {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Monad n]
[Iterator α m β] {lift : α : Type w m α n α} {f : β PostconditionT n γ} [Finite α m] :
instance {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Monad n] [Iterator α m β]
{lift : α : Type w m α n α} {f : β PostconditionT n γ} [Finite α m] :
Finite (Map α m n lift f) n :=
Finite.of_finitenessRelation FilterMap.instFinitenessRelation
@@ -204,7 +198,7 @@ private def Map.instProductivenessRelation {α β γ : Type w} {m : Type w → T
{n : Type w Type w''} [Monad n] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n γ} [Productive α m] :
ProductivenessRelation (Map α m n lift f) n where
Rel := InvImage IterM.IsPlausibleSkipSuccessorOf (FilterMap.inner IterM.internalState)
rel := InvImage IterM.IsPlausibleSkipSuccessorOf (FilterMap.inner IterM.internalState)
wf := InvImage.wf _ Productive.wf
subrelation {it it'} h := by
cases h
@@ -220,13 +214,63 @@ instance Map.instProductive {α β γ : Type w} {m : Type w → Type w'}
Productive (Map α m n lift f) n :=
Productive.of_productivenessRelation Map.instProductivenessRelation
instance {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type w Type x} [Monad n] [Monad o] [Iterator α m β]
{lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} :
IteratorCollect (FilterMap α m n lift f) n o :=
.defaultImplementation
instance {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type w Type x} [Monad n] [Monad o] [Iterator α m β]
{lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} [Finite α m] :
IteratorCollectPartial (FilterMap α m n lift f) n o :=
.defaultImplementation
instance FilterMap.instIteratorLoop {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type x Type x'}
[Monad n] [Monad o] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} :
{f : β PostconditionT n (Option γ)} [Finite α m] :
IteratorLoop (FilterMap α m n lift f) n o :=
.defaultImplementation
instance FilterMap.instIteratorLoopPartial {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type x Type x'}
[Monad n] [Monad o] [Iterator α m β] {lift : α : Type w m α n α}
{f : β PostconditionT n (Option γ)} :
IteratorLoopPartial (FilterMap α m n lift f) n o :=
.defaultImplementation
/--
`map` operations allow for a more efficient implementation of `toArray`. For example,
`array.iter.map f |>.toArray happens in-place if possible.
-/
instance Map.instIteratorCollect {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type w Type x} [Monad n] [Monad o] [Iterator α m β]
{lift₁ : α : Type w m α n α}
{f : β PostconditionT n γ} [IteratorCollect α m o] [Finite α m] :
IteratorCollect (Map α m n lift₁ f) n o where
toArrayMapped lift₂ _ g it :=
letI : MonadLift m n := lift₁ (α := _)
letI : MonadLift n o := lift₂ (δ := _)
IteratorCollect.toArrayMapped
(lift := fun _ => monadLift)
(fun x => do g ( (f x).operation))
it.internalState.inner (m := m)
@[no_expose]
instance Map.instIteratorCollectPartial {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type w Type x} [Monad n] [Monad o] [Iterator α m β]
{lift₁ : α : Type w m α n α}
{f : β PostconditionT n γ} [IteratorCollectPartial α m o] :
IteratorCollectPartial (Map α m n lift₁ f) n o where
toArrayMappedPartial lift₂ _ g it :=
IteratorCollectPartial.toArrayMappedPartial
(lift := fun _ a => lift₂ (lift₁ a))
(fun x => do g ( lift₂ (f x).operation))
it.internalState.inner (m := m)
instance Map.instIteratorLoop {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type x Type x'} [Monad n] [Monad o] [Iterator α m β]
{lift : α : Type w m α n α}
@@ -234,7 +278,12 @@ instance Map.instIteratorLoop {α β γ : Type w} {m : Type w → Type w'}
IteratorLoop (Map α m n lift f) n o :=
.defaultImplementation
end Iterators.Types
instance Map.instIteratorLoopPartial {α β γ : Type w} {m : Type w Type w'}
{n : Type w Type w''} {o : Type x Type x'} [Monad n] [Monad o] [Iterator α m β]
{lift : α : Type w m α n α}
{f : β PostconditionT n γ} :
IteratorLoopPartial (Map α m n lift f) n o :=
.defaultImplementation
/--
*Note: This is a very general combinator that requires an advanced understanding of monads, dependent
@@ -357,8 +406,12 @@ it.filterMapM ---a'-----c'-------⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` never returns `none`, then
this combinator will preserve productiveness. If `f` is an `ExceptT` monad and will always fail,
then `it.filterMapM` will be finite even if `it` isn't. In such cases, the termination proof needs
to be done manually.
then `it.filterMapM` will be finite even if `it` isn't. In the first case, consider
using the `map`/`mapM`/`mapWithPostcondition` combinators instead, which provide more instances out of
the box.
If that does not help, the more general combinator `it.filterMapWithPostcondition f` makes it
possible to manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
**Performance:**
@@ -367,9 +420,9 @@ returned `Option` value.
-/
@[inline, expose]
def IterM.filterMapM {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''}
[Iterator α m β] [Monad n] [MonadAttach n] [MonadLiftT m n]
[Iterator α m β] [Monad n] [MonadLiftT m n]
(f : β n (Option γ)) (it : IterM (α := α) m β) :=
(it.filterMapWithPostcondition (fun b => PostconditionT.attachLift (f b)) : IterM n γ)
(it.filterMapWithPostcondition (fun b => PostconditionT.lift (f b)) : IterM n γ)
/--
If `it` is an iterator, then `it.mapM f` is another iterator that applies a monadic
@@ -396,8 +449,10 @@ it.mapM ---a'--b'--c'--d'-e'----⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` is an `ExceptT` monad and
will always fail, then `it.mapM` will be finite even if `it` isn't. In such cases, the termination
proof needs to be done manually.
will always fail, then `it.mapM` will be finite even if `it` isn't.
If that does not help, the more general combinator `it.mapWithPostcondition f` makes it possible to
manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
**Performance:**
@@ -405,8 +460,8 @@ For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[inline, expose]
def IterM.mapM {α β γ : Type w} {m : Type w Type w'} {n : Type w Type w''} [Iterator α m β]
[Monad n] [MonadAttach n] [MonadLiftT m n] (f : β n γ) (it : IterM (α := α) m β) :=
(it.mapWithPostcondition (fun b => PostconditionT.attachLift (f b)) : IterM n γ)
[Monad n] [MonadLiftT m n] (f : β n γ) (it : IterM (α := α) m β) :=
(it.mapWithPostcondition (fun b => PostconditionT.lift (f b)) : IterM n γ)
/--
If `it` is an iterator, then `it.filterM f` is another iterator that applies a monadic
@@ -434,7 +489,10 @@ it.filterM ---a-----c-------⊥
For certain mapping functions `f`, the resulting iterator will be finite (or productive) even though
no `Finite` (or `Productive`) instance is provided. For example, if `f` is an `ExceptT` monad and
will always fail, then `it.filterWithPostcondition` will be finite -- and productive -- even if `it`
isn't. In such cases, the termination proof needs to be done manually.
isn't.
In such situations, the more general combinator `it.filterWithPostcondition f` makes it possible to
manually prove `Finite` and `Productive` instances depending on the concrete choice of `f`.
**Performance:**
@@ -442,9 +500,9 @@ For each value emitted by the base iterator `it`, this combinator calls `f`.
-/
@[inline, expose]
def IterM.filterM {α β : Type w} {m : Type w Type w'} {n : Type w Type w''} [Iterator α m β]
[Monad n] [MonadAttach n] [MonadLiftT m n] (f : β n (ULift Bool)) (it : IterM (α := α) m β) :=
[Monad n] [MonadLiftT m n] (f : β n (ULift Bool)) (it : IterM (α := α) m β) :=
(it.filterMapWithPostcondition
(fun b => (PostconditionT.attachLift (f b)).map (if ·.down = true then some b else none)) : IterM n β)
(fun b => (PostconditionT.lift (f b)).map (if ·.down = true then some b else none)) : IterM n β)
/--
If `it` is an iterator, then `it.filterMap f` is another iterator that applies a function `f` to all
@@ -546,4 +604,4 @@ def IterM.filter {α β : Type w} {m : Type w → Type w'} [Iterator α m β] [M
(f : β Bool) (it : IterM (α := α) m β) :=
(it.filterMap (fun b => if f b then some b else none) : IterM m β)
end Std
end Std.Iterators

View File

@@ -22,23 +22,22 @@ and so on. In other words, `it` flattens the iterator of iterators obtained by m
`f`.
-/
namespace Std
open Iterators.Types
namespace Std.Iterators
/-- Internal implementation detail of the `flatMap` combinator -/
@[ext, unbox]
public structure Iterators.Types.Flatten (α α₂ β : Type w) (m) where
public structure Flatten (α α₂ β : Type w) (m) where
it₁ : IterM (α := α) m (IterM (α := α₂) m β)
it₂ : Option (IterM (α := α₂) m β)
/--
Internal iterator combinator that is used to implement all `flatMap` variants
-/
@[always_inline, inline]
@[always_inline]
def IterM.flattenAfter {α α₂ β : Type w} {m : Type w Type w'} [Monad m]
[Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
(it₁ : IterM (α := α) m (IterM (α := α₂) m β)) (it₂ : Option (IterM (α := α₂) m β)) :=
(.mk (α := Flatten α α₂ β m) it₁, it₂ m β : IterM m β)
(toIterM (α := Flatten α α₂ β m) it₁, it₂ m β : IterM m β)
/--
Let `it₁` and `it₂` be iterators and `f` a monadic function mapping `it₁`'s outputs to iterators
@@ -76,9 +75,9 @@ iterator.
For each value emitted by the outer iterator `it₁`, this combinator calls `f`.
-/
@[always_inline, inline]
@[always_inline]
public def IterM.flatMapAfterM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [Iterator α m β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
(f : β m (IterM (α := α₂) m γ)) (it₁ : IterM (α := α) m β) (it₂ : Option (IterM (α := α₂) m γ)) :=
((it₁.mapM f).flattenAfter it₂ : IterM m γ)
@@ -115,9 +114,9 @@ This combinator incurs an additional O(1) cost with each output of `it` or an in
For each value emitted by the outer iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline, expose]
@[always_inline, expose]
public def IterM.flatMapM {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [MonadAttach m] [Iterator α m β] [Iterator α₂ m γ]
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
(f : β m (IterM (α := α₂) m γ)) (it : IterM (α := α) m β) :=
(it.flatMapAfterM f none : IterM m γ)
@@ -157,7 +156,7 @@ iterator.
For each value emitted by the outer iterator `it₁`, this combinator calls `f`.
-/
@[always_inline, inline]
@[always_inline]
public def IterM.flatMapAfter {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
(f : β IterM (α := α₂) m γ) (it₁ : IterM (α := α) m β) (it₂ : Option (IterM (α := α₂) m γ)) :=
@@ -196,31 +195,29 @@ This combinator incurs an additional O(1) cost with each output of `it` or an in
For each value emitted by the outer iterator `it`, this combinator calls `f`.
-/
@[always_inline, inline, expose]
@[always_inline, expose]
public def IterM.flatMap {α : Type w} {β : Type w} {α₂ : Type w}
{γ : Type w} {m : Type w Type w'} [Monad m] [Iterator α m β] [Iterator α₂ m γ]
(f : β IterM (α := α₂) m γ) (it : IterM (α := α) m β) :=
(it.flatMapAfter f none : IterM m γ)
namespace Iterators.Types
variable {α α₂ β : Type w} {m : Type w Type w'}
/-- The plausible-step predicate for `Flatten` iterators -/
public inductive Flatten.IsPlausibleStep [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] :
(it : IterM (α := Flatten α α₂ β m) m β) (step : IterStep (IterM (α := Flatten α α₂ β m) m β) β) Prop where
| outerYield : {it₁ it₁' it₂'}, it₁.IsPlausibleStep (.yield it₁' it₂')
IsPlausibleStep (.mk it₁, none m β) (.skip (.mk it₁', some it₂' m β))
IsPlausibleStep (toIterM it₁, none m β) (.skip (toIterM it₁', some it₂' m β))
| outerSkip : {it₁ it₁'}, it₁.IsPlausibleStep (.skip it₁')
IsPlausibleStep (.mk it₁, none m β) (.skip (.mk it₁', none m β))
IsPlausibleStep (toIterM it₁, none m β) (.skip (toIterM it₁', none m β))
| outerDone : {it₁}, it₁.IsPlausibleStep .done
IsPlausibleStep (.mk it₁, none m β) .done
IsPlausibleStep (toIterM it₁, none m β) .done
| innerYield : {it₁ it₂ it₂' b}, it₂.IsPlausibleStep (.yield it₂' b)
IsPlausibleStep (.mk it₁, some it₂ m β) (.yield (.mk it₁, some it₂' m β) b)
IsPlausibleStep (toIterM it₁, some it₂ m β) (.yield (toIterM it₁, some it₂' m β) b)
| innerSkip : {it₁ it₂ it₂'}, it₂.IsPlausibleStep (.skip it₂')
IsPlausibleStep (.mk it₁, some it₂ m β) (.skip (.mk it₁, some it₂' m β))
IsPlausibleStep (toIterM it₁, some it₂ m β) (.skip (toIterM it₁, some it₂' m β))
| innerDone : {it₁ it₂}, it₂.IsPlausibleStep .done
IsPlausibleStep (.mk it₁, some it₂ m β) (.skip (.mk it₁, none m β))
IsPlausibleStep (toIterM it₁, some it₂ m β) (.skip (toIterM it₁, none m β))
public instance Flatten.instIterator [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] :
Iterator (Flatten α α₂ β m) m β where
@@ -249,7 +246,7 @@ section Finite
variable {α : Type w} {α₂ : Type w} {β : Type w} {m : Type w Type w'}
variable (α m β) in
def Flatten.Rel [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m] [Finite α₂ m] :
def Rel [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m] [Finite α₂ m] :
IterM (α := Flatten α α₂ β m) m β IterM (α := Flatten α α₂ β m) m β Prop :=
InvImage
(Prod.Lex
@@ -274,10 +271,10 @@ theorem Flatten.rel_of_right₂ [Monad m] [Iterator α m (IterM (α := α₂) m
Rel α β m it₁, none it₁, some it₂ :=
Prod.Lex.right _ True.intro
def Flatten.instFinitenessRelation [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
[Finite α m] [Finite α₂ m] :
FinitenessRelation (Flatten α α₂ β m) m where
Rel := Rel α β m
rel := Rel α β m
wf := by
apply InvImage.wf
refine fun (a, b) => Prod.lexAccessible (WellFounded.apply ?_ a) (WellFounded.apply ?_) b
@@ -302,9 +299,9 @@ def Flatten.instFinitenessRelation [Monad m] [Iterator α m (IterM (α := α₂)
apply Flatten.rel_of_right₂
@[no_expose]
public instance Flatten.instFinite [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
public instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
[Finite α m] [Finite α₂ m] : Finite (Flatten α α₂ β m) m :=
.of_finitenessRelation instFinitenessRelation
.of_finitenessRelation instFinitenessRelationFlattenOfIterMOfFinite
end Finite
@@ -313,7 +310,7 @@ section Productive
variable {α : Type w} {α₂ : Type w} {β : Type w} {m : Type w Type w'}
variable (α m β) in
def Flatten.ProductiveRel [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m]
def ProductiveRel [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β] [Finite α m]
[Productive α₂ m] :
IterM (α := Flatten α α₂ β m) m β IterM (α := Flatten α α₂ β m) m β Prop :=
InvImage
@@ -339,10 +336,10 @@ theorem Flatten.productiveRel_of_right₂ [Monad m] [Iterator α m (IterM (α :=
ProductiveRel α β m it₁, none it₁, some it₂ :=
Prod.Lex.right _ True.intro
def Flatten.instProductivenessRelation [Monad m] [Iterator α m (IterM (α := α₂) m β)]
[Iterator α₂ m β] [Finite α m] [Productive α₂ m] :
instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
[Finite α m] [Productive α₂ m] :
ProductivenessRelation (Flatten α α₂ β m) m where
Rel := ProductiveRel α β m
rel := ProductiveRel α β m
wf := by
apply InvImage.wf
refine fun (a, b) => Prod.lexAccessible (WellFounded.apply ?_ a) (WellFounded.apply ?_) b
@@ -363,14 +360,26 @@ def Flatten.instProductivenessRelation [Monad m] [Iterator α m (IterM (α := α
apply Flatten.productiveRel_of_right₂
@[no_expose]
public def Flatten.instProductive [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
public instance [Monad m] [Iterator α m (IterM (α := α₂) m β)] [Iterator α₂ m β]
[Finite α m] [Productive α₂ m] : Productive (Flatten α α₂ β m) m :=
.of_productivenessRelation instProductivenessRelation
.of_productivenessRelation instProductivenessRelationFlattenOfFiniteIterMOfProductive
end Productive
public instance Flatten.instIteratorCollect [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
[Iterator α₂ m β] : IteratorCollect (Flatten α α₂ β m) m n :=
.defaultImplementation
public instance Flatten.instIteratorCollectPartial [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
[Iterator α₂ m β] : IteratorCollectPartial (Flatten α α₂ β m) m n :=
.defaultImplementation
public instance Flatten.instIteratorLoop [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
[Iterator α₂ m β] : IteratorLoop (Flatten α α₂ β m) m n :=
.defaultImplementation
end Std.Iterators.Types
public instance Flatten.instIteratorLoopPartial [Monad m] [Monad n] [Iterator α m (IterM (α := α₂) m β)]
[Iterator α₂ m β] : IteratorLoopPartial (Flatten α α₂ β m) m n :=
.defaultImplementation
end Std.Iterators

View File

@@ -9,6 +9,7 @@ prelude
public import Init.Data.Nat.Lemmas
public import Init.Data.Iterators.Consumers.Monadic.Collect
public import Init.Data.Iterators.Consumers.Monadic.Loop
public import Init.Data.Iterators.Internal.Termination
@[expose] public section
@@ -16,7 +17,7 @@ public import Init.Data.Iterators.Consumers.Monadic.Loop
This module provides the iterator combinator `IterM.take`.
-/
namespace Std
namespace Std.Iterators
variable {α : Type w} {m : Type w Type w'} {β : Type w}
@@ -24,7 +25,7 @@ variable {α : Type w} {m : Type w → Type w'} {β : Type w}
The internal state of the `IterM.take` iterator combinator.
-/
@[unbox]
structure Iterators.Types.Take (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
structure Take (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
Internal implementation detail of the iterator library.
Caution: For `take n`, `countdown` is `n + 1`.
@@ -39,8 +40,6 @@ structure Iterators.Types.Take (α : Type w) (m : Type w → Type w') {β : Type
-/
finite : countdown > 0 Finite α m
open Std.Iterators Std.Iterators.Types
/--
Given an iterator `it` and a natural number `n`, `it.take n` is an iterator that outputs
up to the first `n` of `it`'s values in order and then terminates.
@@ -66,7 +65,7 @@ This combinator incurs an additional O(1) cost with each output of `it`.
-/
@[always_inline, inline]
def IterM.take [Iterator α m β] (n : Nat) (it : IterM (α := α) m β) :=
IterM.mk (Take.mk (n + 1) it (Or.inl <| Nat.zero_lt_succ _)) m β
toIterM (Take.mk (n + 1) it (Or.inl <| Nat.zero_lt_succ _)) m β
/--
This combinator is only useful for advanced use cases.
@@ -92,7 +91,7 @@ This combinator incurs an additional O(1) cost with each output of `it`.
-/
@[always_inline, inline]
def IterM.toTake [Iterator α m β] [Finite α m] (it : IterM (α := α) m β) :=
IterM.mk (Take.mk 0 it (Or.inr inferInstance)) m β
toIterM (Take.mk 0 it (Or.inr inferInstance)) m β
theorem IterM.take.surjective_of_zero_lt {α : Type w} {m : Type w Type w'} {β : Type w}
[Iterator α m β] (it : IterM (α := Take α m) m β) (h : 0 < it.internalState.countdown) :
@@ -101,8 +100,6 @@ theorem IterM.take.surjective_of_zero_lt {α : Type w} {m : Type w → Type w'}
simp only [take, Nat.sub_add_cancel (m := 1) (n := it.internalState.countdown) (by omega)]
rfl
namespace Iterators.Types
inductive Take.PlausibleStep [Iterator α m β] (it : IterM (α := Take α m) m β) :
(step : IterStep (IterM (α := Take α m) m β) β) Prop where
| yield : {it' out}, it.internalState.inner.IsPlausibleStep (.yield it' out)
@@ -164,7 +161,7 @@ theorem Take.rel_of_zero_of_inner [Monad m] [Iterator α m β]
private def Take.instFinitenessRelation [Monad m] [Iterator α m β]
[Productive α m] :
FinitenessRelation (Take α m) m where
Rel := Take.Rel m
rel := Take.Rel m
wf := by
rw [Rel]
split
@@ -207,8 +204,20 @@ instance Take.instFinite [Monad m] [Iterator α m β] [Productive α m] :
Finite (Take α m) m :=
by exact Finite.of_finitenessRelation instFinitenessRelation
instance Take.instIteratorCollect {n : Type w Type w'} [Monad m] [Monad n] [Iterator α m β] :
IteratorCollect (Take α m) m n :=
.defaultImplementation
instance Take.instIteratorCollectPartial {n : Type w Type w'} [Monad m] [Monad n] [Iterator α m β] :
IteratorCollectPartial (Take α m) m n :=
.defaultImplementation
instance Take.instIteratorLoop {n : Type x Type x'} [Monad m] [Monad n] [Iterator α m β] :
IteratorLoop (Take α m) m n :=
.defaultImplementation
end Std.Iterators.Types
instance Take.instIteratorLoopPartial [Monad m] [Monad n] [Iterator α m β] :
IteratorLoopPartial (Take α m) m n :=
.defaultImplementation
end Std.Iterators

View File

@@ -6,16 +6,16 @@ Authors: Paul Reichert
module
prelude
public import Init.Data.Iterators.Internal.Termination
public import Init.Data.Iterators.Consumers.Monadic
public section
namespace Std
namespace Std.Iterators
universe v u v' u'
section ULiftT
namespace Iterators
/-- `ULiftT.{v, u}` shrinks a monad on `Type max u v` to a monad on `Type u`. -/
@[expose] -- for codegen
@@ -60,14 +60,11 @@ theorem ULiftT.run_map {n : Type max u v → Type v'} [Monad n] {α β : Type u}
(f <$> x).run = x.run >>= (fun a => pure <| .up (f a.down)) :=
(rfl)
end Iterators
end ULiftT
namespace Iterators.Types
/-- Internal state of the `uLift` iterator combinator. Do not depend on its internals. -/
@[unbox]
structure ULiftIterator (α : Type u) (m : Type u Type u') (n : Type max u v Type v')
structure Types.ULiftIterator (α : Type u) (m : Type u Type u') (n : Type max u v Type v')
(β : Type u) (lift : γ : Type u, m γ ULiftT n γ) : Type max u v where
inner : IterM (α := α) m β
@@ -78,14 +75,14 @@ variable {α : Type u} {m : Type u → Type u'} {n : Type max u v → Type v'}
Transforms a step of the base iterator into a step of the `uLift` iterator.
-/
@[always_inline, inline, expose]
def ULiftIterator.Monadic.modifyStep (step : IterStep (IterM (α := α) m β) β) :
def Types.ULiftIterator.Monadic.modifyStep (step : IterStep (IterM (α := α) m β) β) :
IterStep (IterM (α := ULiftIterator.{v} α m n β lift) n (ULift.{v} β)) (ULift.{v} β) :=
match step with
| .yield it' out => .yield it' (.up out)
| .skip it' => .skip it'
| .done => .done
instance ULiftIterator.instIterator [Iterator α m β] [Monad n] :
instance Types.ULiftIterator.instIterator [Iterator α m β] [Monad n] :
Iterator (ULiftIterator α m n β lift) n (ULift β) where
IsPlausibleStep it step :=
step', it.internalState.inner.IsPlausibleStep step'
@@ -96,9 +93,9 @@ instance ULiftIterator.instIterator [Iterator α m β] [Monad n] :
where finally
case hp => exact step.inflate.val, step.inflate.property, rfl
private def ULiftIterator.instFinitenessRelation [Iterator α m β] [Finite α m] [Monad n] :
def Types.ULiftIterator.instFinitenessRelation [Iterator α m β] [Finite α m] [Monad n] :
FinitenessRelation (ULiftIterator α m n β lift) n where
Rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySteps)
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySteps)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation h := by
rcases h with _, hs, step, hp, rfl
@@ -108,13 +105,13 @@ private def ULiftIterator.instFinitenessRelation [Iterator α m β] [Finite α m
· apply IterM.TerminationMeasures.Finite.rel_of_skip
exact hp
instance ULiftIterator.instFinite [Iterator α m β] [Finite α m] [Monad n] :
instance Types.ULiftIterator.instFinite [Iterator α m β] [Finite α m] [Monad n] :
Finite (ULiftIterator α m n β lift) n :=
.of_finitenessRelation instFinitenessRelation
private def ULiftIterator.instProductivenessRelation [Iterator α m β] [Productive α m] [Monad n] :
def Types.ULiftIterator.instProductivenessRelation [Iterator α m β] [Productive α m] [Monad n] :
ProductivenessRelation (ULiftIterator α m n β lift) n where
Rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySkips)
rel := InvImage WellFoundedRelation.rel (fun it => it.internalState.inner.finitelyManySkips)
wf := InvImage.wf _ WellFoundedRelation.wf
subrelation h := by
rcases h with step, hp, hs
@@ -122,18 +119,26 @@ private def ULiftIterator.instProductivenessRelation [Iterator α m β] [Product
apply IterM.TerminationMeasures.Productive.rel_of_skip
exact hp
instance ULiftIterator.instProductive [Iterator α m β] [Productive α m] [Monad n] :
instance Types.ULiftIterator.instProductive [Iterator α m β] [Productive α m] [Monad n] :
Productive (ULiftIterator α m n β lift) n :=
.of_productivenessRelation instProductivenessRelation
instance ULiftIterator.instIteratorLoop {o : Type x Type x'} [Monad n] [Monad o]
instance Types.ULiftIterator.instIteratorLoop {o : Type x Type x'} [Monad n] [Monad o]
[Iterator α m β] :
IteratorLoop (ULiftIterator α m n β lift) n o :=
.defaultImplementation
end Iterators.Types
instance Types.ULiftIterator.instIteratorLoopPartial {o : Type x Type x'} [Monad n] [Monad o] [Iterator α m β] :
IteratorLoopPartial (ULiftIterator α m n β lift) n o :=
.defaultImplementation
open Std.Iterators Std.Iterators.Types
instance Types.ULiftIterator.instIteratorCollect [Monad n] [Monad o] [Iterator α m β] :
IteratorCollect (ULiftIterator α m n β lift) n o :=
.defaultImplementation
instance Types.ULiftIterator.instIteratorCollectPartial {o} [Monad n] [Monad o] [Iterator α m β] :
IteratorCollectPartial (ULiftIterator α m n β lift) n o :=
.defaultImplementation
/--
Transforms an `m`-monadic iterator with values in `β` into an `n`-monadic iterator with
@@ -152,9 +157,9 @@ it.uLift n ---.up a----.up b---.up c--.up d---⊥
* `Productive`: only if the original iterator is productive
-/
@[always_inline, inline, expose]
def IterM.uLift {α β : Type u} {m : Type u Type u'} (it : IterM (α := α) m β)
(n : Type max u v Type v') [lift : MonadLiftT m (ULiftT n)] :
IterM (α := ULiftIterator α m n β (fun _ => lift.monadLift)) n (ULift β) :=
def IterM.uLift (it : IterM (α := α) m β) (n : Type max u v Type v')
[lift : MonadLiftT m (ULiftT n)] :
IterM (α := Types.ULiftIterator α m n β (fun _ => lift.monadLift)) n (ULift β) :=
it
end Std
end Std.Iterators

View File

@@ -10,8 +10,7 @@ public import Init.Data.Iterators.Combinators.Monadic.Take
@[expose] public section
namespace Std
open Std.Iterators Std.Iterators.Types
namespace Std.Iterators
/--
Given an iterator `it` and a natural number `n`, `it.take n` is an iterator that outputs
@@ -68,4 +67,4 @@ def Iter.toTake {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] (
Iter (α := Take α Id) β :=
it.toIterM.toTake.toIter
end Std
end Std.Iterators

View File

@@ -10,8 +10,7 @@ public import Init.Data.Iterators.Combinators.Monadic.ULift
public section
namespace Std
open Std.Iterators Std.Iterators.Types
namespace Std.Iterators
universe v u v' u'
@@ -21,10 +20,10 @@ variable {α : Type u} {β : Type u}
Transforms a step of the base iterator into a step of the `uLift` iterator.
-/
@[always_inline, inline]
def Iterators.Types.ULiftIterator.modifyStep (step : IterStep (Iter (α := α) β) β) :
def Types.ULiftIterator.modifyStep (step : IterStep (Iter (α := α) β) β) :
IterStep (Iter (α := ULiftIterator.{v} α Id Id β (fun _ => monadLift)) (ULift.{v} β))
(ULift.{v} β) :=
(ULiftIterator.Monadic.modifyStep (step.mapIterator Iter.toIterM)).mapIterator IterM.toIter
(Monadic.modifyStep (step.mapIterator Iter.toIterM)).mapIterator IterM.toIter
/--
Transforms an iterator with values in `β` into one with values in `ULift β`.
@@ -49,4 +48,4 @@ def Iter.uLift (it : Iter (α := α) β) :
Iter (α := Types.ULiftIterator.{v} α Id Id β (fun _ => monadLift)) (ULift β) :=
(it.toIterM.uLift Id).toIter
end Std
end Std.Iterators

View File

@@ -11,6 +11,5 @@ public import Init.Data.Iterators.Consumers.Access
public import Init.Data.Iterators.Consumers.Collect
public import Init.Data.Iterators.Consumers.Loop
public import Init.Data.Iterators.Consumers.Partial
public import Init.Data.Iterators.Consumers.Total
public import Init.Data.Iterators.Consumers.Stream

View File

@@ -11,8 +11,7 @@ public import Init.Data.Iterators.Consumers.Monadic.Access
@[expose] public section
namespace Std
open Std.Iterators
namespace Std.Iterators
/--
If possible, takes `n` steps with the iterator `it` and
@@ -63,4 +62,4 @@ def Iter.atIdx? {α β} [Iterator α Id β] [Productive α Id] [IteratorAccess
| .skip _ => none
| .done => none
end Std
end Std.Iterators

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Data.Iterators.Consumers.Partial
public import Init.Data.Iterators.Consumers.Total
public import Init.Data.Iterators.Consumers.Monadic.Collect
@[expose] public section
@@ -21,113 +20,41 @@ Concretely, the following operations are provided:
* `Iter.toList`, collecting the values in a list
* `Iter.toListRev`, collecting the values in a list in reverse order but more efficiently
* `Iter.toArray`, collecting the values in an array
Some operations are implemented using the `IteratorCollect` and `IteratorCollectPartial`
typeclasses.
-/
namespace Std
open Std.Iterators
namespace Std.Iterators
/--
Traverses the given iterator and stores the emitted values in an array.
If the iterator is not finite, this function might run forever. The variant
`it.ensureTermination.toArray` always terminates after finitely many steps.
-/
@[always_inline, inline]
@[always_inline, inline, inherit_doc IterM.toArray]
def Iter.toArray {α : Type w} {β : Type w}
[Iterator α Id β] (it : Iter (α := α) β) : Array β :=
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter (α := α) β) : Array β :=
it.toIterM.toArray.run
/--
Traverses the given iterator and stores the emitted values in an array.
This function is deprecated. Instead of `it.allowNontermination.toArray`, use `it.toArray`.
-/
@[always_inline, inline, deprecated Iter.toArray (since := "2025-12-04")]
@[always_inline, inline, inherit_doc IterM.Partial.toArray]
def Iter.Partial.toArray {α : Type w} {β : Type w}
[Iterator α Id β] (it : Iter.Partial (α := α) β) : Array β :=
it.it.toArray
[Iterator α Id β] [IteratorCollectPartial α Id Id] (it : Iter.Partial (α := α) β) : Array β :=
it.it.toIterM.allowNontermination.toArray.run
/--
Traverses the given iterator and stores the emitted values in an array.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using `Iter.toArray`.
-/
@[always_inline, inline]
def Iter.Total.toArray {α : Type w} {β : Type w}
[Iterator α Id β] [Finite α Id] (it : Iter.Total (α := α) β) :
Array β :=
it.it.toArray
/--
Traverses the given iterator and stores the emitted values in reverse order in a list. Because
lists are prepend-only, this `toListRev` is usually more efficient that `toList`.
If the iterator is not finite, this function might run forever. The variant
`it.ensureTermination.toListRev` always terminates after finitely many steps.
-/
@[always_inline, inline]
@[always_inline, inline, inherit_doc IterM.toListRev]
def Iter.toListRev {α : Type w} {β : Type w}
[Iterator α Id β] (it : Iter (α := α) β) : List β :=
[Iterator α Id β] [Finite α Id] (it : Iter (α := α) β) : List β :=
it.toIterM.toListRev.run
/--
Traverses the given iterator and stores the emitted values in reverse order in a list. Because
lists are prepend-only, this `toListRev` is usually more efficient that `toList`.
This function is deprecated. Instead of `it.allowNontermination.toListRev`, use `it.toListRev`.
-/
@[always_inline, inline, deprecated Iter.toListRev (since := "2025-12-04")]
@[always_inline, inline, inherit_doc IterM.Partial.toListRev]
def Iter.Partial.toListRev {α : Type w} {β : Type w}
[Iterator α Id β] (it : Iter.Partial (α := α) β) : List β :=
it.it.toListRev
it.it.toIterM.allowNontermination.toListRev.run
/--
Traverses the given iterator and stores the emitted values in reverse order in a list. Because
lists are prepend-only, this `toListRev` is usually more efficient that `toList`.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using `Iter.toListRev`.
-/
@[always_inline, inline]
def Iter.Total.toListRev {α : Type w} {β : Type w}
[Iterator α Id β] [Finite α Id] (it : Iter.Total (α := α) β) : List β :=
it.it.toListRev
/--
Traverses the given iterator and stores the emitted values in a list. Because
lists are prepend-only, `toListRev` is usually more efficient that `toList`.
If the iterator is not finite, this function might run forever. The variant
`it.ensureTermination.toList` always terminates after finitely many steps.
-/
@[always_inline, inline]
@[always_inline, inline, inherit_doc IterM.toList]
def Iter.toList {α : Type w} {β : Type w}
[Iterator α Id β] (it : Iter (α := α) β) : List β :=
[Iterator α Id β] [Finite α Id] [IteratorCollect α Id Id] (it : Iter (α := α) β) : List β :=
it.toIterM.toList.run
/--
Traverses the given iterator and stores the emitted values in a list. Because
lists are prepend-only, `toListRev` is usually more efficient that `toList`.
This function is deprecated. Instead of `it.allowNontermination.toList`, use `it.toList`.
-/
@[always_inline, deprecated Iter.toList (since := "2025-12-04")]
@[always_inline, inline, inherit_doc IterM.Partial.toList]
def Iter.Partial.toList {α : Type w} {β : Type w}
[Iterator α Id β] (it : Iter.Partial (α := α) β) : List β :=
it.it.toList
[Iterator α Id β] [IteratorCollectPartial α Id Id] (it : Iter.Partial (α := α) β) : List β :=
it.it.toIterM.allowNontermination.toList.run
/--
Traverses the given iterator and stores the emitted values in a list. Because
lists are prepend-only, `toListRev` is usually more efficient that `toList`.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using `Iter.toList`.
-/
@[always_inline, inline]
def Iter.Total.toList {α : Type w} {β : Type w}
[Iterator α Id β] [Finite α Id] (it : Iter.Total (α := α) β) :
List β :=
it.it.toList
end Std
end Std.Iterators

View File

@@ -9,8 +9,6 @@ prelude
public import Init.Data.Iterators.Consumers.Collect
public import Init.Data.Iterators.Consumers.Monadic.Loop
set_option linter.missingDocs true
public section
/-!
@@ -23,11 +21,10 @@ function in every iteration. Concretely, the following operations are provided:
* `Iter.fold`, the analogue of `List.foldl`
* `Iter.foldM`, the analogue of `List.foldlM`
These operations are implemented using the `IteratorLoop` type class.
These operations are implemented using the `IteratorLoop` and `IteratorLoopPartial` typeclasses.
-/
namespace Std
open Std.Iterators
namespace Std.Iterators
/--
A `ForIn'` instance for iterators. Its generic membership relation is not easy to use,
@@ -36,7 +33,7 @@ or future library improvements will make it more comfortable.
-/
@[always_inline, inline]
def Iter.instForIn' {α : Type w} {β : Type w} {n : Type x Type x'} [Monad n]
[Iterator α Id β] [IteratorLoop α Id n] :
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id n] :
ForIn' n (Iter (α := α) β) β fun it out => it.IsPlausibleIndirectOutput out where
forIn' it init f :=
IteratorLoop.finiteForIn' (fun _ _ f c => f c.run) |>.forIn' it.toIterM init
@@ -44,68 +41,51 @@ def Iter.instForIn' {α : Type w} {β : Type w} {n : Type x → Type x'} [Monad
f out (Iter.isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM.mpr h) acc
instance (α : Type w) (β : Type w) (n : Type x Type x') [Monad n]
[Iterator α Id β] [IteratorLoop α Id n] :
[Iterator α Id β] [Finite α Id] [IteratorLoop α Id n] :
ForIn n (Iter (α := α) β) β :=
haveI : ForIn' n (Iter (α := α) β) β _ := Iter.instForIn'
instForInOfForIn'
/--
An implementation of `for h : ... in ... do ...` notation for partial iterators.
-/
@[always_inline, inline]
def Iter.Partial.instForIn' {α : Type w} {β : Type w} {n : Type x Type x'} [Monad n]
[Iterator α Id β] [IteratorLoop α Id n] :
[Iterator α Id β] [IteratorLoopPartial α Id n] :
ForIn' n (Iter.Partial (α := α) β) β fun it out => it.it.IsPlausibleIndirectOutput out where
forIn' it init f :=
haveI := @Iter.instForIn'
forIn' it.it init f
IteratorLoopPartial.forInPartial (α := α) (m := Id) (n := n) (fun _ _ f c => f c.run)
it.it.toIterM init
fun out h acc =>
f out (Iter.isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM.mpr h) acc
instance (α : Type w) (β : Type w) (n : Type x Type x') [Monad n]
[Iterator α Id β] [IteratorLoop α Id n] :
[Iterator α Id β] [IteratorLoopPartial α Id n] :
ForIn n (Iter.Partial (α := α) β) β :=
haveI : ForIn' n (Iter.Partial (α := α) β) β _ := Iter.Partial.instForIn'
instForInOfForIn'
/--
A `ForIn'` instance for iterators that is guaranteed to terminate after finitely many steps.
It is not marked as an instance because the membership predicate is difficult to work with.
-/
@[always_inline, inline]
def Iter.Total.instForIn' {α : Type w} {β : Type w} {n : Type x Type x'} [Monad n]
[Iterator α Id β] [IteratorLoop α Id n] [Finite α Id] :
ForIn' n (Iter.Total (α := α) β) β fun it out => it.it.IsPlausibleIndirectOutput out where
forIn' it init f := Iter.instForIn'.forIn' it.it init f
instance (α : Type w) (β : Type w) (n : Type x Type x') [Monad n]
[Iterator α Id β] [IteratorLoop α Id n] [Finite α Id] :
ForIn n (Iter.Total (α := α) β) β :=
haveI : ForIn' n (Iter.Total (α := α) β) β _ := Iter.Total.instForIn'
instForInOfForIn'
instance {m : Type x Type x'}
{α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id m] [Monad m] :
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id m] :
ForM m (Iter (α := α) β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
instance {m : Type x Type x'}
{α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id m] [Monad m] :
{α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoopPartial α Id m] :
ForM m (Iter.Partial (α := α) β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
instance {m : Type x Type x'}
{α : Type w} {β : Type w} [Monad m] [Iterator α Id β] [IteratorLoop α Id m] [Finite α Id] :
ForM m (Iter.Total (α := α) β) β where
forM it f := forIn it PUnit.unit (fun out _ => do f out; return .yield .unit)
/--
Folds a monadic function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldlM`.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.foldM` instead of `it.foldM`. However, it is not possible to formally
verify the behavior of the partial variant.
-/
@[always_inline, inline]
def Iter.foldM {m : Type x Type x'} [Monad m]
{α : Type w} {β : Type w} {γ : Type x} [Iterator α Id β]
{α : Type w} {β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id m] (f : γ β m γ)
(init : γ) (it : Iter (α := α) β) : m γ :=
ForIn.forIn it init (fun x acc => ForInStep.yield <$> f acc x)
@@ -116,39 +96,29 @@ The accumulated value is combined with the each element of the list in order, us
It is equivalent to `it.toList.foldlM`.
This function is deprecated. Instead of `it.allowNontermination.foldM`, use `it.foldM`.
-/
@[always_inline, inline, deprecated Iter.foldM (since := "2025-12-04")]
def Iter.Partial.foldM {m : Type x Type x'} [Monad m]
{α : Type w} {β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id m] (f : γ β m γ)
(init : γ) (it : Iter.Partial (α := α) β) : m γ :=
it.it.foldM (init := init) f
/--
Folds a monadic function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldlM`.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using `Iter.foldM`.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.foldM` instead.
-/
@[always_inline, inline]
def Iter.Total.foldM {m : Type x Type x'} [Monad m]
def Iter.Partial.foldM {m : Type x Type x'} [Monad m]
{α : Type w} {β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id] (f : γ β m γ)
(init : γ) (it : Iter.Total (α := α) β) : m γ :=
it.it.foldM (init := init) f
[IteratorLoopPartial α Id m] (f : γ β m γ)
(init : γ) (it : Iter.Partial (α := α) β) : m γ :=
ForIn.forIn it init (fun x acc => ForInStep.yield <$> f acc x)
/--
Folds a function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldl`.
This function requires a `Finite` instance proving that the iterator will finish after a finite
number of steps. If the iterator is not finite or such an instance is not available, consider using
`it.allowNontermination.fold` instead of `it.fold`. However, it is not possible to formally
verify the behavior of the partial variant.
-/
@[always_inline, inline]
def Iter.fold {α : Type w} {β : Type w} {γ : Type x} [Iterator α Id β]
def Iter.fold {α : Type w} {β : Type w} {γ : Type x} [Iterator α Id β] [Finite α Id]
[IteratorLoop α Id Id] (f : γ β γ)
(init : γ) (it : Iter (α := α) β) : γ :=
ForIn.forIn (m := Id) it init (fun x acc => ForInStep.yield (f acc x))
@@ -159,28 +129,14 @@ The accumulated value is combined with the each element of the list in order, us
It is equivalent to `it.toList.foldl`.
This function is deprecated. Instead of `it.allowNontermination.fold`, use `it.fold`.
-/
@[always_inline, inline, deprecated Iter.fold (since := "2025-12-04")]
def Iter.Partial.fold {α : Type w} {β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id Id] (f : γ β γ)
(init : γ) (it : Iter.Partial (α := α) β) : γ :=
it.it.fold (init := init) f
/--
Folds a function over an iterator from the left, accumulating a value starting with `init`.
The accumulated value is combined with the each element of the list in order, using `f`.
It is equivalent to `it.toList.foldl`.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using `Iter.fold`.
This is a partial, potentially nonterminating, function. It is not possible to formally verify
its behavior. If the iterator has a `Finite` instance, consider using `IterM.fold` instead.
-/
@[always_inline, inline]
def Iter.Total.fold {α : Type w} {β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] (f : γ β γ)
(init : γ) (it : Iter.Total (α := α) β) : γ :=
it.it.fold (init := init) f
def Iter.Partial.fold {α : Type w} {β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoopPartial α Id Id] (f : γ β γ)
(init : γ) (it : Iter.Partial (α := α) β) : γ :=
ForIn.forIn (m := Id) it init (fun x acc => ForInStep.yield (f acc x))
set_option doc.verso true in
/--
@@ -190,9 +146,9 @@ any element emitted by the iterator {name}`it`.
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
examined in order of iteration.
-/
@[always_inline]
@[specialize]
def Iter.anyM {α β : Type w} {m : Type Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m]
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id]
(p : β m Bool) (it : Iter (α := α) β) : m Bool :=
ForIn.forIn it false (fun x _ => do
if p x then
@@ -200,23 +156,6 @@ def Iter.anyM {α β : Type w} {m : Type → Type w'} [Monad m]
else
return .yield false)
set_option doc.verso true in
/--
Returns {lean}`true` if the monadic predicate {name}`p` returns {lean}`true` for
any element emitted by the iterator {name}`it`.
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
examined in order of iteration.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using {name}`Iter.anyM`.
-/
@[always_inline, inline]
def Iter.Total.anyM {α β : Type w} {m : Type Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id]
(p : β m Bool) (it : Iter.Total (α := α) β) : m Bool :=
it.it.anyM p
set_option doc.verso true in
/--
Returns {lean}`true` if the pure predicate {name}`p` returns {lean}`true` for
@@ -227,38 +166,21 @@ examined in order of iteration.
-/
@[inline]
def Iter.any {α β : Type w}
[Iterator α Id β] [IteratorLoop α Id Id]
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
(p : β Bool) (it : Iter (α := α) β) : Bool :=
(it.anyM (fun x => pure (f := Id) (p x))).run
set_option doc.verso true in
/--
Returns {lean}`true` if the pure predicate {name}`p` returns {lean}`true` for
any element emitted by the iterator {name}`it`.
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
examined in order of iteration.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using {name}`Iter.any`.
-/
@[inline]
def Iter.Total.any {α β : Type w}
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
(p : β Bool) (it : Iter.Total (α := α) β) : Bool :=
it.it.any p
set_option doc.verso true in
/--
Returns {lean}`true` if the monadic predicate {name}`p` returns {lean}`true` for
all element emitted by the iterator {name}`it`.
all elements emitted by the iterator {name}`it`.
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
examined in order of iteration.
-/
@[always_inline, inline]
@[specialize]
def Iter.allM {α β : Type w} {m : Type Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m]
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id]
(p : β m Bool) (it : Iter (α := α) β) : m Bool :=
ForIn.forIn it true (fun x _ => do
if p x then
@@ -266,369 +188,72 @@ def Iter.allM {α β : Type w} {m : Type → Type w'} [Monad m]
else
return .done false)
set_option doc.verso true in
/--
Returns {lean}`true` if the monadic predicate {name}`p` returns {lean}`true` for
all element emitted by the iterator {name}`it`.
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
examined in order of iteration.
This variant terminates after finitely mall steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using {name}`Iter.allM`.
-/
@[always_inline, inline]
def Iter.Total.allM {α β : Type w} {m : Type Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id]
(p : β m Bool) (it : Iter.Total (α := α) β) : m Bool :=
it.it.allM p
set_option doc.verso true in
/--
Returns {lean}`true` if the pure predicate {name}`p` returns {lean}`true` for
all element emitted by the iterator {name}`it`.
all elements emitted by the iterator {name}`it`.
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
{lit}`O(|xs|)`. Short-circuits upon encountering the first mismatch. The elements in {name}`it` are
examined in order of iteration.
-/
@[inline]
def Iter.all {α β : Type w}
[Iterator α Id β] [IteratorLoop α Id Id]
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
(p : β Bool) (it : Iter (α := α) β) : Bool :=
(it.allM (fun x => pure (f := Id) (p x))).run
set_option doc.verso true in
/--
Returns {lean}`true` if the pure predicate {name}`p` returns {lean}`true` for
all element emitted by the iterator {name}`it`.
{lit}`O(|xs|)`. Short-circuits upon encountering the first match. The elements in {name}`it` are
examined in order of iteration.
This variant terminates after finitely mall steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using {name}`Iter.all`.
-/
@[inline]
def Iter.Total.all {α β : Type w}
[Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
(p : β Bool) (it : Iter.Total (α := α) β) : Bool :=
it.it.all p
/--
Returns the first non-`none` result of applying the monadic function `f` to each output
of the iterator, in order. Returns `none` if `f` returns `none` for all outputs.
`O(|it|)`. Short-circuits when `f` returns `some _`. The outputs of `it` are
examined in order of iteration.
If the iterator is not finite, this function might run forever. The variant
`it.ensureTermination.findSomeM?` always terminates after finitely many steps.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].iter.findSomeM? fun i => do
if i < 5 then
return some (i * 10)
if i ≤ 6 then
IO.println s!"Almost! {i}"
return none
```
```output
Almost! 6
Almost! 5
```
```output
some 10
```
-/
@[inline]
def Iter.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] (it : Iter (α := α) β) (f : β m (Option γ)) :
[IteratorLoop α Id m] [Finite α Id] (it : Iter (α := α) β) (f : β m (Option γ)) :
m (Option γ) :=
ForIn.forIn it none (fun x _ => do
match f x with
| none => return .yield none
| some fx => return .done (some fx))
/--
Returns the first non-`none` result of applying the monadic function `f` to each output
of the iterator, in order. Returns `none` if `f` returns `none` for all outputs.
`O(|it|)`. Short-circuits when `f` returns `some _`. The outputs of `it` are
examined in order of iteration.
This function is deprecated. Instead of `it.allowNontermination.findSomeM?`, use `it.findSomeM?`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].iter.findSomeM? fun i => do
if i < 5 then
return some (i * 10)
if i ≤ 6 then
IO.println s!"Almost! {i}"
return none
```
```output
Almost! 6
Almost! 5
```
```output
some 10
```
-/
@[inline, deprecated Iter.findSomeM? (since := "2025-12-04")]
def Iter.Partial.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] (it : Iter.Partial (α := α) β)
(f : β m (Option γ)) :
m (Option γ) :=
it.it.findSomeM? f
/--
Returns the first non-`none` result of applying the monadic function `f` to each output
of the iterator, in order. Returns `none` if `f` returns `none` for all outputs.
`O(|it|)`. Short-circuits when `f` returns `some _`. The outputs of `it` are
examined in order of iteration.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using `Iter.findSomeM?`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].iter.findSomeM? fun i => do
if i < 5 then
return some (i * 10)
if i ≤ 6 then
IO.println s!"Almost! {i}"
return none
```
```output
Almost! 6
Almost! 5
```
```output
some 10
```
-/
@[inline]
def Iter.Total.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoop α Id m] [Finite α Id] (it : Iter.Total (α := α) β)
def Iter.Partial.findSomeM? {α β : Type w} {γ : Type x} {m : Type x Type w'} [Monad m]
[Iterator α Id β] [IteratorLoopPartial α Id m] (it : Iter.Partial (α := α) β)
(f : β m (Option γ)) :
m (Option γ) :=
it.it.findSomeM? f
ForIn.forIn it none (fun x _ => do
match f x with
| none => return .yield none
| some fx => return .done (some fx))
/--
Returns the first non-`none` result of applying `f` to each output of the iterator, in order.
Returns `none` if `f` returns `none` for all outputs.
`O(|it|)`. Short-circuits when `f` returns `some _`.The outputs of `it` are examined in order of
iteration.
If the iterator is not finite, this function might run forever. The variant
`it.ensureTermination.findSome?` always terminates after finitely many steps.
Examples:
* `[7, 6, 5, 8, 1, 2, 6].iter.findSome? (fun x => if x < 5 then some (10 * x) else none) = some 10`
* `[7, 6, 5, 8, 1, 2, 6].iter.findSome? (fun x => if x < 1 then some (10 * x) else none) = none`
-/
@[inline]
def Iter.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id Id] (it : Iter (α := α) β) (f : β Option γ) :
[IteratorLoop α Id Id] [Finite α Id] (it : Iter (α := α) β) (f : β Option γ) :
Option γ :=
Id.run (it.findSomeM? (pure <| f ·))
/--
Returns the first non-`none` result of applying `f` to each output of the iterator, in order.
Returns `none` if `f` returns `none` for all outputs.
`O(|it|)`. Short-circuits when `f` returns `some _`.The outputs of `it` are examined in order of
iteration.
This function is deprecated. Instead of `it.allowNontermination.findSome?`, use `it.findSome?`.
Examples:
* `[7, 6, 5, 8, 1, 2, 6].iter.allowNontermination.findSome? (fun x => if x < 5 then some (10 * x) else none) = some 10`
* `[7, 6, 5, 8, 1, 2, 6].iter.allowNontermination.findSome? (fun x => if x < 1 then some (10 * x) else none) = none`
-/
@[inline, deprecated Iter.findSome? (since := "2025-12-04")]
def Iter.Partial.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id Id] (it : Iter.Partial (α := α) β) (f : β Option γ) :
Option γ :=
it.it.findSome? f
/--
Returns the first non-`none` result of applying `f` to each output of the iterator, in order.
Returns `none` if `f` returns `none` for all outputs.
`O(|it|)`. Short-circuits when `f` returns `some _`.The outputs of `it` are examined in order of
iteration.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using `Iter.findSome?`.
Examples:
* `[7, 6, 5, 8, 1, 2, 6].iter.ensureTermination.findSome? (fun x => if x < 5 then some (10 * x) else none) = some 10`
* `[7, 6, 5, 8, 1, 2, 6].iter.ensureTermination.findSome? (fun x => if x < 1 then some (10 * x) else none) = none`
-/
@[inline]
def Iter.Total.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoop α Id Id] [Finite α Id] (it : Iter.Total (α := α) β) (f : β Option γ) :
def Iter.Partial.findSome? {α β : Type w} {γ : Type x} [Iterator α Id β]
[IteratorLoopPartial α Id Id] (it : Iter.Partial (α := α) β) (f : β Option γ) :
Option γ :=
it.it.findSome? f
Id.run (it.findSomeM? (pure <| f ·))
/--
Returns the first output of the iterator for which the monadic predicate `p` returns `true`, or
`none` if no such element is found.
`O(|it|)`. Short-circuits when `f` returns `true`. The outputs of `it` are examined in order of
iteration.
If the iterator is not finite, this function might run forever. The variant
`it.ensureTermination.findM?` always terminates after finitely many steps.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].iter.findM? fun i => do
if i < 5 then
return true
if i ≤ 6 then
IO.println s!"Almost! {i}"
return false
```
```output
Almost! 6
Almost! 5
```
```output
some 1
```
-/
@[inline]
def Iter.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] (it : Iter (α := α) β) (f : β m (ULift Bool)) :
[IteratorLoop α Id m] [Finite α Id] (it : Iter (α := α) β) (f : β m (ULift Bool)) :
m (Option β) :=
it.findSomeM? (fun x => return if ( f x).down then some x else none)
/--
Returns the first output of the iterator for which the monadic predicate `p` returns `true`, or
`none` if no such element is found.
`O(|it|)`. Short-circuits when `f` returns `true`. The outputs of `it` are examined in order of
iteration.
This function is deprecated. Instead of `it.ensureTermination.findM?`, use `it.findM?`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].iter.findM? fun i => do
if i < 5 then
return true
if i ≤ 6 then
IO.println s!"Almost! {i}"
return false
```
```output
Almost! 6
Almost! 5
```
```output
some 1
```
-/
@[inline, deprecated Iter.findM? (since := "2025-12-04")]
def Iter.Partial.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] (it : Iter.Partial (α := α) β) (f : β m (ULift Bool)) :
m (Option β) :=
it.it.findM? f
/--
Returns the first output of the iterator for which the monadic predicate `p` returns `true`, or
`none` if no such element is found.
`O(|it|)`. Short-circuits when `f` returns `true`. The outputs of `it` are examined in order of
iteration.
This variant requires terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using `Iter.findM?`.
Example:
```lean example
#eval [7, 6, 5, 8, 1, 2, 6].iter.findM? fun i => do
if i < 5 then
return true
if i ≤ 6 then
IO.println s!"Almost! {i}"
return false
```
```output
Almost! 6
Almost! 5
```
```output
some 1
```
-/
@[inline]
def Iter.Total.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoop α Id m] [Finite α Id] (it : Iter.Total (α := α) β) (f : β m (ULift Bool)) :
def Iter.Partial.findM? {α β : Type w} {m : Type w Type w'} [Monad m] [Iterator α Id β]
[IteratorLoopPartial α Id m] (it : Iter.Partial (α := α) β) (f : β m (ULift Bool)) :
m (Option β) :=
it.it.findM? f
it.findSomeM? (fun x => return if ( f x).down then some x else none)
/--
Returns the first output of the iterator for which the predicate `p` returns `true`, or `none` if
no such output is found.
`O(|it|)`. Short-circuits upon encountering the first match. The elements in `it` are examined in
order of iteration.
If the iterator is not finite, this function might run forever. The variant
`it.ensureTermination.find?` always terminates after finitely many steps.
Examples:
* `[7, 6, 5, 8, 1, 2, 6].iter.find? (· < 5) = some 1`
* `[7, 6, 5, 8, 1, 2, 6].iter.find? (· < 1) = none`
-/
@[inline]
def Iter.find? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter (α := α) β) (f : β Bool) : Option β :=
[Finite α Id] (it : Iter (α := α) β) (f : β Bool) : Option β :=
Id.run (it.findM? (pure <| .up <| f ·))
/--
Returns the first output of the iterator for which the predicate `p` returns `true`, or `none` if
no such output is found.
`O(|it|)`. Short-circuits upon encountering the first match. The elements in `it` are examined in
order of iteration.
This function is deprecated. Instead of `it.allowNontermination.find?`, use `it.find?`.
Examples:
* `[7, 6, 5, 8, 1, 2, 6].iter.allowNontermination.find? (· < 5) = some 1`
* `[7, 6, 5, 8, 1, 2, 6].iter.allowNontermination.find? (· < 1) = none`
-/
@[inline, deprecated Iter.find? (since := "2025-12-04")]
def Iter.Partial.find? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
(it : Iter.Partial (α := α) β) (f : β Bool) : Option β :=
it.it.find? f
/--
Returns the first output of the iterator for which the predicate `p` returns `true`, or `none` if
no such output is found.
`O(|it|)`. Short-circuits upon encountering the first match. The elements in `it` are examined in
order of iteration.
This variant terminates after finitely many steps and requires a proof that the iterator is
finite. If such a proof is not available, consider using `Iter.find?`.
Examples:
* `[7, 6, 5, 8, 1, 2, 6].iter.find? (· < 5) = some 1`
* `[7, 6, 5, 8, 1, 2, 6].iter.find? (· < 1) = none`
-/
@[inline]
def Iter.Total.find? {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id] [Finite α Id]
(it : Iter.Total (α := α) β) (f : β Bool) : Option β :=
it.it.find? f
def Iter.Partial.find? {α β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
(it : Iter.Partial (α := α) β) (f : β Bool) : Option β :=
Id.run (it.findM? (pure <| .up <| f ·))
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -638,7 +263,7 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose]
def Iter.count {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
def Iter.count {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Nat :=
it.toIterM.count.run.down
@@ -650,7 +275,7 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-10-29")]
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
def Iter.size {α : Type w} {β : Type w} [Iterator α Id β] [Finite α Id] [IteratorLoop α Id Id]
(it : Iter (α := α) β) : Nat :=
it.count
@@ -661,10 +286,10 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-12-04")]
def Iter.Partial.count {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
@[always_inline, inline, expose]
def Iter.Partial.count {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
(it : Iter.Partial (α := α) β) : Nat :=
it.it.toIterM.count.run.down
it.it.toIterM.allowNontermination.count.run.down
/--
Steps through the whole iterator, counting the number of outputs emitted.
@@ -673,9 +298,9 @@ Steps through the whole iterator, counting the number of outputs emitted.
This function's runtime is linear in the number of steps taken by the iterator.
-/
@[always_inline, inline, expose, deprecated Iter.count (since := "2025-10-29")]
def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
@[always_inline, inline, expose, deprecated Iter.Partial.count (since := "2025-10-29")]
def Iter.Partial.size {α : Type w} {β : Type w} [Iterator α Id β] [IteratorLoopPartial α Id Id]
(it : Iter.Partial (α := α) β) : Nat :=
it.it.count
it.count
end Std
end Std.Iterators

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