mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 18:34:06 +00:00
Compare commits
228 Commits
ofList_eq_
...
missing_ar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
140ec40b6c | ||
|
|
a912652b7d | ||
|
|
3af9ab64ed | ||
|
|
a6dd6a4656 | ||
|
|
1e752b0a01 | ||
|
|
11f7d6da39 | ||
|
|
e2fc9ba92e | ||
|
|
c1866a7b7e | ||
|
|
03e905d994 | ||
|
|
383f68f806 | ||
|
|
41c2ae12f3 | ||
|
|
9982bab93e | ||
|
|
be513656b0 | ||
|
|
bdbb659765 | ||
|
|
2a1354b3cc | ||
|
|
a54872f5f6 | ||
|
|
2b0b1e013f | ||
|
|
1f000feb80 | ||
|
|
d5060e9e66 | ||
|
|
38ca310fb7 | ||
|
|
3dd12f85f0 | ||
|
|
0f8618f842 | ||
|
|
acdef6e04b | ||
|
|
7b80cd24a9 | ||
|
|
21846ebdf8 | ||
|
|
9ea4946560 | ||
|
|
3b205505ef | ||
|
|
6afa8208ec | ||
|
|
65a5d0cb9d | ||
|
|
fc3c82b1c7 | ||
|
|
8fc94c5c90 | ||
|
|
96b81f3cc1 | ||
|
|
44ff70020d | ||
|
|
ae1ab94992 | ||
|
|
5e40f4af52 | ||
|
|
2594a8edad | ||
|
|
b24e232a7a | ||
|
|
9ad3974314 | ||
|
|
b31bf4e645 | ||
|
|
c8d245a08f | ||
|
|
4eccb5b479 | ||
|
|
0a43c138ac | ||
|
|
1138062a70 | ||
|
|
ebf455a137 | ||
|
|
87cc330489 | ||
|
|
47a1355fc4 | ||
|
|
79254d039c | ||
|
|
c28b052576 | ||
|
|
a541b8e75e | ||
|
|
a9a069a0ef | ||
|
|
8753239226 | ||
|
|
f4ee72b18c | ||
|
|
8535a2268b | ||
|
|
d8e7ca2355 | ||
|
|
8e0870beec | ||
|
|
3790f8c78e | ||
|
|
3bf95e9b58 | ||
|
|
bc21b57396 | ||
|
|
6395d69140 | ||
|
|
4ba72aeef7 | ||
|
|
e984473886 | ||
|
|
88f6439955 | ||
|
|
fc8f290347 | ||
|
|
423b31755d | ||
|
|
d1ec806834 | ||
|
|
b93231f97e | ||
|
|
f40d72ea47 | ||
|
|
10fdfc54cb | ||
|
|
943a9c6a43 | ||
|
|
a8a6f71abb | ||
|
|
9ad4414642 | ||
|
|
efe2ab4c04 | ||
|
|
831026bcf4 | ||
|
|
fbac0d2ddb | ||
|
|
e7b8df0c0e | ||
|
|
601ea24e31 | ||
|
|
ca037ded0d | ||
|
|
006d2925ba | ||
|
|
c8290bd942 | ||
|
|
b7b95896aa | ||
|
|
e46daa8ee6 | ||
|
|
3854ba87b6 | ||
|
|
4d58a3d124 | ||
|
|
6b7a803bf4 | ||
|
|
0e96318c72 | ||
|
|
7994e55d80 | ||
|
|
d24aa91232 | ||
|
|
e7b61232c9 | ||
|
|
af7eb01f29 | ||
|
|
ca9b3eb75f | ||
|
|
a817067295 | ||
|
|
fcb6bcee67 | ||
|
|
73509d03f3 | ||
|
|
6448547f41 | ||
|
|
632b688cb7 | ||
|
|
c5335b6f9a | ||
|
|
a594f655da | ||
|
|
7a6bca5276 | ||
|
|
e5393cf6bc | ||
|
|
3481f43130 | ||
|
|
528fe0b0ed | ||
|
|
01dbbeed99 | ||
|
|
9486421fcc | ||
|
|
d69a8eff3f | ||
|
|
8154aaa1b3 | ||
|
|
abc85c2f3c | ||
|
|
436221986a | ||
|
|
49369f9c7c | ||
|
|
305fba625d | ||
|
|
83001213e3 | ||
|
|
06ef738aec | ||
|
|
37529a5518 | ||
|
|
fad3e0ef5e | ||
|
|
e982bf9472 | ||
|
|
4efef5760c | ||
|
|
0a32ba371a | ||
|
|
f699e18212 | ||
|
|
b8c941d39a | ||
|
|
995fa4766b | ||
|
|
e635eeacd3 | ||
|
|
7240ae2fa2 | ||
|
|
12ff2d8c49 | ||
|
|
88078930a9 | ||
|
|
6ca31baa55 | ||
|
|
1312d08eda | ||
|
|
8c80c3ca49 | ||
|
|
7688fbb067 | ||
|
|
5b2e39e3b5 | ||
|
|
b5bf0b1d05 | ||
|
|
ff85acedb9 | ||
|
|
337685a38a | ||
|
|
127776288b | ||
|
|
1d90eac631 | ||
|
|
92b59ae4f6 | ||
|
|
aa3e7848c2 | ||
|
|
e0a266780b | ||
|
|
2299c3c9ec | ||
|
|
2cf3ac9461 | ||
|
|
384c78ae13 | ||
|
|
a6a2833c68 | ||
|
|
b04ecaefd7 | ||
|
|
e575736cae | ||
|
|
8eec1e4cfb | ||
|
|
9659469998 | ||
|
|
efcf94298a | ||
|
|
f75e36dcdb | ||
|
|
aa647f3cd6 | ||
|
|
77302b6572 | ||
|
|
29cc75531a | ||
|
|
a08d182359 | ||
|
|
ef77434a49 | ||
|
|
aa54390c85 | ||
|
|
579d0ad15d | ||
|
|
e212890dfc | ||
|
|
1aa16f1e3c | ||
|
|
cc80f7943d | ||
|
|
c55bf5172d | ||
|
|
3f75f08e1d | ||
|
|
eda467e066 | ||
|
|
ab5b8ffed1 | ||
|
|
7f6f4c889d | ||
|
|
294360518a | ||
|
|
c7acb7e481 | ||
|
|
9105c01757 | ||
|
|
d0c4d19270 | ||
|
|
60ea92fdb0 | ||
|
|
2b4f372317 | ||
|
|
10bda559f9 | ||
|
|
33aaabaed7 | ||
|
|
dc1a70fa43 | ||
|
|
ca73223d4c | ||
|
|
1f85fd2db8 | ||
|
|
e681855428 | ||
|
|
9096eb168d | ||
|
|
575b4786f9 | ||
|
|
ddf5512c9a | ||
|
|
eabde77d84 | ||
|
|
5df7770977 | ||
|
|
0e49576fe4 | ||
|
|
33afaa061e | ||
|
|
1db53b39c4 | ||
|
|
836d7b703a | ||
|
|
732471fddf | ||
|
|
02cbe4969f | ||
|
|
e602bdc80c | ||
|
|
529fb5c67f | ||
|
|
edcad9a14b | ||
|
|
cd100b8832 | ||
|
|
c96dfa54a4 | ||
|
|
898eec78cd | ||
|
|
65b37b40ff | ||
|
|
af51e3e4b1 | ||
|
|
9c7cb147b9 | ||
|
|
9576e48e1a | ||
|
|
77b9e510fc | ||
|
|
cdb18f48cd | ||
|
|
208ff3e2b3 | ||
|
|
ef603cf37d | ||
|
|
8cc4505bb1 | ||
|
|
70917fac9f | ||
|
|
132c608ebc | ||
|
|
d005a306f9 | ||
|
|
80349ac77b | ||
|
|
6e2e1a4f89 | ||
|
|
afab374305 | ||
|
|
bc1d30de38 | ||
|
|
14d647f219 | ||
|
|
daf7a579ed | ||
|
|
9f48af3edd | ||
|
|
63cf1052f4 | ||
|
|
0fd516a1df | ||
|
|
34d944c4a9 | ||
|
|
7f4f6b3457 | ||
|
|
43e8288e3f | ||
|
|
d26d7973ad | ||
|
|
1143b4766c | ||
|
|
af4c693030 | ||
|
|
92775557d9 | ||
|
|
29fc6a46a8 | ||
|
|
f634bfe0fc | ||
|
|
2b80f801f6 | ||
|
|
18a9a694b3 | ||
|
|
05153d66b1 | ||
|
|
ae5fe802ce | ||
|
|
1e9864363f | ||
|
|
1d5110e140 | ||
|
|
670158345a | ||
|
|
96fcc94acb |
24
.github/workflows/awaiting-mathlib.yml
vendored
24
.github/workflows/awaiting-mathlib.yml
vendored
@@ -10,11 +10,29 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check awaiting-mathlib label
|
||||
id: check-awaiting-mathlib-label
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { labels } = context.payload.pull_request;
|
||||
if (labels.some(label => label.name == "awaiting-mathlib") && !labels.some(label => label.name == "builds-mathlib")) {
|
||||
core.setFailed('PR is marked "awaiting-mathlib" but "builds-mathlib" label has not been applied yet by the bot');
|
||||
const { labels, number: prNumber } = context.payload.pull_request;
|
||||
const hasAwaiting = labels.some(label => label.name == "awaiting-mathlib");
|
||||
const hasBreaks = labels.some(label => label.name == "breaks-mathlib");
|
||||
const hasBuilds = labels.some(label => label.name == "builds-mathlib");
|
||||
|
||||
if (hasAwaiting && hasBreaks) {
|
||||
core.setFailed('PR has both "awaiting-mathlib" and "breaks-mathlib" labels.');
|
||||
} else if (hasAwaiting && !hasBreaks && !hasBuilds) {
|
||||
core.info('PR is marked "awaiting-mathlib" but neither "breaks-mathlib" nor "builds-mathlib" labels are present.');
|
||||
core.setOutput('awaiting', 'true');
|
||||
}
|
||||
|
||||
- name: Wait for mathlib compatibility
|
||||
if: github.event_name == 'pull_request' && steps.check-awaiting-mathlib-label.outputs.awaiting == 'true'
|
||||
run: |
|
||||
echo "::notice title=Awaiting mathlib::PR is marked 'awaiting-mathlib' but neither 'breaks-mathlib' nor 'builds-mathlib' labels are present."
|
||||
echo "This check will remain in progress until the PR is updated with appropriate mathlib compatibility labels."
|
||||
# Keep the job running indefinitely to show "in progress" status
|
||||
while true; do
|
||||
sleep 3600 # Sleep for 1 hour at a time
|
||||
done
|
||||
|
||||
2
.github/workflows/build-template.yml
vendored
2
.github/workflows/build-template.yml
vendored
@@ -193,7 +193,7 @@ jobs:
|
||||
run: |
|
||||
ulimit -c unlimited # coredumps
|
||||
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }}
|
||||
if: (matrix.wasm || !matrix.cross) && inputs.check-level >= 1
|
||||
if: (matrix.wasm || !matrix.cross) && (inputs.check-level >= 1 || matrix.name == 'Linux release')
|
||||
- name: Test Summary
|
||||
uses: test-summary/action@v2
|
||||
with:
|
||||
|
||||
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@@ -174,8 +174,9 @@ jobs:
|
||||
// just a secondary build job for now until false positives can be excluded
|
||||
"secondary": true,
|
||||
"CMAKE_OPTIONS": "-DUSE_LAKE=ON",
|
||||
// TODO: why does this fail?
|
||||
"CTEST_OPTIONS": "-E 'scopedMacros'"
|
||||
// TODO: importStructure is not compatible with .olean caching
|
||||
// TODO: why does scopedMacros fail?
|
||||
"CTEST_OPTIONS": "-E 'scopedMacros|importStructure'"
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
|
||||
112
.github/workflows/nix-ci.yml
vendored
112
.github/workflows/nix-ci.yml
vendored
@@ -1,112 +0,0 @@
|
||||
name: Nix CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# see ci.yml
|
||||
configure:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.result }}
|
||||
steps:
|
||||
- name: Configure build matrix
|
||||
id: set-matrix
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
let large = ${{ github.repository == 'leanprover/lean4' }};
|
||||
let matrix = [
|
||||
{
|
||||
"name": "Nix Linux",
|
||||
"os": large ? "nscloud-ubuntu-22.04-amd64-8x8" : "ubuntu-latest",
|
||||
}
|
||||
];
|
||||
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
|
||||
return matrix;
|
||||
|
||||
Build:
|
||||
needs: [configure]
|
||||
runs-on: ${{ matrix.os }}
|
||||
defaults:
|
||||
run:
|
||||
shell: nix run .#ciShell -- bash -euxo pipefail {0}
|
||||
strategy:
|
||||
matrix:
|
||||
include: ${{fromJson(needs.configure.outputs.matrix)}}
|
||||
# complete all jobs
|
||||
fail-fast: false
|
||||
name: ${{ matrix.name }}
|
||||
env:
|
||||
NIX_BUILD_ARGS: --print-build-logs --fallback
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# the default is to use a virtual merge commit between the PR and master: just use the PR
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Set Up Nix Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: nix-store-cache
|
||||
key: ${{ matrix.name }}-nix-store-cache-${{ github.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
${{ matrix.name }}-nix-store-cache
|
||||
save-always: true
|
||||
- name: Further Set Up Nix Cache
|
||||
shell: bash -euxo pipefail {0}
|
||||
run: |
|
||||
# Nix seems to mutate the cache, so make a copy
|
||||
cp -r nix-store-cache nix-store-cache-copy || true
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
extra-conf: |
|
||||
extra-sandbox-paths = /nix/var/cache/ccache?
|
||||
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
|
||||
- name: Prepare CCache Cache
|
||||
run: |
|
||||
sudo mkdir -m0770 -p /nix/var/cache/ccache
|
||||
sudo chown -R $USER /nix/var/cache/ccache
|
||||
- name: Setup CCache Cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /nix/var/cache/ccache
|
||||
key: ${{ matrix.name }}-nix-ccache-${{ github.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
${{ matrix.name }}-nix-ccache
|
||||
save-always: true
|
||||
- name: Further Set Up CCache Cache
|
||||
run: |
|
||||
sudo chown -R root:nixbld /nix/var/cache
|
||||
sudo chmod -R 770 /nix/var/cache
|
||||
- name: Build
|
||||
run: |
|
||||
nix build $NIX_BUILD_ARGS .#cacheRoots -o push-build
|
||||
- name: Test
|
||||
run: |
|
||||
nix build --keep-failed $NIX_BUILD_ARGS .#test -o push-test || (ln -s /tmp/nix-build-*/build/source/src/build ./push-test; false)
|
||||
- name: Test Summary
|
||||
uses: test-summary/action@v2
|
||||
with:
|
||||
paths: push-test/test-results.xml
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
- name: Rebuild Nix Store Cache
|
||||
run: |
|
||||
rm -rf nix-store-cache || true
|
||||
nix copy ./push-* --to file://$PWD/nix-store-cache?compression=none
|
||||
- name: Fixup CCache Cache
|
||||
run: |
|
||||
sudo chown -R $USER /nix/var/cache
|
||||
38
.github/workflows/update-stage0.yml
vendored
38
.github/workflows/update-stage0.yml
vendored
@@ -40,34 +40,24 @@ jobs:
|
||||
run: |
|
||||
git config --global user.name "Lean stage0 autoupdater"
|
||||
git config --global user.email "<>"
|
||||
# Would be nice, but does not work yet:
|
||||
# https://github.com/DeterminateSystems/magic-nix-cache/issues/39
|
||||
# This action does not run that often and building runs in a few minutes, so ok for now
|
||||
#- if: env.should_update_stage0 == 'yes'
|
||||
# uses: DeterminateSystems/magic-nix-cache-action@v2
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
name: Restore Build Cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: nix-store-cache
|
||||
key: Nix Linux-nix-store-cache-${{ github.sha }}
|
||||
# fall back to (latest) previous cache
|
||||
restore-keys: |
|
||||
Nix Linux-nix-store-cache
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
name: Further Set Up Nix Cache
|
||||
shell: bash -euxo pipefail {0}
|
||||
run: |
|
||||
# Nix seems to mutate the cache, so make a copy
|
||||
cp -r nix-store-cache nix-store-cache-copy || true
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
extra-conf: |
|
||||
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
|
||||
- name: Open Nix shell once
|
||||
if: env.should_update_stage0 == 'yes'
|
||||
run: true
|
||||
shell: 'nix develop -c bash -euxo pipefail {0}'
|
||||
- name: Set up NPROC
|
||||
if: env.should_update_stage0 == 'yes'
|
||||
run: |
|
||||
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV
|
||||
shell: 'nix develop -c bash -euxo pipefail {0}'
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: nix run .#update-stage0-commit
|
||||
run: cmake --preset release
|
||||
shell: 'nix develop -c bash -euxo pipefail {0}'
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: make -j$NPROC -C build/release update-stage0-commit
|
||||
shell: 'nix develop -c bash -euxo pipefail {0}'
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: git show --stat
|
||||
- if: env.should_update_stage0 == 'yes' && github.event_name == 'push'
|
||||
|
||||
@@ -5,7 +5,7 @@ option(USE_MIMALLOC "use mimalloc" ON)
|
||||
# store all variables passed on the command line into CL_ARGS so we can pass them to the stage builds
|
||||
# https://stackoverflow.com/a/48555098/161659
|
||||
# MUST be done before call to 'project'
|
||||
# Use standard release build (discarding LEAN_CXX_EXTRA_FLAGS etc.) for stage0 by default since it is assumed to be "good", but still pass through CMake platform arguments (compiler, toolchain file, ..).
|
||||
# Use standard release build (discarding LEAN_EXTRA_CXX_FLAGS etc.) for stage0 by default since it is assumed to be "good", but still pass through CMake platform arguments (compiler, toolchain file, ..).
|
||||
# Use `STAGE0_` prefix to pass variables to stage0 explicitly.
|
||||
get_cmake_property(vars CACHE_VARIABLES)
|
||||
foreach(var ${vars})
|
||||
@@ -39,10 +39,14 @@ endif()
|
||||
|
||||
# Don't do anything with cadical on wasm
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
# On CI Linux, we source cadical from Nix instead; see flake.nix
|
||||
find_program(CADICAL cadical)
|
||||
if(NOT CADICAL)
|
||||
set(CADICAL_CXX c++)
|
||||
if (CADICAL_USE_CUSTOM_CXX)
|
||||
set(CADICAL_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(CADICAL_CXXFLAGS "${LEAN_EXTRA_CXX_FLAGS}")
|
||||
set(CADICAL_LDFLAGS "-Wl,-rpath=\\$$ORIGIN/../lib")
|
||||
endif()
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
set(CADICAL_CXX "${CCACHE} ${CADICAL_CXX}")
|
||||
@@ -57,8 +61,11 @@ if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
GIT_REPOSITORY https://github.com/arminbiere/cadical
|
||||
GIT_TAG rel-2.1.2
|
||||
CONFIGURE_COMMAND ""
|
||||
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
|
||||
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
|
||||
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk
|
||||
CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX}
|
||||
CXX=${CADICAL_CXX}
|
||||
CXXFLAGS=${CADICAL_CXXFLAGS}
|
||||
LDFLAGS=${CADICAL_LDFLAGS}
|
||||
BUILD_IN_SOURCE ON
|
||||
INSTALL_COMMAND "")
|
||||
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
/.github/ @kim-em
|
||||
/RELEASES.md @kim-em
|
||||
/src/kernel/ @leodemoura
|
||||
/src/library/compiler/ @zwarich
|
||||
/src/lake/ @tydeu
|
||||
/src/Lean/Compiler/ @leodemoura
|
||||
/src/Lean/Compiler/ @leodemoura @zwarich
|
||||
/src/Lean/Data/Lsp/ @mhuisi
|
||||
/src/Lean/Elab/Deriving/ @kim-em
|
||||
/src/Lean/Elab/Tactic/ @kim-em
|
||||
|
||||
11
README.md
11
README.md
@@ -2,20 +2,19 @@ This is the repository for **Lean 4**.
|
||||
|
||||
# About
|
||||
|
||||
- [Quickstart](https://lean-lang.org/lean4/doc/quickstart.html)
|
||||
- [Quickstart](https://lean-lang.org/documentation/setup/)
|
||||
- [Homepage](https://lean-lang.org)
|
||||
- [Theorem Proving Tutorial](https://lean-lang.org/theorem_proving_in_lean4/)
|
||||
- [Functional Programming in Lean](https://lean-lang.org/functional_programming_in_lean/)
|
||||
- [Documentation Overview](https://lean-lang.org/lean4/doc/)
|
||||
- [Documentation Overview](https://lean-lang.org/documentation/)
|
||||
- [Language Reference](https://lean-lang.org/doc/reference/latest/)
|
||||
- [Release notes](RELEASES.md) starting at v4.0.0-m3
|
||||
- [Examples](https://lean-lang.org/lean4/doc/examples.html)
|
||||
- [Examples](https://lean-lang.org/documentation/examples/)
|
||||
- [External Contribution Guidelines](CONTRIBUTING.md)
|
||||
- [FAQ](https://lean-lang.org/lean4/doc/faq.html)
|
||||
|
||||
# Installation
|
||||
|
||||
See [Setting Up Lean](https://lean-lang.org/lean4/doc/setup.html).
|
||||
See [Setting Up Lean](https://lean-lang.org/documentation/setup/).
|
||||
|
||||
# Contributing
|
||||
|
||||
@@ -23,4 +22,4 @@ Please read our [Contribution Guidelines](CONTRIBUTING.md) first.
|
||||
|
||||
# Building from Source
|
||||
|
||||
See [Building Lean](https://lean-lang.org/lean4/doc/make/index.html) (documentation source: [doc/make/index.md](doc/make/index.md)).
|
||||
See [Building Lean](doc/make/index.md).
|
||||
|
||||
10
doc/README.md
Normal file
10
doc/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Developer Documentation and Examples
|
||||
|
||||
This directory contains documentation that describes how to work on
|
||||
Lean itself, as well as examples that are included in documentation
|
||||
that's hosted on the Lean website. The `make` directory contains
|
||||
information on building Lean, and the `dev` directory describes how to
|
||||
work on Lean.
|
||||
|
||||
The [documentation section](https://lean-lang.org/documentation) has
|
||||
links to documentation that describes how to use Lean itself.
|
||||
@@ -1,46 +0,0 @@
|
||||
# Summary
|
||||
|
||||
- [What is Lean](./whatIsLean.md)
|
||||
- [Tour of Lean](./tour.md)
|
||||
- [Setting Up Lean](./quickstart.md)
|
||||
- [Extended Setup Notes](./setup.md)
|
||||
- [Theorem Proving in Lean](./tpil.md)
|
||||
- [Functional Programming in Lean](fplean.md)
|
||||
- [Examples](./examples.md)
|
||||
- [Palindromes](examples/palindromes.lean.md)
|
||||
- [Binary Search Trees](examples/bintree.lean.md)
|
||||
- [A Certified Type Checker](examples/tc.lean.md)
|
||||
- [The Well-Typed Interpreter](examples/interp.lean.md)
|
||||
- [Dependent de Bruijn Indices](examples/deBruijn.lean.md)
|
||||
- [Parametric Higher-Order Abstract Syntax](examples/phoas.lean.md)
|
||||
- [Syntax Examples](./syntax_examples.md)
|
||||
- [Balanced Parentheses](./syntax_example.md)
|
||||
- [Arithmetic DSL](./metaprogramming-arith.md)
|
||||
|
||||
# Language Manual
|
||||
|
||||
- [The Lean Reference Manual](./reference.md)
|
||||
|
||||
# Other
|
||||
|
||||
- [Frequently Asked Questions](./faq.md)
|
||||
- [Significant Changes from Lean 3](./lean3changes.md)
|
||||
- [Syntax Highlighting Lean in LaTeX](./syntax_highlight_in_latex.md)
|
||||
- [User Widgets](examples/widgets.lean.md)
|
||||
- [Semantic Highlighting](./semantic_highlighting.md)
|
||||
|
||||
# Development
|
||||
|
||||
- [Development Guide](./dev/index.md)
|
||||
- [Building Lean](./make/index.md)
|
||||
- [Ubuntu Setup](./make/ubuntu.md)
|
||||
- [macOS Setup](./make/osx-10.9.md)
|
||||
- [Windows MSYS2 Setup](./make/msys2.md)
|
||||
- [Windows with WSL](./make/wsl.md)
|
||||
- [Bootstrapping](./dev/bootstrap.md)
|
||||
- [Testing](./dev/testing.md)
|
||||
- [Debugging](./dev/debugging.md)
|
||||
- [Commit Convention](./dev/commit_convention.md)
|
||||
- [Release checklist](./dev/release_checklist.md)
|
||||
- [Building This Manual](./dev/mdbook.md)
|
||||
- [Foreign Function Interface](./dev/ffi.md)
|
||||
@@ -1,786 +0,0 @@
|
||||
@charset "UTF-8";
|
||||
/*
|
||||
Copyright © 2019 Clément Pit-Claudel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*******************************/
|
||||
/* CSS reset for .alectryon-io */
|
||||
/*******************************/
|
||||
|
||||
.content {
|
||||
/*
|
||||
Use `initial` instead of `contents` to avoid a browser bug which removes
|
||||
the element from the accessibility tree.
|
||||
https://developer.mozilla.org/en-US/docs/Web/CSS/display#display_contents
|
||||
*/
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.alectryon-io blockquote {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.alectryon-io blockquote:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alectryon-io label {
|
||||
display: inline;
|
||||
font-size: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.alectryon-io a {
|
||||
text-decoration: none !important;
|
||||
font-style: oblique !important;
|
||||
color: unset;
|
||||
}
|
||||
|
||||
/* Undo <small> and <blockquote>, added to improve RSS rendering. */
|
||||
|
||||
.alectryon-io small.alectryon-output,
|
||||
.alectryon-io small.alectryon-type-info {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.alectryon-io blockquote.alectryon-goal,
|
||||
.alectryon-io blockquote.alectryon-message {
|
||||
font-weight: normal;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
/***************/
|
||||
/* Main styles */
|
||||
/***************/
|
||||
|
||||
.alectryon-coqdoc .doc .code,
|
||||
.alectryon-coqdoc .doc .comment,
|
||||
.alectryon-coqdoc .doc .inlinecode,
|
||||
.alectryon-mref,
|
||||
.alectryon-block, .alectryon-io,
|
||||
.alectryon-toggle-label, .alectryon-banner {
|
||||
font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important;
|
||||
font-size: 0.875em;
|
||||
font-feature-settings: "COQX" 1 /* Coq ligatures */, "XV00" 1 /* Legacy */, "calt" 1 /* Fallback */;
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.alectryon-io, .alectryon-block, .alectryon-toggle-label, .alectryon-banner {
|
||||
overflow: visible;
|
||||
overflow-wrap: break-word;
|
||||
position: relative;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/*
|
||||
CoqIDE doesn't turn off the unicode bidirectional algorithm (and PG simply
|
||||
respects the user's `bidi-display-reordering` setting), so don't turn it off
|
||||
here either. But beware unexpected results like `Definition test_אב := 0.`
|
||||
|
||||
.alectryon-io span {
|
||||
direction: ltr;
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
|
||||
In any case, make an exception for comments:
|
||||
|
||||
.highlight .c {
|
||||
direction: embed;
|
||||
unicode-bidi: initial;
|
||||
}
|
||||
*/
|
||||
|
||||
.alectryon-mref,
|
||||
.alectryon-mref-marker {
|
||||
align-self: center;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
font-size: 80%;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
box-shadow: 0 0 0 1pt black;
|
||||
padding: 1pt 0.3em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.alectryon-block .alectryon-mref-marker,
|
||||
.alectryon-io .alectryon-mref-marker {
|
||||
user-select: none;
|
||||
margin: -0.25em 0 -0.25em 0.5em;
|
||||
}
|
||||
|
||||
.alectryon-inline .alectryon-mref-marker {
|
||||
margin: -0.25em 0.15em -0.25em 0.625em; /* 625 = 0.5em / 80% */
|
||||
}
|
||||
|
||||
.alectryon-mref {
|
||||
color: inherit;
|
||||
margin: -0.5em 0.25em;
|
||||
}
|
||||
|
||||
.alectryon-goal:target .goal-separator .alectryon-mref-marker,
|
||||
:target > .alectryon-mref-marker {
|
||||
animation: blink 0.2s step-start 0s 3 normal none;
|
||||
background-color: #fcaf3e;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
50% {
|
||||
box-shadow: 0 0 0 3pt #fcaf3e, 0 0 0 4pt black;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
.alectryon-toggle,
|
||||
.alectryon-io .alectryon-extra-goal-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alectryon-bubble,
|
||||
.alectryon-io label,
|
||||
.alectryon-toggle-label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.alectryon-toggle-label {
|
||||
display: block;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-input {
|
||||
padding: 0.1em 0; /* Enlarge the hitbox slightly to fill interline gaps */
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-token {
|
||||
white-space: pre-wrap;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-sentence.alectryon-target .alectryon-input {
|
||||
/* FIXME if keywords were ‘bolder’ we wouldn't need !important */
|
||||
font-weight: bold !important; /* Use !important to avoid a * selector */
|
||||
}
|
||||
|
||||
.alectryon-bubble:before,
|
||||
.alectryon-toggle-label:before,
|
||||
.alectryon-io label.alectryon-input:after,
|
||||
.alectryon-io .alectryon-goal > label:before {
|
||||
border: 1px solid #babdb6;
|
||||
border-radius: 1em;
|
||||
box-sizing: border-box;
|
||||
content: '';
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
height: 0.25em;
|
||||
margin-bottom: 0.15em;
|
||||
vertical-align: middle;
|
||||
width: 0.75em;
|
||||
}
|
||||
|
||||
.alectryon-toggle-label:before,
|
||||
.alectryon-io .alectryon-goal > label:before {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-goal > label:before {
|
||||
margin-top: 0.125em;
|
||||
}
|
||||
|
||||
.alectryon-io label.alectryon-input {
|
||||
padding-right: 1em; /* Prevent line wraps before the checkbox bubble */
|
||||
}
|
||||
|
||||
.alectryon-io label.alectryon-input:after {
|
||||
margin-left: 0.25em;
|
||||
margin-right: -1em; /* Compensate for the anti-wrapping space */
|
||||
}
|
||||
|
||||
.alectryon-failed {
|
||||
/* Underlines are broken in Chrome (they reset at each element boundary)… */
|
||||
/* text-decoration: red wavy underline; */
|
||||
/* … but it isn't too noticeable with dots */
|
||||
text-decoration: red dotted underline;
|
||||
text-decoration-skip-ink: none;
|
||||
/* Chrome prints background images in low resolution, yielding a blurry underline */
|
||||
/* background: bottom / 0.3em auto repeat-x url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyLjY0NiAxLjg1MiIgaGVpZ2h0PSI4IiB3aWR0aD0iMTAiPjxwYXRoIGQ9Ik0wIC4yNjVjLjc5NCAwIC41MyAxLjMyMiAxLjMyMyAxLjMyMi43OTQgMCAuNTMtMS4zMjIgMS4zMjMtMS4zMjIiIGZpbGw9Im5vbmUiIHN0cm9rZT0icmVkIiBzdHJva2Utd2lkdGg9Ii41MjkiLz48L3N2Zz4=); */
|
||||
}
|
||||
|
||||
/* Wrapping :hover rules in a media query ensures that tapping a Coq sentence
|
||||
doesn't trigger its :hover state (otherwise, on mobile, tapping a sentence to
|
||||
hide its output causes it to remain visible (its :hover state gets triggered.
|
||||
We only do it for the default style though, since other styles don't put the
|
||||
output over the main text, so showing too much is not an issue. */
|
||||
@media (any-hover: hover) {
|
||||
.alectryon-bubble:hover:before,
|
||||
.alectryon-toggle-label:hover:before,
|
||||
.alectryon-io label.alectryon-input:hover:after {
|
||||
background: #eeeeec;
|
||||
}
|
||||
|
||||
.alectryon-io label.alectryon-input:hover {
|
||||
text-decoration: underline dotted #babdb6;
|
||||
text-shadow: 0 0 1px rgb(46, 52, 54, 0.3); /* #2e3436 + opacity */
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-sentence:hover .alectryon-output,
|
||||
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper,
|
||||
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper {
|
||||
z-index: 2; /* Place hovered goals above .alectryon-sentence.alectryon-target ones */
|
||||
}
|
||||
}
|
||||
|
||||
.alectryon-toggle:checked + .alectryon-toggle-label:before,
|
||||
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked + label.alectryon-input:after,
|
||||
.alectryon-io .alectryon-extra-goal-toggle:checked + .alectryon-goal > label:before {
|
||||
background-color: #babdb6;
|
||||
border-color: #babdb6;
|
||||
}
|
||||
|
||||
/* Disable clicks on sentences when the document-wide toggle is set. */
|
||||
.alectryon-toggle:checked + label + .alectryon-container label.alectryon-input {
|
||||
cursor: unset;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Hide individual checkboxes when the document-wide toggle is set. */
|
||||
.alectryon-toggle:checked + label + .alectryon-container label.alectryon-input:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* .alectryon-output is displayed by toggles, :hover, and .alectryon-target rules */
|
||||
.alectryon-io .alectryon-output {
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
padding: 0.25em 0;
|
||||
overflow: visible; /* Let box-shadows overflow */
|
||||
z-index: 1; /* Default to an index lower than that used by :hover */
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-type-info-wrapper {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-type-info-wrapper.full-width {
|
||||
left: 0;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-type-info .goal-separator {
|
||||
height: unset;
|
||||
margin-top: 0em;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-type-info-wrapper .alectryon-type-info {
|
||||
box-sizing: border-box;
|
||||
bottom: 100%;
|
||||
position: absolute;
|
||||
/*padding: 0.25em 0;*/
|
||||
visibility: hidden;
|
||||
overflow: visible; /* Let box-shadows overflow */
|
||||
z-index: 1; /* Default to an index lower than that used by :hover */
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-type-info-wrapper .alectryon-type-info .alectryon-goal.alectryon-docstring {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
|
||||
@media (any-hover: hover) { /* See note above about this @media query */
|
||||
.alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.alectryon-io.output-hidden .alectryon-sentence:hover .alectryon-output:not(:hover) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.alectryon-io.type-info-hidden .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info,
|
||||
.alectryon-io.type-info-hidden .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info {
|
||||
/*visibility: hidden !important;*/
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info,
|
||||
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info {
|
||||
visibility: visible;
|
||||
transition-delay: 0.5s;
|
||||
}
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-sentence.alectryon-target .alectryon-output {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Indicate active (hovered or targeted) goals with a shadow. */
|
||||
.alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) .alectryon-messages,
|
||||
.alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .alectryon-messages,
|
||||
.alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) .alectryon-goals,
|
||||
.alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .alectryon-goals,
|
||||
.alectryon-io .alectryon-token:hover .alectryon-type-info-wrapper .alectryon-type-info {
|
||||
box-shadow: 2px 2px 2px gray;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-extra-goals .alectryon-goal .goal-hyps {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-extra-goals .alectryon-extra-goal-toggle:not(:checked) + .alectryon-goal label.goal-separator hr {
|
||||
/* Dashes indicate that the hypotheses are hidden */
|
||||
border-top-style: dashed;
|
||||
}
|
||||
|
||||
|
||||
/* Show just a small preview of the other goals; this is undone by the
|
||||
"extra-goal" toggle and by :hover and .alectryon-target in windowed mode. */
|
||||
.alectryon-io .alectryon-extra-goals .alectryon-goal .goal-conclusion {
|
||||
max-height: 5.2em;
|
||||
overflow-y: auto;
|
||||
/* Combining ‘overflow-y: auto’ with ‘display: inline-block’ causes extra space
|
||||
to be added below the box. ‘vertical-align: middle’ gets rid of it. */
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-goals,
|
||||
.alectryon-io .alectryon-messages {
|
||||
background: #f6f7f6;
|
||||
/*border: thin solid #d3d7cf; /* Convenient when pre's background is already #EEE */
|
||||
display: block;
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
.alectryon-message::before {
|
||||
content: '';
|
||||
float: right;
|
||||
/* etc/svg/square-bubble-xl.svg */
|
||||
background: url("data:image/svg+xml,%3Csvg width='14' height='14' viewBox='0 0 3.704 3.704' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill-rule='evenodd' stroke='%23000' stroke-width='.264'%3E%3Cpath d='M.794.934h2.115M.794 1.463h1.455M.794 1.992h1.852'/%3E%3C/g%3E%3Cpath d='M.132.14v2.646h.794v.661l.926-.661h1.72V.14z' fill='none' stroke='%23000' stroke-width='.265'/%3E%3C/svg%3E") top right no-repeat;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.alectryon-toggle:checked + label + .alectryon-container {
|
||||
width: unset;
|
||||
}
|
||||
|
||||
/* Show goals when a toggle is set */
|
||||
.alectryon-toggle:checked + label + .alectryon-container label.alectryon-input + .alectryon-output,
|
||||
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-output {
|
||||
display: block;
|
||||
position: static;
|
||||
width: unset;
|
||||
background: unset; /* Override the backgrounds set in floating in windowed mode */
|
||||
padding: 0.25em 0; /* Re-assert so that later :hover rules don't override this padding */
|
||||
}
|
||||
|
||||
.alectryon-toggle:checked + label + .alectryon-container label.alectryon-input + .alectryon-output .goal-hyps,
|
||||
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-output .goal-hyps {
|
||||
/* Overridden back in windowed style */
|
||||
flex-flow: row wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.alectryon-toggle:checked + label + .alectryon-container .alectryon-sentence .alectryon-output > div,
|
||||
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-output > div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-extra-goal-toggle:checked + .alectryon-goal .goal-hyps {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-extra-goal-toggle:checked + .alectryon-goal .goal-conclusion {
|
||||
max-height: unset;
|
||||
overflow-y: unset;
|
||||
}
|
||||
|
||||
.alectryon-toggle:checked + label + .alectryon-container .alectryon-sentence > .alectryon-toggle ~ .alectryon-wsp,
|
||||
.alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-wsp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-messages,
|
||||
.alectryon-io .alectryon-message,
|
||||
.alectryon-io .alectryon-goals,
|
||||
.alectryon-io .alectryon-goal,
|
||||
.alectryon-io .goal-hyps > span,
|
||||
.alectryon-io .goal-conclusion {
|
||||
border-radius: 0.15em;
|
||||
}
|
||||
|
||||
.alectryon-io .alectryon-goal,
|
||||
.alectryon-io .alectryon-message {
|
||||
align-items: center;
|
||||
background: #f6f7f6;
|
||||
border: 0em;
|
||||
display: block;
|
||||
flex-direction: column;
|
||||
margin: 0.25em;
|
||||
padding: 0.5em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.alectryon-io .goal-hyps {
|
||||
align-content: space-around;
|
||||
align-items: baseline;
|
||||
display: flex;
|
||||
flex-flow: column nowrap; /* re-stated in windowed mode */
|
||||
justify-content: space-around;
|
||||
/* LATER use a ‘gap’ property instead of margins once supported */
|
||||
margin: -0.15em -0.25em; /* -0.15em to cancel the item spacing */
|
||||
padding-bottom: 0.35em; /* 0.5em-0.15em to cancel the 0.5em of .goal-separator */
|
||||
}
|
||||
|
||||
.alectryon-io .goal-hyps > br {
|
||||
display: none; /* Only for RSS readers */
|
||||
}
|
||||
|
||||
.alectryon-io .goal-hyps > span,
|
||||
.alectryon-io .goal-conclusion {
|
||||
/*background: #eeeeec;*/
|
||||
display: inline-block;
|
||||
padding: 0.15em 0.35em;
|
||||
}
|
||||
|
||||
.alectryon-io .goal-hyps > span {
|
||||
align-items: baseline;
|
||||
display: inline-flex;
|
||||
margin: 0.15em 0.25em;
|
||||
}
|
||||
|
||||
.alectryon-block var,
|
||||
.alectryon-inline var,
|
||||
.alectryon-io .goal-hyps > span > var {
|
||||
font-weight: 600;
|
||||
font-style: unset;
|
||||
}
|
||||
|
||||
.alectryon-io .goal-hyps > span > var {
|
||||
/* Shrink the list of names, but let it grow as long as space is available. */
|
||||
flex-basis: min-content;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.alectryon-io .goal-hyps > span b {
|
||||
font-weight: 600;
|
||||
margin: 0 0 0 0.5em;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.alectryon-io .hyp-body,
|
||||
.alectryon-io .hyp-type {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.alectryon-io .goal-separator {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 1em; /* Fixed height to ignore goal name and markers */
|
||||
margin-top: -0.5em; /* Compensated in .goal-hyps when shown */
|
||||
}
|
||||
|
||||
.alectryon-io .goal-separator hr {
|
||||
border: none;
|
||||
border-top: thin solid #555753;
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.alectryon-io .goal-separator .goal-name {
|
||||
font-size: 0.75em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
/**********/
|
||||
/* Banner */
|
||||
/**********/
|
||||
|
||||
.alectryon-banner {
|
||||
background: #eeeeec;
|
||||
border: 1px solid #babcbd;
|
||||
font-size: 0.75em;
|
||||
padding: 0.25em;
|
||||
text-align: center;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.alectryon-banner a {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.alectryon-banner kbd {
|
||||
background: #d3d7cf;
|
||||
border-radius: 0.15em;
|
||||
border: 1px solid #babdb6;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
font-family: inherit;
|
||||
font-size: 0.9em;
|
||||
height: 1.3em;
|
||||
line-height: 1.2em;
|
||||
margin: -0.25em 0;
|
||||
padding: 0 0.25em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/**********/
|
||||
/* Toggle */
|
||||
/**********/
|
||||
|
||||
.alectryon-toggle-label {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
/******************/
|
||||
/* Floating style */
|
||||
/******************/
|
||||
|
||||
/* If there's space, display goals to the right of the code, not below it. */
|
||||
@media (min-width: 80rem) {
|
||||
/* Unlike the windowed case, we don't want to move output blocks to the side
|
||||
when they are both :checked and -targeted, since it gets confusing as
|
||||
things jump around; hence the commented-output part of the selector,
|
||||
which would otherwise increase specificity */
|
||||
.alectryon-floating .alectryon-sentence.alectryon-target /* > .alectryon-toggle ~ */ .alectryon-output,
|
||||
.alectryon-floating .alectryon-sentence:hover .alectryon-output {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
right: -100%;
|
||||
padding: 0 0.5em;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.alectryon-floating .alectryon-output {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.alectryon-floating .alectryon-sentence:hover .alectryon-output {
|
||||
background: white; /* Ensure that short goals hide long ones */
|
||||
}
|
||||
|
||||
/* This odd margin-bottom property prevents the sticky div from bumping
|
||||
against the bottom of its container (.alectryon-output). The alternative
|
||||
would be enlarging .alectryon-output, but that would cause overflows,
|
||||
enlarging scrollbars and yielding scrolling towards the bottom of the
|
||||
page. Doing things this way instead makes it possible to restrict
|
||||
.alectryon-output to a reasonable size (100%, through top = bottom = 0).
|
||||
See also https://stackoverflow.com/questions/43909940/. */
|
||||
/* See note on specificity above */
|
||||
.alectryon-floating .alectryon-sentence.alectryon-target /* > .alectryon-toggle ~ */ .alectryon-output > div,
|
||||
.alectryon-floating .alectryon-sentence:hover .alectryon-output > div {
|
||||
margin-bottom: -200%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.alectryon-floating .alectryon-toggle:checked + label + .alectryon-container .alectryon-sentence .alectryon-output > div,
|
||||
.alectryon-floating .alectryon-io .alectryon-sentence > .alectryon-toggle:checked ~ .alectryon-output > div {
|
||||
margin-bottom: unset; /* Undo the margin */
|
||||
}
|
||||
|
||||
/* Float underneath the current fragment
|
||||
@media (max-width: 80rem) {
|
||||
.alectryon-floating .alectryon-output {
|
||||
top: 100%;
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
/********************/
|
||||
/* Multi-pane style */
|
||||
/********************/
|
||||
|
||||
.alectryon-windowed {
|
||||
border: 0 solid #2e3436;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.alectryon-windowed .alectryon-sentence:hover .alectryon-output {
|
||||
background: white; /* Ensure that short goals hide long ones */
|
||||
}
|
||||
|
||||
.alectryon-windowed .alectryon-output {
|
||||
position: fixed; /* Overwritten by the ‘:checked’ rules */
|
||||
}
|
||||
|
||||
/* See note about specificity below */
|
||||
.alectryon-windowed .alectryon-sentence:hover .alectryon-output,
|
||||
.alectryon-windowed .alectryon-sentence.alectryon-target > .alectryon-toggle ~ .alectryon-output {
|
||||
padding: 0.5em;
|
||||
overflow-y: auto; /* Windowed contents may need to scroll */
|
||||
}
|
||||
|
||||
.alectryon-windowed .alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) .alectryon-messages,
|
||||
.alectryon-windowed .alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .alectryon-messages,
|
||||
.alectryon-windowed .alectryon-io .alectryon-sentence:hover .alectryon-output:not(:hover) .alectryon-goals,
|
||||
.alectryon-windowed .alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .alectryon-goals {
|
||||
box-shadow: none; /* A shadow is unnecessary here and incompatible with overflow-y set to auto */
|
||||
}
|
||||
|
||||
.alectryon-windowed .alectryon-io .alectryon-sentence.alectryon-target .alectryon-output .goal-hyps {
|
||||
/* Restated to override the :checked style */
|
||||
flex-flow: column nowrap;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
|
||||
.alectryon-windowed .alectryon-sentence.alectryon-target .alectryon-extra-goals .alectryon-goal .goal-conclusion
|
||||
/* Like .alectryon-io .alectryon-extra-goal-toggle:checked + .alectryon-goal .goal-conclusion */ {
|
||||
max-height: unset;
|
||||
overflow-y: unset;
|
||||
}
|
||||
|
||||
.alectryon-windowed .alectryon-output > div {
|
||||
display: flex; /* Put messages after goals */
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
/*********************/
|
||||
/* Standalone styles */
|
||||
/*********************/
|
||||
|
||||
.alectryon-standalone {
|
||||
font-family: 'IBM Plex Serif', 'PT Serif', 'Merriweather', 'DejaVu Serif', serif;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 50rem) {
|
||||
html.alectryon-standalone {
|
||||
/* Prevent flickering when hovering a block causes scrollbars to appear. */
|
||||
margin-left: calc(100vw - 100%);
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Coqdoc */
|
||||
|
||||
.alectryon-coqdoc .doc .code,
|
||||
.alectryon-coqdoc .doc .inlinecode,
|
||||
.alectryon-coqdoc .doc .comment {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.alectryon-coqdoc .doc .comment {
|
||||
color: #eeeeec;
|
||||
}
|
||||
|
||||
.alectryon-coqdoc .doc .paragraph {
|
||||
height: 0.75em;
|
||||
}
|
||||
|
||||
/* Centered, Floating */
|
||||
|
||||
.alectryon-standalone .alectryon-centered,
|
||||
.alectryon-standalone .alectryon-floating {
|
||||
max-width: 50rem;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 80rem) {
|
||||
.alectryon-standalone .alectryon-floating {
|
||||
max-width: 80rem;
|
||||
}
|
||||
|
||||
.alectryon-standalone .alectryon-floating > * {
|
||||
width: 50%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Windowed */
|
||||
|
||||
.alectryon-standalone .alectryon-windowed {
|
||||
display: block;
|
||||
margin: 0;
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
.alectryon-standalone .alectryon-windowed > * {
|
||||
/* Override properties of docutils_basic.css */
|
||||
margin-left: 0;
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
.alectryon-standalone .alectryon-windowed .alectryon-io {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* No need to predicate the ‘:hover’ rules below on ‘:not(:checked)’, since ‘left’,
|
||||
‘right’, ‘top’, and ‘bottom’ will be inactived by the :checked rules setting
|
||||
‘position’ to ‘static’ */
|
||||
|
||||
|
||||
/* Specificity: We want the output to stay inline when hovered while unfolded
|
||||
(:checked), but we want it to move when it's targeted (i.e. when the user
|
||||
is browsing goals one by one using the keyboard, in which case we want to
|
||||
goals to appear in consistent locations). The selectors below ensure
|
||||
that :hover < :checked < -targeted in terms of specificity. */
|
||||
/* LATER: Reimplement this stuff with CSS variables */
|
||||
.alectryon-windowed .alectryon-sentence.alectryon-target > .alectryon-toggle ~ .alectryon-output {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 60rem) {
|
||||
.alectryon-standalone .alectryon-windowed {
|
||||
border-right-width: thin;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 50%;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.alectryon-standalone .alectryon-windowed .alectryon-sentence:hover .alectryon-output,
|
||||
.alectryon-standalone .alectryon-windowed .alectryon-sentence.alectryon-target .alectryon-output {
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 60rem) {
|
||||
.alectryon-standalone .alectryon-windowed {
|
||||
border-bottom-width: 1px;
|
||||
bottom: 40%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.alectryon-standalone .alectryon-windowed .alectryon-sentence:hover .alectryon-output,
|
||||
.alectryon-standalone .alectryon-windowed .alectryon-sentence.alectryon-target .alectryon-output {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 60%;
|
||||
}
|
||||
}
|
||||
190
doc/alectryon.js
190
doc/alectryon.js
@@ -1,190 +0,0 @@
|
||||
var Alectryon;
|
||||
(function(Alectryon) {
|
||||
(function (slideshow) {
|
||||
function anchor(sentence) { return "#" + sentence.id; }
|
||||
|
||||
function current_sentence() { return slideshow.sentences[slideshow.pos]; }
|
||||
|
||||
function unhighlight() {
|
||||
var sentence = current_sentence();
|
||||
if (sentence) sentence.classList.remove("alectryon-target");
|
||||
slideshow.pos = -1;
|
||||
}
|
||||
|
||||
function highlight(sentence) {
|
||||
sentence.classList.add("alectryon-target");
|
||||
}
|
||||
|
||||
function scroll(sentence) {
|
||||
// Put the top of the current fragment close to the top of the
|
||||
// screen, but scroll it out of view if showing it requires pushing
|
||||
// the sentence past half of the screen. If sentence is already in
|
||||
// a reasonable position, don't move.
|
||||
var parent = sentence.parentElement;
|
||||
/* We want to scroll the whole document, so start at root… */
|
||||
while (parent && !parent.classList.contains("alectryon-root"))
|
||||
parent = parent.parentElement;
|
||||
/* … and work up from there to find a scrollable element.
|
||||
parent.scrollHeight can be greater than parent.clientHeight
|
||||
without showing scrollbars, so we add a 10px buffer. */
|
||||
while (parent && parent.scrollHeight <= parent.clientHeight + 10)
|
||||
parent = parent.parentElement;
|
||||
/* <body> and <html> elements can have their client rect overflow
|
||||
* the window if their height is unset, so scroll the window
|
||||
* instead */
|
||||
if (parent && (parent.nodeName == "BODY" || parent.nodeName == "HTML"))
|
||||
parent = null;
|
||||
|
||||
var rect = function(e) { return e.getBoundingClientRect(); };
|
||||
var parent_box = parent ? rect(parent) : { y: 0, height: window.innerHeight },
|
||||
sentence_y = rect(sentence).y - parent_box.y,
|
||||
fragment_y = rect(sentence.parentElement).y - parent_box.y;
|
||||
|
||||
// The assertion below sometimes fails for the first element in a block.
|
||||
// console.assert(sentence_y >= fragment_y);
|
||||
|
||||
if (sentence_y < 0.1 * parent_box.height ||
|
||||
sentence_y > 0.7 * parent_box.height) {
|
||||
(parent || window).scrollBy(
|
||||
0, Math.max(sentence_y - 0.5 * parent_box.height,
|
||||
fragment_y - 0.1 * parent_box.height));
|
||||
}
|
||||
}
|
||||
|
||||
function highlighted(pos) {
|
||||
return slideshow.pos == pos;
|
||||
}
|
||||
|
||||
function navigate(pos, inhibitScroll) {
|
||||
unhighlight();
|
||||
slideshow.pos = Math.min(Math.max(pos, 0), slideshow.sentences.length - 1);
|
||||
var sentence = current_sentence();
|
||||
highlight(sentence);
|
||||
if (!inhibitScroll)
|
||||
scroll(sentence);
|
||||
}
|
||||
|
||||
var keys = {
|
||||
PAGE_UP: 33,
|
||||
PAGE_DOWN: 34,
|
||||
ARROW_UP: 38,
|
||||
ARROW_DOWN: 40,
|
||||
h: 72, l: 76, p: 80, n: 78
|
||||
};
|
||||
|
||||
function onkeydown(e) {
|
||||
e = e || window.event;
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
if (e.keyCode == keys.ARROW_UP)
|
||||
slideshow.previous();
|
||||
else if (e.keyCode == keys.ARROW_DOWN)
|
||||
slideshow.next();
|
||||
else
|
||||
return;
|
||||
} else {
|
||||
// if (e.keyCode == keys.PAGE_UP || e.keyCode == keys.p || e.keyCode == keys.h)
|
||||
// slideshow.previous();
|
||||
// else if (e.keyCode == keys.PAGE_DOWN || e.keyCode == keys.n || e.keyCode == keys.l)
|
||||
// slideshow.next();
|
||||
// else
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function start() {
|
||||
slideshow.navigate(0);
|
||||
}
|
||||
|
||||
function toggleHighlight(idx) {
|
||||
if (highlighted(idx))
|
||||
unhighlight();
|
||||
else
|
||||
navigate(idx, true);
|
||||
}
|
||||
|
||||
function handleClick(evt) {
|
||||
if (evt.ctrlKey || evt.metaKey) {
|
||||
var sentence = evt.currentTarget;
|
||||
|
||||
// Ensure that the goal is shown on the side, not inline
|
||||
var checkbox = sentence.getElementsByClassName("alectryon-toggle")[0];
|
||||
if (checkbox)
|
||||
checkbox.checked = false;
|
||||
|
||||
toggleHighlight(sentence.alectryon_index);
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
document.onkeydown = onkeydown;
|
||||
slideshow.pos = -1;
|
||||
slideshow.sentences = Array.from(document.getElementsByClassName("alectryon-sentence"));
|
||||
slideshow.sentences.forEach(function (s, idx) {
|
||||
s.addEventListener('click', handleClick, false);
|
||||
s.alectryon_index = idx;
|
||||
});
|
||||
}
|
||||
|
||||
slideshow.start = start;
|
||||
slideshow.end = unhighlight;
|
||||
slideshow.navigate = navigate;
|
||||
slideshow.next = function() { navigate(slideshow.pos + 1); };
|
||||
slideshow.previous = function() { navigate(slideshow.pos + -1); };
|
||||
window.addEventListener('DOMContentLoaded', init);
|
||||
})(Alectryon.slideshow || (Alectryon.slideshow = {}));
|
||||
|
||||
(function (styles) {
|
||||
var styleNames = ["centered", "floating", "windowed"];
|
||||
|
||||
function className(style) {
|
||||
return "alectryon-" + style;
|
||||
}
|
||||
|
||||
function setStyle(style) {
|
||||
var root = document.getElementsByClassName("alectryon-root")[0];
|
||||
styleNames.forEach(function (s) {
|
||||
root.classList.remove(className(s)); });
|
||||
root.classList.add(className(style));
|
||||
}
|
||||
|
||||
function init() {
|
||||
var banner = document.getElementsByClassName("alectryon-banner")[0];
|
||||
if (banner) {
|
||||
banner.append(" Style: ");
|
||||
styleNames.forEach(function (styleName, idx) {
|
||||
var s = styleName;
|
||||
var a = document.createElement("a");
|
||||
a.onclick = function() { setStyle(s); };
|
||||
a.append(styleName);
|
||||
if (idx > 0) banner.append("; ");
|
||||
banner.appendChild(a);
|
||||
});
|
||||
banner.append(".");
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', init);
|
||||
|
||||
styles.setStyle = setStyle;
|
||||
})(Alectryon.styles || (Alectryon.styles = {}));
|
||||
})(Alectryon || (Alectryon = {}));
|
||||
|
||||
function setHidden(elements, isVisible, token) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
if (isVisible) {
|
||||
elements[i].classList.remove(token)
|
||||
} else {
|
||||
elements[i].classList.add(token)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleShowTypes(checkbox) {
|
||||
setHidden(document.getElementsByClassName("alectryon-io"), checkbox.checked, "type-info-hidden")
|
||||
}
|
||||
|
||||
function toggleShowGoals(checkbox) {
|
||||
setHidden(document.getElementsByClassName("alectryon-io"), checkbox.checked, "output-hidden")
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
Lean binary distribution
|
||||
------------------------
|
||||
|
||||
The binary distribution package contains:
|
||||
|
||||
- Lean executable (located in the sub-directory bin)
|
||||
- Standard library (located in the sub-directory lib/lean/library)
|
||||
|
||||
Assuming you are in the same directory this file is located,
|
||||
the following command executes a simple set of examples
|
||||
|
||||
% bin/lean examples/ex.lean
|
||||
|
||||
For more information on Lean and supported editors, please see https://lean-lang.org/documentation/.
|
||||
@@ -1,21 +0,0 @@
|
||||
[book]
|
||||
authors = ["Leonardo de Moura", "Sebastian Ullrich"]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "."
|
||||
title = "Lean Documentation Overview"
|
||||
|
||||
[build]
|
||||
build-dir = "out"
|
||||
|
||||
[output.html]
|
||||
git-repository-url = "https://github.com/leanprover/lean4"
|
||||
additional-css = ["alectryon.css", "pygments.css"]
|
||||
additional-js = ["alectryon.js"]
|
||||
|
||||
[output.html.fold]
|
||||
enable = true
|
||||
level = 0
|
||||
|
||||
[output.html.playground.boring-prefixes]
|
||||
lean = "# "
|
||||
@@ -1 +0,0 @@
|
||||
# Booleans
|
||||
@@ -1,885 +0,0 @@
|
||||
# Declarations
|
||||
|
||||
-- TODO (fix)
|
||||
|
||||
Declaration Names
|
||||
=================
|
||||
|
||||
A declaration name is a hierarchical [identifier](lexical_structure.md#identifiers) that is interpreted relative to the current namespace as well as (during lookup) to the set of open namespaces.
|
||||
|
||||
```lean
|
||||
namespace A
|
||||
opaque B.c : Nat
|
||||
#print B.c -- opaque A.B.c : Nat
|
||||
end A
|
||||
|
||||
#print A.B.c -- opaque A.B.c : Nat
|
||||
open A
|
||||
#print B.c -- opaque A.B.c : Nat
|
||||
```
|
||||
|
||||
Declaration names starting with an underscore are reserved for internal use. Names starting with the special atomic name ``_root_`` are interpreted as absolute names.
|
||||
|
||||
```lean
|
||||
opaque a : Nat
|
||||
namespace A
|
||||
opaque a : Int
|
||||
#print _root_.a -- opaque a : Nat
|
||||
#print A.a -- opaque A.a : Int
|
||||
end A
|
||||
```
|
||||
|
||||
Contexts and Telescopes
|
||||
=======================
|
||||
|
||||
When processing user input, Lean first parses text to a raw expression format. It then uses background information and type constants to disambiguate overloaded symbols and infer implicit arguments, resulting in a fully-formed expression. This process is known as *elaboration*.
|
||||
|
||||
As hinted in [Expression Syntax](expressions.md#expression_syntax),
|
||||
expressions are parsed and elaborated with respect to an *environment*
|
||||
and a *local context*. Roughly speaking, an environment represents the
|
||||
state of Lean at the point where an expression is parsed, including
|
||||
previously declared axioms, constants, definitions, and theorems. In a
|
||||
given environment, a *local context* consists of a sequence ``(a₁ :
|
||||
α₁) (a₂ : α₂) ... (aₙ : αₙ)`` where each ``aᵢ`` is a name denoting a
|
||||
local constant and each ``αᵢ`` is an expression of type ``Sort u`` for
|
||||
some ``u`` which can involve elements of the environment and the local
|
||||
constants ``aⱼ`` for ``j < i``.
|
||||
|
||||
Intuitively, a local context is a list of variables that are held constant while an expression is being elaborated. Consider the following
|
||||
|
||||
```lean
|
||||
def f (a b : Nat) : Nat → Nat := fun c => a + (b + c)
|
||||
```
|
||||
|
||||
Here the expression ``fun c => a + (b + c)`` is elaborated in the context ``(a : Nat) (b : Nat)`` and the expression ``a + (b + c)`` is elaborated in the context ``(a : Nat) (b : Nat) (c : Nat)``. If you replace the expression ``a + (b + c)`` with an underscore, the error message from Lean will include the current *goal*:
|
||||
|
||||
```
|
||||
a b c : Nat
|
||||
⊢ Nat
|
||||
```
|
||||
|
||||
Here ``a b c : Nat`` indicates the local context, and the second ``Nat`` indicates the expected type of the result.
|
||||
|
||||
A *context* is sometimes called a *telescope*, but the latter is used more generally to include a sequence of declarations occurring relative to a given context. For example, relative to the context ``(a₁ : α₁) (a₂ : α₂) ... (aₙ : αₙ)``, the types ``βᵢ`` in a telescope ``(b₁ : β₁) (b₂ : β₂) ... (bₙ : βₙ)`` can refer to ``a₁, ..., aₙ``. Thus a context can be viewed as a telescope relative to the empty context.
|
||||
|
||||
Telescopes are often used to describe a list of arguments, or parameters, to a declaration. In such cases, it is often notationally convenient to let ``(a : α)`` stand for a telescope rather than just a single argument. In general, the annotations described in [Implicit Arguments](expressions.md#implicit_arguments) can be used to mark arguments as implicit.
|
||||
|
||||
.. _basic_declarations:
|
||||
|
||||
Basic Declarations
|
||||
==================
|
||||
|
||||
Lean provides ways of adding new objects to the environment. The following provide straightforward ways of declaring new objects:
|
||||
|
||||
* ``axiom c : α`` : declare a constant named ``c`` of type ``α``, it is postulating that `α` is not an empty type.
|
||||
* ``def c : α := v`` : defines ``c`` to denote ``v``, which should have type ``α``.
|
||||
* ``theorem c : p := v`` : similar to ``def``, but intended to be used when ``p`` is a proposition.
|
||||
* ``opaque c : α (:= v)?`` : declares a opaque constant named ``c`` of type ``α``, the optional value `v` is must have type `α`
|
||||
and can be viewed as a certificate that ``α`` is not an empty type. If the value is not provided, Lean tries to find one
|
||||
using a procedure based on type class resolution. The value `v` is hidden from the type checker. You can assume that
|
||||
Lean "forgets" `v` after type checking this kind of declaration.
|
||||
|
||||
It is sometimes useful to be able to simulate a definition or theorem without naming it or adding it to the environment.
|
||||
|
||||
* ``example : α := t`` : elaborates ``t`` and checks that it has sort ``α`` (often a proposition), without adding it to the environment.
|
||||
|
||||
In ``def``, the type (``α`` or ``p``, respectively) can be omitted when it can be inferred by Lean. Constants declared with ``theorem`` are marked as ``irreducible``.
|
||||
|
||||
Any of ``def``, ``theorem``, ``axiom``, or ``example`` can take a list of arguments (that is, a context) before the colon. If ``(a : α)`` is a context, the definition ``def foo (a : α) : β := t``
|
||||
is interpreted as ``def foo : (a : α) → β := fun a : α => t``. Similarly, a theorem ``theorem foo (a : α) : p := t`` is interpreted as ``theorem foo : ∀ a : α, p := fun a : α => t``.
|
||||
|
||||
```lean
|
||||
opaque c : Nat
|
||||
opaque d : Nat
|
||||
axiom cd_eq : c = d
|
||||
|
||||
def foo : Nat := 5
|
||||
def bar := 6
|
||||
def baz (x y : Nat) (s : List Nat) := [x, y] ++ s
|
||||
|
||||
theorem foo_eq_five : foo = 5 := rfl
|
||||
theorem baz_theorem (x y : Nat) : baz x y [] = [x, y] := rfl
|
||||
|
||||
example (x y : Nat) : baz x y [] = [x, y] := rfl
|
||||
```
|
||||
|
||||
Inductive Types
|
||||
===============
|
||||
|
||||
Lean's axiomatic foundation allows users to declare arbitrary
|
||||
inductive families, following the pattern described by [Dybjer]_. To
|
||||
make the presentation more manageable, we first describe inductive
|
||||
*types*, and then describe the generalization to inductive *families*
|
||||
in the next section. The declaration of an inductive type has the
|
||||
following form:
|
||||
|
||||
```
|
||||
inductive Foo (a : α) where
|
||||
| constructor₁ : (b : β₁) → Foo a
|
||||
| constructor₂ : (b : β₂) → Foo a
|
||||
...
|
||||
| constructorₙ : (b : βₙ) → Foo a
|
||||
```
|
||||
|
||||
Here ``(a : α)`` is a context and each ``(b : βᵢ)`` is a telescope in the context ``(a : α)`` together with ``Foo``, subject to the following constraints.
|
||||
|
||||
Suppose the telescope ``(b : βᵢ)`` is ``(b₁ : βᵢ₁) ... (bᵤ : βᵢᵤ)``. Each argument in the telescope is either *nonrecursive* or *recursive*.
|
||||
|
||||
- An argument ``(bⱼ : βᵢⱼ)`` is *nonrecursive* if ``βᵢⱼ`` does not refer to ``foo,`` the inductive type being defined. In that case, ``βᵢⱼ`` can be any type, so long as it does not refer to any nonrecursive arguments.
|
||||
|
||||
- An argument ``(bⱼ : βᵢⱼ)`` is *recursive* if it ``βᵢⱼ`` of the form ``Π (d : δ), foo`` where ``(d : δ)`` is a telescope which does not refer to ``foo`` or any nonrecursive arguments.
|
||||
|
||||
The inductive type ``foo`` represents a type that is freely generated by the constructors. Each constructor can take arbitrary data and facts as arguments (the nonrecursive arguments), as well as indexed sequences of elements of ``foo`` that have been previously constructed (the recursive arguments). In set theoretic models, such sets can be represented by well-founded trees labeled by the constructor data, or they can defined using other transfinite or impredicative means.
|
||||
|
||||
The declaration of the type ``foo`` as above results in the addition of the following constants to the environment:
|
||||
|
||||
- the *type former* ``foo : Π (a : α), Sort u``
|
||||
- for each ``i``, the *constructor* ``foo.constructorᵢ : Π (a : α) (b : βᵢ), foo a``
|
||||
- the *eliminator* ``foo.rec``, which takes arguments
|
||||
|
||||
+ ``(a : α)`` (the parameters)
|
||||
+ ``{C : foo a → Type u}`` (the *motive* of the elimination)
|
||||
+ for each ``i``, the *minor premise* corresponding to ``constructorᵢ``
|
||||
+ ``(x : foo)`` (the *major premise*)
|
||||
|
||||
and returns an element of ``C x``. Here, The ith minor premise is a function which takes
|
||||
|
||||
+ ``(b : βᵢ)`` (the arguments to the constructor)
|
||||
+ an argument of type ``Π (d : δ), C (bⱼ d)`` corresponding to each recursive argument ``(bⱼ : βᵢⱼ)``, where ``βᵢⱼ`` is of the form ``Π (d : δ), foo`` (the recursive values of the function being defined)
|
||||
|
||||
and returns an element of ``C (constructorᵢ a b)``, the intended value of the function at ``constructorᵢ a b``.
|
||||
|
||||
The eliminator represents a principle of recursion: to construct an element of ``C x`` where ``x : foo a``, it suffices to consider each of the cases where ``x`` is of the form ``constructorᵢ a b`` and to provide an auxiliary construction in each case. In the case where some of the arguments to ``constructorᵢ`` are recursive, we can assume that we have already constructed values of ``C y`` for each value ``y`` constructed at an earlier stage.
|
||||
|
||||
Under the propositions-as-type correspondence, when ``C x`` is an element of ``Prop``, the eliminator represents a principle of induction. In order to show ``∀ x, C x``, it suffices to show that ``C`` holds for each constructor, under the inductive hypothesis that it holds for all recursive inputs to the constructor.
|
||||
|
||||
The eliminator and constructors satisfy the following identities, in which all the arguments are shown explicitly. Suppose we set ``F := foo.rec a C f₁ ... fₙ``. Then for each constructor, we have the definitional reduction:
|
||||
|
||||
```
|
||||
F (constructorᵢ a b) = fᵢ b ... (fun d : δᵢⱼ => F (bⱼ d)) ...
|
||||
```
|
||||
|
||||
where the ellipses include one entry for each recursive argument.
|
||||
|
||||
Below are some common examples of inductive types, many of which are defined in the core library.
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
universe u v
|
||||
|
||||
-- BEGIN
|
||||
inductive Empty : Type
|
||||
|
||||
inductive Unit : Type
|
||||
| unit : Unit
|
||||
|
||||
inductive Bool : Type
|
||||
| false : Bool
|
||||
| true : Bool
|
||||
|
||||
inductive Prod (α : Type u) (β : Type v) : Type (max u v)
|
||||
| mk : α → β → Prod α β
|
||||
|
||||
inductive Sum (α : Type u) (β : Type v)
|
||||
| inl : α → Sum α β
|
||||
| inr : β → Sum α β
|
||||
|
||||
inductive Sigma (α : Type u) (β : α → Type v)
|
||||
| mk : (a : α) → β a → Sigma α β
|
||||
|
||||
inductive false : Prop
|
||||
|
||||
inductive True : Prop
|
||||
| trivial : True
|
||||
|
||||
inductive And (p q : Prop) : Prop
|
||||
| intro : p → q → And p q
|
||||
|
||||
inductive Or (p q : Prop) : Prop
|
||||
| inl : p → Or p q
|
||||
| inr : q → Or p q
|
||||
|
||||
inductive Exists (α : Type u) (p : α → Prop) : Prop
|
||||
| intro : ∀ x : α, p x → Exists α p
|
||||
|
||||
inductive Subtype (α : Type u) (p : α → Prop) : Type u
|
||||
| intro : ∀ x : α, p x → Subtype α p
|
||||
|
||||
inductive Nat : Type
|
||||
| zero : Nat
|
||||
| succ : Nat → Nat
|
||||
|
||||
inductive List (α : Type u)
|
||||
| nil : List α
|
||||
| cons : α → List α → List α
|
||||
|
||||
-- full binary tree with nodes and leaves labeled from α
|
||||
inductive BinTree (α : Type u)
|
||||
| leaf : α → BinTree α
|
||||
| node : BinTree α → α → BinTree α → BinTree α
|
||||
|
||||
-- every internal node has subtrees indexed by Nat
|
||||
inductive CBT (α : Type u)
|
||||
| leaf : α → CBT α
|
||||
| node : (Nat → CBT α) → CBT α
|
||||
-- END
|
||||
end Hide
|
||||
```
|
||||
|
||||
Note that in the syntax of the inductive definition ``Foo``, the context ``(a : α)`` is left implicit. In other words, constructors and recursive arguments are written as though they have return type ``Foo`` rather than ``Foo a``.
|
||||
|
||||
Elements of the context ``(a : α)`` can be marked implicit as described in [Implicit Arguments](#implicit.md#implicit_arguments). These annotations bear only on the type former, ``Foo``. Lean uses a heuristic to determine which arguments to the constructors should be marked implicit, namely, an argument is marked implicit if it can be inferred from the type of a subsequent argument. If the annotation ``{}`` appears after the constructor, a argument is marked implicit if it can be inferred from the type of a subsequent argument *or the return type*. For example, it is useful to let ``nil`` denote the empty list of any type, since the type can usually be inferred in the context in which it appears. These heuristics are imperfect, and you may sometimes wish to define your own constructors in terms of the default ones. In that case, use the ``[match_pattern]`` [attribute](TODO: missing link) to ensure that these will be used appropriately by the [Equation Compiler](#the-equation-compiler).
|
||||
|
||||
There are restrictions on the universe ``u`` in the return type ``Sort u`` of the type former. There are also restrictions on the universe ``u`` in the return type ``Sort u`` of the motive of the eliminator. These will be discussed in the next section in the more general setting of inductive families.
|
||||
|
||||
Lean allows some additional syntactic conveniences. You can omit the return type of the type former, ``Sort u``, in which case Lean will infer the minimal possible nonzero value for ``u``. As with function definitions, you can list arguments to the constructors before the colon. In an enumerated type (that is, one where the constructors have no arguments), you can also leave out the return type of the constructors.
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
universe u
|
||||
|
||||
-- BEGIN
|
||||
inductive Weekday
|
||||
| sunday | monday | tuesday | wednesday
|
||||
| thursday | friday | saturday
|
||||
|
||||
inductive Nat
|
||||
| zero
|
||||
| succ (n : Nat) : Nat
|
||||
|
||||
inductive List (α : Type u)
|
||||
| nil : List α
|
||||
| cons (a : α) (l : List α) : List α
|
||||
|
||||
@[match_pattern]
|
||||
def List.nil' (α : Type u) : List α := List.nil
|
||||
|
||||
def length {α : Type u} : List α → Nat
|
||||
| (List.nil' _) => 0
|
||||
| (List.cons a l) => 1 + length l
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
```
|
||||
|
||||
The type former, constructors, and eliminator are all part of Lean's axiomatic foundation, which is to say, they are part of the trusted kernel. In addition to these axiomatically declared constants, Lean automatically defines some additional objects in terms of these, and adds them to the environment. These include the following:
|
||||
|
||||
- ``Foo.recOn`` : a variant of the eliminator, in which the major premise comes first
|
||||
- ``Foo.casesOn`` : a restricted version of the eliminator which omits any recursive calls
|
||||
- ``Foo.noConfusionType``, ``Foo.noConfusion`` : functions which witness the fact that the inductive type is freely generated, i.e. that the constructors are injective and that distinct constructors produce distinct objects
|
||||
- ``Foo.below``, ``Foo.ibelow`` : functions used by the equation compiler to implement structural recursion
|
||||
- ``instance : SizeOf Foo`` : a measure which can be used for well-founded recursion
|
||||
|
||||
Note that it is common to put definitions and theorems related to a datatype ``foo`` in a namespace of the same name. This makes it possible to use projection notation described in [Structures](struct.md#structures) and [Namespaces](namespaces.md#namespaces).
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
universe u
|
||||
|
||||
-- BEGIN
|
||||
inductive Nat
|
||||
| zero
|
||||
| succ (n : Nat) : Nat
|
||||
|
||||
#check Nat
|
||||
#check @Nat.rec
|
||||
#check Nat.zero
|
||||
#check Nat.succ
|
||||
|
||||
#check @Nat.recOn
|
||||
#check @Nat.casesOn
|
||||
#check @Nat.noConfusionType
|
||||
#check @Nat.noConfusion
|
||||
#check @Nat.brecOn
|
||||
#check Nat.below
|
||||
#check Nat.ibelow
|
||||
#check Nat._sizeOf_1
|
||||
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
```
|
||||
|
||||
.. _inductive_families:
|
||||
|
||||
Inductive Families
|
||||
==================
|
||||
|
||||
In fact, Lean implements a slight generalization of the inductive types described in the previous section, namely, inductive *families*. The declaration of an inductive family in Lean has the following form:
|
||||
|
||||
```
|
||||
inductive Foo (a : α) : Π (c : γ), Sort u
|
||||
| constructor₁ : Π (b : β₁), Foo t₁
|
||||
| constructor₂ : Π (b : β₂), Foo t₂
|
||||
...
|
||||
| constructorₙ : Π (b : βₙ), Foo tₙ
|
||||
```
|
||||
|
||||
Here ``(a : α)`` is a context, ``(c : γ)`` is a telescope in context ``(a : α)``, each ``(b : βᵢ)`` is a telescope in the context ``(a : α)`` together with ``(Foo : Π (c : γ), Sort u)`` subject to the constraints below, and each ``tᵢ`` is a tuple of terms in the context ``(a : α) (b : βᵢ)`` having the types ``γ``. Instead of defining a single inductive type ``Foo a``, we are now defining a family of types ``Foo a c`` indexed by elements ``c : γ``. Each constructor, ``constructorᵢ``, places its result in the type ``Foo a tᵢ``, the member of the family with index ``tᵢ``.
|
||||
|
||||
The modifications to the scheme in the previous section are straightforward. Suppose the telescope ``(b : βᵢ)`` is ``(b₁ : βᵢ₁) ... (bᵤ : βᵢᵤ)``.
|
||||
|
||||
- As before, an argument ``(bⱼ : βᵢⱼ)`` is *nonrecursive* if ``βᵢⱼ`` does not refer to ``Foo,`` the inductive type being defined. In that case, ``βᵢⱼ`` can be any type, so long as it does not refer to any nonrecursive arguments.
|
||||
|
||||
- An argument ``(bⱼ : βᵢⱼ)`` is *recursive* if ``βᵢⱼ`` is of the form ``Π (d : δ), Foo s`` where ``(d : δ)`` is a telescope which does not refer to ``Foo`` or any nonrecursive arguments and ``s`` is a tuple of terms in context ``(a : α)`` and the previous nonrecursive ``bⱼ``'s with types ``γ``.
|
||||
|
||||
The declaration of the type ``Foo`` as above results in the addition of the following constants to the environment:
|
||||
|
||||
- the *type former* ``Foo : Π (a : α) (c : γ), Sort u``
|
||||
- for each ``i``, the *constructor* ``Foo.constructorᵢ : Π (a : α) (b : βᵢ), Foo a tᵢ``
|
||||
- the *eliminator* ``Foo.rec``, which takes arguments
|
||||
|
||||
+ ``(a : α)`` (the parameters)
|
||||
+ ``{C : Π (c : γ), Foo a c → Type u}`` (the motive of the elimination)
|
||||
+ for each ``i``, the minor premise corresponding to ``constructorᵢ``
|
||||
+ ``(x : Foo a)`` (the major premise)
|
||||
|
||||
and returns an element of ``C x``. Here, The ith minor premise is a function which takes
|
||||
|
||||
+ ``(b : βᵢ)`` (the arguments to the constructor)
|
||||
+ an argument of type ``Π (d : δ), C s (bⱼ d)`` corresponding to each recursive argument ``(bⱼ : βᵢⱼ)``, where ``βᵢⱼ`` is of the form ``Π (d : δ), Foo s``
|
||||
|
||||
and returns an element of ``C tᵢ (constructorᵢ a b)``.
|
||||
|
||||
Suppose we set ``F := Foo.rec a C f₁ ... fₙ``. Then for each constructor, we have the definitional reduction, as before:
|
||||
|
||||
```
|
||||
F (constructorᵢ a b) = fᵢ b ... (fun d : δᵢⱼ => F (bⱼ d)) ...
|
||||
```
|
||||
|
||||
where the ellipses include one entry for each recursive argument.
|
||||
|
||||
The following are examples of inductive families.
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
universe u
|
||||
|
||||
-- BEGIN
|
||||
inductive Vector (α : Type u) : Nat → Type u
|
||||
| nil : Vector 0
|
||||
| succ : Π n, Vector n → Vector (n + 1)
|
||||
|
||||
-- 'IsProd s n' means n is a product of elements of s
|
||||
inductive IsProd (s : Set Nat) : Nat → Prop
|
||||
| base : ∀ n ∈ s, IsProd n
|
||||
| step : ∀ m n, IsProd m → IsProd n → IsProd (m * n)
|
||||
|
||||
inductive Eq {α : Sort u} (a : α) : α → Prop
|
||||
| refl : Eq a
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
```
|
||||
|
||||
We can now describe the constraints on the return type of the type former, ``Sort u``. We can always take ``u`` to be ``0``, in which case we are defining an inductive family of propositions. If ``u`` is nonzero, however, it must satisfy the following constraint: for each type ``βᵢⱼ : Sort v`` occurring in the constructors, we must have ``u ≥ v``. In the set-theoretic interpretation, this ensures that the universe in which the resulting type resides is large enough to contain the inductively generated family, given the number of distinctly-labeled constructors. The restriction does not hold for inductively defined propositions, since these contain no data.
|
||||
|
||||
Putting an inductive family in ``Prop``, however, does impose a restriction on the eliminator. Generally speaking, for an inductive family in ``Prop``, the motive in the eliminator is required to be in ``Prop``. But there is an exception to this rule: you are allowed to eliminate from an inductively defined ``Prop`` to an arbitrary ``Sort`` when there is only one constructor, and each argument to that constructor is either in ``Prop`` or an index. The intuition is that in this case the elimination does not make use of any information that is not already given by the mere fact that the type of argument is inhabited. This special case is known as *singleton elimination*.
|
||||
|
||||
.. _mutual_and_nested_inductive_definitions:
|
||||
|
||||
Mutual and Nested Inductive Definitions
|
||||
=======================================
|
||||
|
||||
Lean supports two generalizations of the inductive families described above, namely, *mutual* and *nested* inductive definitions. These are *not* implemented natively in the kernel. Rather, the definitions are compiled down to the primitive inductive types and families.
|
||||
|
||||
The first generalization allows for multiple inductive types to be defined simultaneously.
|
||||
|
||||
```
|
||||
mutual
|
||||
|
||||
inductive Foo (a : α) : Π (c : γ₁), Sort u
|
||||
| constructor₁₁ : Π (b : β₁₁), Foo a t₁₁
|
||||
| constructor₁₂ : Π (b : β₁₂), Foo a t₁₂
|
||||
...
|
||||
| constructor₁ₙ : Π (b : β₁ₙ), Foo a t₁ₙ
|
||||
|
||||
inductive Bar (a : α) : Π (c : γ₂), Sort u
|
||||
| constructor₂₁ : Π (b : β₂₁), Bar a t₂₁
|
||||
| constructor₂₂ : Π (b : β₂₂), Bar a t₂₂
|
||||
...
|
||||
| constructor₂ₘ : Π (b : β₂ₘ), Bar a t₂ₘ
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
Here the syntax is shown for defining two inductive families, ``Foo`` and ``Bar``, but any number is allowed. The restrictions are almost the same as for ordinary inductive families. For example, each ``(b : βᵢⱼ)`` is a telescope relative to the context ``(a : α)``. The difference is that the constructors can now have recursive arguments whose return types are any of the inductive families currently being defined, in this case ``Foo`` and ``Bar``. Note that all of the inductive definitions share the same parameters ``(a : α)``, though they may have different indices.
|
||||
|
||||
A mutual inductive definition is compiled down to an ordinary inductive definition using an extra finite-valued index to distinguish the components. The details of the internal construction are meant to be hidden from most users. Lean defines the expected type formers ``Foo`` and ``Bar`` and constructors ``constructorᵢⱼ`` from the internal inductive definition. There is no straightforward elimination principle, however. Instead, Lean defines an appropriate ``sizeOf`` measure, meant for use with well-founded recursion, with the property that the recursive arguments to a constructor are smaller than the constructed value.
|
||||
|
||||
The second generalization relaxes the restriction that in the recursive definition of ``Foo``, ``Foo`` can only occur strictly positively in the type of any of its recursive arguments. Specifically, in a nested inductive definition, ``Foo`` can appear as an argument to another inductive type constructor, so long as the corresponding parameter occurs strictly positively in the constructors for *that* inductive type. This process can be iterated, so that additional type constructors can be applied to those, and so on.
|
||||
|
||||
A nested inductive definition is compiled down to an ordinary inductive definition using a mutual inductive definition to define copies of all the nested types simultaneously. Lean then constructs isomorphisms between the mutually defined nested types and their independently defined counterparts. Once again, the internal details are not meant to be manipulated by users. Rather, the type former and constructors are made available and work as expected, while an appropriate ``sizeOf`` measure is generated for use with well-founded recursion.
|
||||
|
||||
```lean
|
||||
universe u
|
||||
-- BEGIN
|
||||
mutual
|
||||
inductive Even : Nat → Prop
|
||||
| even_zero : Even 0
|
||||
| even_succ : ∀ n, Odd n → Even (n + 1)
|
||||
inductive Odd : Nat → Prop
|
||||
| odd_succ : ∀ n, Even n → Odd (n + 1)
|
||||
end
|
||||
|
||||
inductive Tree (α : Type u)
|
||||
| mk : α → List (Tree α) → Tree α
|
||||
|
||||
inductive DoubleTree (α : Type u)
|
||||
| mk : α → List (DoubleTree α) × List (DoubleTree α) → DoubleTree α
|
||||
-- END
|
||||
```
|
||||
|
||||
.. _the_equation_compiler:
|
||||
|
||||
The Equation Compiler
|
||||
=====================
|
||||
|
||||
The equation compiler takes an equational description of a function or proof and tries to define an object meeting that specification. It expects input with the following syntax:
|
||||
|
||||
```
|
||||
def foo (a : α) : Π (b : β), γ
|
||||
| [patterns₁] => t₁
|
||||
...
|
||||
| [patternsₙ] => tₙ
|
||||
```
|
||||
|
||||
Here ``(a : α)`` is a telescope, ``(b : β)`` is a telescope in the context ``(a : α)``, and ``γ`` is an expression in the context ``(a : α) (b : β)`` denoting a ``Type`` or a ``Prop``.
|
||||
|
||||
Each ``patternsᵢ`` is a sequence of patterns of the same length as ``(b : β)``. A pattern is either:
|
||||
|
||||
- a variable, denoting an arbitrary value of the relevant type,
|
||||
- an underscore, denoting a *wildcard* or *anonymous variable*,
|
||||
- an inaccessible term (see below), or
|
||||
- a constructor for the inductive type of the corresponding argument, applied to a sequence of patterns.
|
||||
|
||||
In the last case, the pattern must be enclosed in parentheses.
|
||||
|
||||
Each term ``tᵢ`` is an expression in the context ``(a : α)`` together with the variables introduced on the left-hand side of the token ``=>``. The term ``tᵢ`` can also include recursive calls to ``foo``, as described below. The equation compiler does case splitting on the variables ``(b : β)`` as necessary to match the patterns, and defines ``foo`` so that it has the value ``tᵢ`` in each of the cases. In ideal circumstances (see below), the equations hold definitionally. Whether they hold definitionally or only propositionally, the equation compiler proves the relevant equations and assigns them internal names. They are accessible by the ``rewrite`` and ``simp`` tactics under the name ``foo`` (see [Rewrite](tactics.md#rewrite) and _[TODO: where is simplifier tactic documented?]_. If some of the patterns overlap, the equation compiler interprets the definition so that the first matching pattern applies in each case. Thus, if the last pattern is a variable, it covers all the remaining cases. If the patterns that are presented do not cover all possible cases, the equation compiler raises an error.
|
||||
|
||||
When identifiers are marked with the ``[match_pattern]`` attribute, the equation compiler unfolds them in the hopes of exposing a constructor. For example, this makes it possible to write ``n+1`` and ``0`` instead of ``Nat.succ n`` and ``Nat.zero`` in patterns.
|
||||
|
||||
For a nonrecursive definition involving case splits, the defining equations will hold definitionally. With inductive types like ``Char``, ``String``, and ``Fin n``, a case split would produce definitions with an inordinate number of cases. To avoid this, the equation compiler uses ``if ... then ... else`` instead of ``casesOn`` when defining the function. In this case, the defining equations hold definitionally as well.
|
||||
|
||||
```lean
|
||||
open Nat
|
||||
|
||||
def sub2 : Nat → Nat
|
||||
| zero => 0
|
||||
| succ zero => 0
|
||||
| succ (succ a) => a
|
||||
|
||||
def bar : Nat → List Nat → Bool → Nat
|
||||
| 0, _, false => 0
|
||||
| 0, b :: _, _ => b
|
||||
| 0, [], true => 7
|
||||
| a+1, [], false => a
|
||||
| a+1, [], true => a + 1
|
||||
| a+1, b :: _, _ => a + b
|
||||
|
||||
def baz : Char → Nat
|
||||
| 'A' => 1
|
||||
| 'B' => 2
|
||||
| _ => 3
|
||||
```
|
||||
|
||||
The case where patterns are matched against an argument whose type is an inductive family is known as *dependent pattern matching*. This is more complicated, because the type of the function being defined can impose constraints on the patterns that are matched. In this case, the equation compiler will detect inconsistent cases and rule them out.
|
||||
|
||||
```lean
|
||||
universe u
|
||||
|
||||
inductive Vector (α : Type u) : Nat → Type u
|
||||
| nil : Vector α 0
|
||||
| cons : α → Vector α n → Vector α (n+1)
|
||||
|
||||
namespace Vector
|
||||
|
||||
def head : Vector α (n+1) → α
|
||||
| cons h t => h
|
||||
|
||||
def tail : Vector α (n+1) → Vector α n
|
||||
| cons h t => t
|
||||
|
||||
def map (f : α → β → γ) : Vector α n → Vector β n → Vector γ n
|
||||
| nil, nil => nil
|
||||
| cons a va, cons b vb => cons (f a b) (map f va vb)
|
||||
|
||||
end Vector
|
||||
```
|
||||
|
||||
.. _recursive_functions:
|
||||
|
||||
Recursive functions
|
||||
===================
|
||||
|
||||
Lean must ensure that a recursive function terminates, for which there are two strategies: _structural recursion_, in which all recursive calls are made on smaller parts of the input data, and _well-founded recursion_, in which recursive calls are justified by showing that arguments to recursive calls are smaller according to some other measure.
|
||||
|
||||
Structural recursion
|
||||
--------------------
|
||||
|
||||
If the definition of a function contains recursive calls, Lean first tries to interpret the definition as a structural recursion. In order for that to succeed, the recursive arguments must be subterms of the corresponding arguments on the left-hand side.
|
||||
|
||||
The function is then defined using a *course of values* recursion, using automatically generated functions ``below`` and ``brec`` in the namespace corresponding to the inductive type of the recursive argument. In this case the defining equations hold definitionally, possibly with additional case splits.
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
|
||||
-- BEGIN
|
||||
def fib : Nat → Nat
|
||||
| 0 => 1
|
||||
| 1 => 1
|
||||
| (n+2) => fib (n+1) + fib n
|
||||
|
||||
def append {α : Type} : List α → List α → List α
|
||||
| [], l => l
|
||||
| h::t, l => h :: append t l
|
||||
|
||||
example : append [(1 : Nat), 2, 3] [4, 5] = [1, 2, 3, 4, 5] => rfl
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
```
|
||||
|
||||
Well-founded recursion
|
||||
---------------------
|
||||
|
||||
If structural recursion fails, the equation compiler falls back on well-founded recursion. It tries to infer an instance of ``SizeOf`` for the type of each argument, and then tries to find a permutation of the arguments such that each recursive call is decreasing under the lexicographic order with respect to ``sizeOf`` measures. Lean uses information in the local context, so you can often provide the relevant proof manually using ``have`` in the body of the definition.
|
||||
|
||||
In the case of well-founded recursion, the equation used to declare the function holds only propositionally, but not definitionally, and can be accessed using ``unfold``, ``simp`` and ``rewrite`` with the function name (for example ``unfold foo`` or ``simp [foo]``, where ``foo`` is the function defined with well-founded recursion).
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
open Nat
|
||||
|
||||
-- BEGIN
|
||||
def div : Nat → Nat → Nat
|
||||
| x, y =>
|
||||
if h : 0 < y ∧ y ≤ x then
|
||||
have : x - y < x :=
|
||||
sub_lt (Nat.lt_of_lt_of_le h.left h.right) h.left
|
||||
div (x - y) y + 1
|
||||
else
|
||||
0
|
||||
|
||||
example (x y : Nat) :
|
||||
div x y = if 0 < y ∧ y ≤ x then div (x - y) y + 1 else 0 :=
|
||||
by rw [div]; rfl
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
```
|
||||
|
||||
If Lean cannot find a permutation of the arguments for which all recursive calls are decreasing, it will print a table that contains, for every recursive call, which arguments Lean could prove to be decreasing. For example, a function with three recursive calls and four parameters might cause the following message to be printed
|
||||
|
||||
```
|
||||
example.lean:37:0-43:31: error: Could not find a decreasing measure.
|
||||
The arguments relate at each recursive call as follows:
|
||||
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
|
||||
x1 x2 x3 x4
|
||||
1) 39:6-27 = = _ =
|
||||
2) 40:6-25 = ? _ <
|
||||
3) 41:6-25 < _ _ _
|
||||
Please use `termination_by` to specify a decreasing measure.
|
||||
```
|
||||
|
||||
This table should be read as follows:
|
||||
|
||||
* In the first recursive call, in line 39, arguments 1, 2 and 4 are equal to the function's parameters.
|
||||
* The second recursive call, in line 40, has an equal first argument, a smaller fourth argument, and nothing could be inferred for the second argument.
|
||||
* The third recursive call, in line 41, has a decreasing first argument.
|
||||
* No other proofs were attempted, either because the parameter has a type without a non-trivial ``WellFounded`` instance (parameter 3), or because it is already clear that no decreasing measure can be found.
|
||||
|
||||
|
||||
Lean will print the termination measure it found if ``set_option showInferredTerminationBy true`` is set.
|
||||
|
||||
If Lean does not find the termination measure, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
|
||||
```
|
||||
termination_by e
|
||||
```
|
||||
where ``e`` is an expression that depends on the parameters of the function and should be decreasing at each recursive call. The type of `e` should be an instance of the class ``WellFoundedRelation``, which determines how to compare two values of that type.
|
||||
|
||||
If ``f`` has parameters “after the ``:``” (for example when defining functions via patterns using `|`), then these can be brought into scope using the syntax
|
||||
```
|
||||
termination_by a₁ … aₙ => e
|
||||
```
|
||||
|
||||
By default, Lean uses the tactic ``decreasing_tactic`` when proving that an argument is decreasing; see its documentation for how to globally extend it. You can also choose to use a different tactic for a given function definition with the clause
|
||||
```
|
||||
decreasing_by <tac>
|
||||
```
|
||||
which should come after ``termination_by`, if present.
|
||||
|
||||
|
||||
Note that recursive definitions can in general require nested recursions, that is, recursion on different arguments of ``foo`` in the template above. The equation compiler handles this by abstracting later arguments, and recursively defining higher-order functions to meet the specification.
|
||||
|
||||
Mutual recursion
|
||||
----------------
|
||||
|
||||
The equation compiler also allows mutual recursive definitions, with a syntax similar to that of [Mutual and Nested Inductive Definitions](#mutual-and-nested-inductive-definitions). Mutual definitions are always compiled using well-founded recursion, and so once again the defining equations hold only propositionally.
|
||||
|
||||
```lean
|
||||
mutual
|
||||
def even : Nat → Bool
|
||||
| 0 => true
|
||||
| a+1 => odd a
|
||||
def odd : Nat → Bool
|
||||
| 0 => false
|
||||
| a+1 => even a
|
||||
end
|
||||
|
||||
example (a : Nat) : even (a + 1) = odd a :=
|
||||
by simp [even]
|
||||
|
||||
example (a : Nat) : odd (a + 1) = even a :=
|
||||
by simp [odd]
|
||||
```
|
||||
|
||||
Well-founded recursion is especially useful with [Mutual and Nested Inductive Definitions](#mutual-and-nested-inductive-definitions), since it provides the canonical way of defining functions on these types.
|
||||
|
||||
```lean
|
||||
mutual
|
||||
inductive Even : Nat → Prop
|
||||
| even_zero : Even 0
|
||||
| even_succ : ∀ n, Odd n → Even (n + 1)
|
||||
inductive Odd : Nat → Prop
|
||||
| odd_succ : ∀ n, Even n → Odd (n + 1)
|
||||
end
|
||||
|
||||
open Even Odd
|
||||
|
||||
theorem not_odd_zero : ¬ Odd 0 := fun x => nomatch x
|
||||
|
||||
mutual
|
||||
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
|
||||
| _, odd_succ n h => h
|
||||
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
|
||||
| _, even_succ n h => h
|
||||
end
|
||||
|
||||
inductive Term
|
||||
| const : String → Term
|
||||
| app : String → List Term → Term
|
||||
|
||||
open Term
|
||||
|
||||
mutual
|
||||
def num_consts : Term → Nat
|
||||
| .const n => 1
|
||||
| .app n ts => num_consts_lst ts
|
||||
def num_consts_lst : List Term → Nat
|
||||
| [] => 0
|
||||
| t::ts => num_consts t + num_consts_lst ts
|
||||
end
|
||||
```
|
||||
|
||||
In a set of mutually recursive function, either all or no functions must have an explicit termination measure (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
|
||||
|
||||
```
|
||||
mutual
|
||||
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
|
||||
| _, odd_succ n h => h
|
||||
termination_by n h => h
|
||||
decreasing_by decreasing_tactic
|
||||
|
||||
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
|
||||
| _, even_succ n h => h
|
||||
termination_by n h => h
|
||||
end
|
||||
```
|
||||
|
||||
Another way to express mutual recursion is using local function definitions in ``where`` or ``let rec`` clauses: these can be mutually recursive with each other and their containing function:
|
||||
|
||||
```
|
||||
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
|
||||
| _, odd_succ n h => h
|
||||
termination_by n h => h
|
||||
where
|
||||
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
|
||||
| _, even_succ n h => h
|
||||
termination_by n h => h
|
||||
```
|
||||
|
||||
.. _match_expressions:
|
||||
|
||||
Match Expressions
|
||||
=================
|
||||
|
||||
Lean supports a ``match ... with ...`` construct similar to ones found in most functional programming languages. The syntax is as follows:
|
||||
|
||||
```
|
||||
match t₁, ..., tₙ with
|
||||
| p₁₁, ..., p₁ₙ => s₁
|
||||
...
|
||||
| pₘ₁, ..., pₘₙ => sₘ
|
||||
```
|
||||
|
||||
Here ``t₁, ..., tₙ`` are any terms in the context in which the expression appears, the expressions ``pᵢⱼ`` are patterns, and the terms ``sᵢ`` are expressions in the local context together with variables introduced by the patterns on the left-hand side. Each ``sᵢ`` should have the expected type of the entire ``match`` expression.
|
||||
|
||||
Any ``match`` expression is interpreted using the equation compiler, which generalizes ``t₁, ..., tₙ``, defines an internal function meeting the specification, and then applies it to ``t₁, ..., tₙ``. In contrast to the definitions in [The Equation Compiler](declarations.md#the-equation-compiler), the terms ``tᵢ`` are arbitrary terms rather than just variables, and the expression can occur anywhere within a Lean expression, not just at the top level of a definition. Note that the syntax here is somewhat different: both the terms ``tᵢ`` and the patterns ``pᵢⱼ`` are separated by commas.
|
||||
|
||||
```lean
|
||||
def foo (n : Nat) (b c : Bool) :=
|
||||
5 + match n - 5, b && c with
|
||||
| 0, true => 0
|
||||
| m+1, true => m + 7
|
||||
| 0, false => 5
|
||||
| m+1, false => m + 3
|
||||
```
|
||||
|
||||
When a ``match`` has only one line, Lean provides alternative syntax with a destructuring ``let``, as well as a destructuring lambda abstraction. Thus the following definitions all have the same net effect.
|
||||
|
||||
```lean
|
||||
def bar₁ : Nat × Nat → Nat
|
||||
| (m, n) => m + n
|
||||
|
||||
def bar₂ (p : Nat × Nat) : Nat :=
|
||||
match p with | (m, n) => m + n
|
||||
|
||||
def bar₃ : Nat × Nat → Nat :=
|
||||
fun ⟨m, n⟩ => m + n
|
||||
|
||||
def bar₄ (p : Nat × Nat) : Nat :=
|
||||
let ⟨m, n⟩ := p; m + n
|
||||
```
|
||||
|
||||
Information about the term being matched can be preserved in each branch using the syntax `match h : t with`. For example, a user may want to match a term `ns ++ ms : List Nat`, while tracking the hypothesis `ns ++ ms = []` or `ns ++ ms= h :: t` in the respective match arm:
|
||||
|
||||
```lean
|
||||
def foo (ns ms : List Nat) (h1 : ns ++ ms ≠ []) (k : Nat -> Char) : Char :=
|
||||
match h2 : ns ++ ms with
|
||||
-- in this arm, we have the hypothesis `h2 : ns ++ ms = []`
|
||||
| [] => absurd h2 h1
|
||||
-- in this arm, we have the hypothesis `h2 : ns ++ ms = h :: t`
|
||||
| h :: t => k h
|
||||
|
||||
-- '7'
|
||||
#eval foo [7, 8, 9] [] (by decide) Nat.digitChar
|
||||
```
|
||||
|
||||
.. _structures_and_records:
|
||||
|
||||
Structures and Records
|
||||
======================
|
||||
|
||||
The ``structure`` command in Lean is used to define an inductive data type with a single constructor and to define its projections at the same time. The syntax is as follows:
|
||||
|
||||
```
|
||||
structure Foo (a : α) : Sort u extends Bar, Baz :=
|
||||
constructor :: (field₁ : β₁) ... (fieldₙ : βₙ)
|
||||
```
|
||||
|
||||
Here ``(a : α)`` is a telescope, that is, the parameters to the inductive definition. The name ``constructor`` followed by the double colon is optional; if it is not present, the name ``mk`` is used by default. The keyword ``extends`` followed by a list of previously defined structures is also optional; if it is present, an instance of each of these structures is included among the fields to ``Foo``, and the types ``βᵢ`` can refer to their fields as well. The output type, ``Sort u``, can be omitted, in which case Lean infers to smallest non-``Prop`` sort possible (unless all the fields are ``Prop``, in which case it infers ``Prop``).
|
||||
Finally, ``(field₁ : β₁) ... (fieldₙ : βₙ)`` is a telescope relative to ``(a : α)`` and the fields in ``bar`` and ``baz``.
|
||||
|
||||
The declaration above is syntactic sugar for an inductive type declaration, and so results in the addition of the following constants to the environment:
|
||||
|
||||
- the type former : ``Foo : Π (a : α), Sort u``
|
||||
- the single constructor :
|
||||
|
||||
```
|
||||
Foo.constructor : Π (a : α) (toBar : Bar) (toBaz : Baz)
|
||||
(field₁ : β₁) ... (fieldₙ : βₙ), Foo a
|
||||
```
|
||||
|
||||
- the eliminator ``Foo.rec`` for the inductive type with that constructor
|
||||
|
||||
In addition, Lean defines
|
||||
|
||||
- the projections : ``fieldᵢ : Π (a : α) (c : Foo) : βᵢ`` for each ``i``
|
||||
|
||||
where any other fields mentioned in ``βᵢ`` are replaced by the relevant projections from ``c``.
|
||||
|
||||
Given ``c : Foo``, Lean offers the following convenient syntax for the projection ``Foo.fieldᵢ c``:
|
||||
|
||||
- *anonymous projections* : ``c.fieldᵢ``
|
||||
- *numbered projections* : ``c.i``
|
||||
|
||||
These can be used in any situation where Lean can infer that the type of ``c`` is of the form ``Foo a``. The convention for anonymous projections is extended to any function ``f`` defined in the namespace ``Foo``, as described in [Namespaces](namespaces.md).
|
||||
|
||||
Similarly, Lean offers the following convenient syntax for constructing elements of ``Foo``. They are equivalent to ``Foo.constructor b₁ b₂ f₁ f₁ ... fₙ``, where ``b₁ : Bar``, ``b₂ : Baz``, and each ``fᵢ : βᵢ`` :
|
||||
|
||||
- *anonymous constructor*: ``⟨ b₁, b₂, f₁, ..., fₙ ⟩``
|
||||
- *record notation*:
|
||||
|
||||
```
|
||||
{ toBar := b₁, toBaz := b₂, field₁ := f₁, ...,
|
||||
fieldₙ := fₙ : Foo a }
|
||||
```
|
||||
|
||||
The anonymous constructor can be used in any context where Lean can infer that the expression should have a type of the form ``Foo a``. The unicode brackets are entered as ``\<`` and ``\>`` respectively.
|
||||
|
||||
When using record notation, you can omit the annotation ``: Foo a`` when Lean can infer that the expression should have a type of the form ``Foo a``. You can replace either ``toBar`` or ``toBaz`` by assignments to *their* fields as well, essentially acting as though the fields of ``Bar`` and ``Baz`` are simply imported into ``Foo``. Finally, record notation also supports
|
||||
|
||||
- *record updates*: ``{ t with ... fieldᵢ := fᵢ ...}``
|
||||
|
||||
Here ``t`` is a term of type ``Foo a`` for some ``a``. The notation instructs Lean to take values from ``t`` for any field assignment that is omitted from the list.
|
||||
|
||||
Lean also allows you to specify a default value for any field in a structure by writing ``(fieldᵢ : βᵢ := t)``. Here ``t`` specifies the value to use when the field ``fieldᵢ`` is left unspecified in an instance of record notation.
|
||||
|
||||
```lean
|
||||
universe u v
|
||||
|
||||
structure Vec (α : Type u) (n : Nat) :=
|
||||
(l : List α) (h : l.length = n)
|
||||
|
||||
structure Foo (α : Type u) (β : Nat → Type v) : Type (max u v) :=
|
||||
(a : α) (n : Nat) (b : β n)
|
||||
|
||||
structure Bar :=
|
||||
(c : Nat := 8) (d : Nat)
|
||||
|
||||
structure Baz extends Foo Nat (Vec Nat), Bar :=
|
||||
(v : Vec Nat n)
|
||||
|
||||
#check Foo
|
||||
#check @Foo.mk
|
||||
#check @Foo.rec
|
||||
|
||||
#check Foo.a
|
||||
#check Foo.n
|
||||
#check Foo.b
|
||||
|
||||
#check Baz
|
||||
#check @Baz.mk
|
||||
#check @Baz.rec
|
||||
|
||||
#check Baz.toFoo
|
||||
#check Baz.toBar
|
||||
#check Baz.v
|
||||
|
||||
def bzz := Vec.mk [1, 2, 3] rfl
|
||||
|
||||
#check Vec.l bzz
|
||||
#check Vec.h bzz
|
||||
#check bzz.l
|
||||
#check bzz.h
|
||||
#check bzz.1
|
||||
#check bzz.2
|
||||
|
||||
example : Vec Nat 3 := Vec.mk [1, 2, 3] rfl
|
||||
example : Vec Nat 3 := ⟨[1, 2, 3], rfl⟩
|
||||
example : Vec Nat 3 := { l := [1, 2, 3], h := rfl : Vec Nat 3 }
|
||||
example : Vec Nat 3 := { l := [1, 2, 3], h := rfl }
|
||||
|
||||
example : Foo Nat (Vec Nat) := ⟨1, 3, bzz⟩
|
||||
|
||||
example : Baz := ⟨⟨1, 3, bzz⟩, ⟨5, 7⟩, bzz⟩
|
||||
example : Baz := { a := 1, n := 3, b := bzz, c := 5, d := 7, v := bzz}
|
||||
def fzz : Foo Nat (Vec Nat) := {a := 1, n := 3, b := bzz}
|
||||
|
||||
example : Foo Nat (Vec Nat) := { fzz with a := 7 }
|
||||
example : Baz := { fzz with c := 5, d := 7, v := bzz }
|
||||
|
||||
example : Bar := { c := 8, d := 9 }
|
||||
example : Bar := { d := 9 } -- uses the default value for c
|
||||
```
|
||||
|
||||
.. _type_classes:
|
||||
|
||||
Type Classes
|
||||
============
|
||||
|
||||
(Classes and instances. Anonymous instances. Local instances.)
|
||||
|
||||
|
||||
.. [Dybjer] Dybjer, Peter, *Inductive Families*. Formal Aspects of Computing 6, 1994, pages 440-465.
|
||||
@@ -1 +0,0 @@
|
||||
# Definitions
|
||||
66
doc/dep.md
66
doc/dep.md
@@ -1,66 +0,0 @@
|
||||
## What makes dependent type theory dependent?
|
||||
|
||||
The short explanation is that what makes dependent type theory dependent is that types can depend on parameters.
|
||||
You have already seen a nice example of this: the type ``List α`` depends on the argument ``α``, and
|
||||
this dependence is what distinguishes ``List Nat`` and ``List Bool``.
|
||||
For another example, consider the type ``Vector α n``, the type of vectors of elements of ``α`` of length ``n``.
|
||||
This type depends on *two* parameters: the type ``α : Type`` of the elements in the vector and the length ``n : Nat``.
|
||||
|
||||
Suppose we wish to write a function ``cons`` which inserts a new element at the head of a list.
|
||||
What type should ``cons`` have? Such a function is *polymorphic*: we expect the ``cons`` function for ``Nat``, ``Bool``,
|
||||
or an arbitrary type ``α`` to behave the same way.
|
||||
So it makes sense to take the type to be the first argument to ``cons``, so that for any type, ``α``, ``cons α``
|
||||
is the insertion function for lists of type ``α``. In other words, for every ``α``, ``cons α`` is the function that takes an element ``a : α``
|
||||
and a list ``as : List α``, and returns a new list, so we have ``cons α a as : list α``.
|
||||
|
||||
It is clear that ``cons α`` should have type ``α → List α → List α``. But what type should ``cons`` have?
|
||||
A first guess might be ``Type → α → list α → list α``, but, on reflection, this does not make sense:
|
||||
the ``α`` in this expression does not refer to anything, whereas it should refer to the argument of type ``Type``.
|
||||
In other words, *assuming* ``α : Type`` is the first argument to the function, the type of the next two elements are ``α`` and ``List α``.
|
||||
These types vary depending on the first argument, ``α``.
|
||||
|
||||
This is an instance of a *dependent function type*, or *dependent arrow type*. Given ``α : Type`` and ``β : α → Type``,
|
||||
think of ``β`` as a family of types over ``α``, that is, a type ``β a`` for each ``a : α``.
|
||||
In that case, the type ``(a : α) → β a`` denotes the type of functions ``f`` with the property that,
|
||||
for each ``a : α``, ``f a`` is an element of ``β a``. In other words, the type of the value returned by ``f`` depends on its input.
|
||||
|
||||
Notice that ``(a : α) → β`` makes sense for any expression ``β : Type``. When the value of ``β`` depends on ``a``
|
||||
(as does, for example, the expression ``β a`` in the previous paragraph), ``(a : α) → β`` denotes a dependent function type.
|
||||
When ``β`` doesn't depend on ``a``, ``(a : α) → β`` is no different from the type ``α → β``.
|
||||
Indeed, in dependent type theory (and in Lean), ``α → β`` is just notation for ``(a : α) → β`` when ``β`` does not depend on ``a``.
|
||||
|
||||
Returning to the example of lists, we can use the command `#check` to inspect the type of the following `List` functions
|
||||
We will explain the ``@`` symbol and the difference between the round and curly braces momentarily.
|
||||
|
||||
```lean
|
||||
#check @List.cons -- {α : Type u_1} → α → List α → List α
|
||||
#check @List.nil -- {α : Type u_1} → List α
|
||||
#check @List.length -- {α : Type u_1} → List α → Nat
|
||||
#check @List.append -- {α : Type u_1} → List α → List α → List α
|
||||
```
|
||||
|
||||
Just as dependent function types ``(a : α) → β a`` generalize the notion of a function type ``α → β`` by allowing ``β`` to depend on ``α``,
|
||||
dependent Cartesian product types ``(a : α) × β a`` generalize the Cartesian product ``α × β`` in the same way. Dependent products are also
|
||||
called *sigma* types, and you can also write them as `Σ a : α, β a`. You can use `⟨a, b⟩` or `Sigma.mk a b` to create a dependent pair.
|
||||
|
||||
```lean
|
||||
universe u v
|
||||
|
||||
def f (α : Type u) (β : α → Type v) (a : α) (b : β a) : (a : α) × β a :=
|
||||
⟨a, b⟩
|
||||
|
||||
def g (α : Type u) (β : α → Type v) (a : α) (b : β a) : Σ a : α, β a :=
|
||||
Sigma.mk a b
|
||||
|
||||
#reduce f
|
||||
#reduce g
|
||||
|
||||
#reduce f Type (fun α => α) Nat 10
|
||||
#reduce g Type (fun α => α) Nat 10
|
||||
|
||||
#reduce (f Type (fun α => α) Nat 10).1 -- Nat
|
||||
#reduce (g Type (fun α => α) Nat 10).1 -- Nat
|
||||
#reduce (f Type (fun α => α) Nat 10).2 -- 10
|
||||
#reduce (g Type (fun α => α) Nat 10).2 -- 10
|
||||
```
|
||||
The function `f` and `g` above denote the same function.
|
||||
@@ -1,3 +0,0 @@
|
||||
# Dependent Types
|
||||
|
||||
In this section, we introduce simple type theory, types as objects, definitions, and explain what makes dependent type theory *dependent*.
|
||||
@@ -1,109 +0,0 @@
|
||||
# Documentation
|
||||
|
||||
The Lean `doc` folder contains the [Lean Manual](https://lean-lang.org/lean4/doc/) and is
|
||||
authored in a combination of markdown (`*.md`) files and literate Lean files. The .lean files are
|
||||
preprocessed using a tool called [LeanInk](https://github.com/leanprover/leanink) and
|
||||
[Alectryon](https://github.com/Kha/alectryon) which produces a generated markdown file. We then run
|
||||
`mdbook` on the result to generate the html pages.
|
||||
|
||||
|
||||
## Settings
|
||||
|
||||
We are using the following settings while editing the markdown docs.
|
||||
|
||||
```json
|
||||
{
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"[markdown]": {
|
||||
"rewrap.wrappingColumn": 70
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
### Using Nix
|
||||
|
||||
Building the manual using Nix (which is what the CI does) is as easy as
|
||||
```bash
|
||||
$ nix build --update-input lean ./doc
|
||||
```
|
||||
You can also open a shell with `mdbook` for running the commands mentioned below with
|
||||
`nix develop ./doc#book`. Otherwise, read on.
|
||||
|
||||
### Manually
|
||||
|
||||
To build and test the book you have to preprocess the .lean files with Alectryon then use our own
|
||||
fork of the Rust tool named [mdbook](https://github.com/leanprover/mdbook). We have our own fork of
|
||||
mdBook with the following additional features:
|
||||
|
||||
* Add support for hiding lines in other languages
|
||||
[#1339](https://github.com/rust-lang/mdBook/pull/1339)
|
||||
* Make `mdbook test` call the `lean` compiler to test the snippets.
|
||||
* Ability to test a single chapter at a time which is handy when you
|
||||
are working on that chapter. See the `--chapter` option.
|
||||
|
||||
So you need to setup these tools before you can run `mdBook`.
|
||||
|
||||
1. install [Rust](https://www.rust-lang.org/tools/install)
|
||||
which provides you with the `cargo` tool for building rust packages.
|
||||
Then run the following:
|
||||
```bash
|
||||
cargo install --git https://github.com/leanprover/mdBook mdbook
|
||||
```
|
||||
|
||||
1. Clone https://github.com/leanprover/LeanInk.git and run `lake build` then make the resulting
|
||||
binary available to Alectryon using e.g.
|
||||
```bash
|
||||
# make `leanInk` available in the current shell
|
||||
export PATH=$PWD/build/bin:$PATH
|
||||
```
|
||||
|
||||
1. Create a Python 3.10 environment.
|
||||
|
||||
1. Install Alectryon:
|
||||
```
|
||||
python3 -m pip install git+https://github.com/Kha/alectryon.git@typeid
|
||||
```
|
||||
|
||||
1. Now you are ready to process the `*.lean` files using Alectryon as follows:
|
||||
|
||||
```
|
||||
cd lean4/doc
|
||||
alectryon --frontend lean4+markup examples/palindromes.lean --backend webpage -o palindromes.lean.md
|
||||
```
|
||||
|
||||
Repeat this for the other .lean files you care about or write a script to process them all.
|
||||
|
||||
1. Now you can build the book using:
|
||||
```
|
||||
cd lean4/doc
|
||||
mdbook build
|
||||
```
|
||||
|
||||
This will put the HTML in a `out` folder so you can load `out/index.html` in your web browser and
|
||||
it should look like https://lean-lang.org/lean4/doc/.
|
||||
|
||||
1. It is also handy to use e.g. [`mdbook watch`](https://rust-lang.github.io/mdBook/cli/watch.html)
|
||||
in the `doc/` folder so that it keeps the html up to date while you are editing.
|
||||
|
||||
```bash
|
||||
mdbook watch --open # opens the output in `out/` in your default browser
|
||||
```
|
||||
|
||||
## Testing Lean Snippets
|
||||
|
||||
You can run the following in the `doc/` folder to test all the lean code snippets.
|
||||
|
||||
```bash
|
||||
mdbook test
|
||||
```
|
||||
|
||||
and you can use the `--chapter` option to test a specific chapter that you are working on:
|
||||
|
||||
```bash
|
||||
mdbook test --chapter Array
|
||||
```
|
||||
|
||||
Use chapter name `?` to get a list of all the chapter names.
|
||||
@@ -69,7 +69,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
|
||||
- `repl`:
|
||||
There are two copies of `lean-toolchain`/`lakefile.lean`:
|
||||
in the root, and in `test/Mathlib/`. Edit both, and run `lake update` in both directories.
|
||||
- An awkward situtation that sometimes occurs (e.g. with Verso) is that the `master`/`main` branch has already been moved
|
||||
- An awkward situation that sometimes occurs (e.g. with Verso) is that the `master`/`main` branch has already been moved
|
||||
to a nightly toolchain that comes *after* the stable toolchain we are
|
||||
targeting. In this case it is necessary to create a branch `releases/v4.6.0` from the last commit which was on
|
||||
an earlier toolchain, move that branch to the stable toolchain, and create the toolchain tag from that branch.
|
||||
@@ -144,6 +144,10 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
- Run `script/release_steps.py v4.7.0-rc1 <repo>` (e.g. replacing `<repo>` with `batteries`), which will walk you through the following steps:
|
||||
- Create a new branch off `master`/`main` (as specified in the `branch` field), called `bump_to_v4.7.0-rc1`.
|
||||
- Merge `origin/bump/v4.7.0` if relevant (i.e. `bump-branch: true` appears in `release_repos.yml`).
|
||||
- Otherwise, you *may* need to merge `origin/nightly-testing`.
|
||||
- Note that for `verso` and `reference-manual` development happens on `nightly-testing`, so
|
||||
we will merge that branch into `bump_to_v4.7.0-rc1`, but it is essential in the GitHub interface that we do a rebase merge,
|
||||
in order to preserve the history.
|
||||
- Update the contents of `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`.
|
||||
- In the `lakefile.toml` or `lakefile.lean`, if there are dependencies on `nightly-testing`, `bump/v4.7.0`, or specific version tags, update them to the new tag.
|
||||
If they depend on `main` or `master`, don't change this; you've just updated the dependency, so `lake update` will take care of modifying the manifest.
|
||||
@@ -151,7 +155,7 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
|
||||
- Run `lake build && if lake check-test; then lake test; fi` to check things are working.
|
||||
- Commit the changes as `chore: bump toolchain to v4.7.0-rc1` and push.
|
||||
- Create a PR with title "chore: bump toolchain to v4.7.0-rc1".
|
||||
- Merge the PR once CI completes.
|
||||
- Merge the PR once CI completes. (Recall: for `verso` and `reference-manual` you will need to do a rebase merge.)
|
||||
- Re-running `script/release_checklist.py` will then create the tag `v4.7.0-rc1` from `master`/`main` and push it (unless `toolchain-tag: false` in the `release_repos.yml` file)
|
||||
- We do this for the same list of repositories as for stable releases, see above for notes about special cases.
|
||||
As above, there are dependencies between these, and so the process above is iterative.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
Examples
|
||||
========
|
||||
|
||||
- [Palindromes](examples/palindromes.lean.md)
|
||||
- [Binary Search Trees](examples/bintree.lean.md)
|
||||
- [A Certified Type Checker](examples/tc.lean.md)
|
||||
- [The Well-Typed Interpreter](examples/interp.lean.md)
|
||||
- [Dependent de Bruijn Indices](examples/deBruijn.lean.md)
|
||||
- [Parametric Higher-Order Abstract Syntax](examples/phoas.lean.md)
|
||||
4
doc/examples/README.md
Normal file
4
doc/examples/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
These examples are checked in Lean's CI to ensure that they continue
|
||||
to work. They are included in the documentation section of the Lean
|
||||
website via a script that copies the latest version, in order to
|
||||
ensure that the website tracks Lean releases rather than `master`.
|
||||
@@ -1,5 +0,0 @@
|
||||
(this example is rendered by Alectryon in the CI)
|
||||
|
||||
```lean
|
||||
{{#include bintree.lean}}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
(this example is rendered by Alectryon in the CI)
|
||||
|
||||
```lean
|
||||
{{#include deBruijn.lean}}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
(this example is rendered by Alectryon in the CI)
|
||||
|
||||
```lean
|
||||
{{#include interp.lean}}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
(this example is rendered by Alectryon in the CI)
|
||||
|
||||
```lean
|
||||
{{#include palindromes.lean}}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
(this example is rendered by Alectryon in the CI)
|
||||
|
||||
```lean
|
||||
{{#include phoas.lean}}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
(this example is rendered by Alectryon in the CI)
|
||||
|
||||
```lean
|
||||
{{#include tc.lean}}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
(this chapter is rendered by Alectryon in the CI)
|
||||
|
||||
```lean
|
||||
{{#include widgets.lean}}
|
||||
```
|
||||
@@ -1,550 +0,0 @@
|
||||
Expressions
|
||||
===========
|
||||
|
||||
Every expression in Lean has a [Type](types.md). Every type is also an
|
||||
expression of type `Sort u` for some universe level u. See [Type
|
||||
Universes](types.md#type_universes).
|
||||
|
||||
Expression Syntax
|
||||
=================
|
||||
|
||||
The set of expressions in Lean is defined inductively as follows:
|
||||
|
||||
* ``Sort u`` : the universe of types at universe level ``u``
|
||||
* ``c`` : where ``c`` is an identifier denoting a declared constant or a defined object
|
||||
* ``x`` : where ``x`` is a variable in the local context in which the expression is interpreted
|
||||
* `m?` : where `m?` is a metavariable in the metavariable context in which the expression is interpreted,
|
||||
you can view metavariable as a "hole" that still needs to be synthesized
|
||||
* ``(x : α) → β`` : the type of functions taking an element ``x`` of ``α`` to an element of ``β``,
|
||||
where ``β`` is an expression whose type is a ``Sort``
|
||||
* ``s t`` : the result of applying ``s`` to ``t``, where ``s`` and ``t`` are expressions
|
||||
* ``fun x : α => t`` or `λ x : α => t`: the function mapping any value ``x`` of type ``α`` to ``t``, where ``t`` is an expression
|
||||
* ``let x := t; s`` : a local definition, denotes the value of ``s`` when ``x`` is replaced by ``t``
|
||||
* `s.i` : a projection, denotes the value of the `i`-th field of `s`
|
||||
* `lit` : a natural number or string literal
|
||||
* `mdata k s` : the expression `s` decorated with metadata `k`, where is a key-value map
|
||||
|
||||
Every well formed term in Lean has a *type*, which itself is an expression of type ``Sort u`` for some ``u``. The fact that a term ``t`` has type ``α`` is written ``t : α``.
|
||||
|
||||
For an expression to be well formed, its components have to satisfy certain typing constraints. These, in turn, determine the type of the resulting term, as follows:
|
||||
|
||||
* ``Sort u : Sort (u + 1)``
|
||||
* ``c : α``, where ``α`` is the type that ``c`` has been declared or defined to have
|
||||
* ``x : α``, where ``α`` is the type that ``x`` has been assigned in the local context where it is interpreted
|
||||
* ``?m : α``, where ``α`` is the type that ``?m`` has been declared in the metavariable context where it is interpreted
|
||||
* ``(x : α) → β : Sort (imax u v)`` where ``α : Sort u``, and ``β : Sort v`` assuming ``x : α``
|
||||
* ``s t : β[t/x]`` where ``s`` has type ``(x : α) → β`` and ``t`` has type ``α``
|
||||
* ``(fun x : α => t) : (x : α) → β`` if ``t`` has type ``β`` whenever ``x`` has type ``α``
|
||||
* ``(let x := t; s) : β[t/x]`` where ``t`` has type ``α`` and ``s`` has type ``β`` assuming ``x : α``
|
||||
* `lit : Nat` if `lit` is a numeral
|
||||
* `lit : String` if `lit` is a string literal
|
||||
* `mdata k s : α` if `s : α`
|
||||
* `s.i : α` if `s : β` and `β` is an inductive datatype with only one constructor, and `i`-th field has type `α`
|
||||
|
||||
``Prop`` abbreviates ``Sort 0``, ``Type`` abbreviates ``Sort 1``, and
|
||||
``Type u`` abbreviates ``Sort (u + 1)`` when ``u`` is a universe
|
||||
variable. We say "``α`` is a type" to express ``α : Type u`` for some
|
||||
``u``, and we say "``p`` is a proposition" to express
|
||||
``p : Prop``. Using the *propositions as types* correspondence, given
|
||||
``p : Prop``, we refer to an expression ``t : p`` as a *proof* of ``p``. In
|
||||
contrast, given ``α : Type u`` for some ``u`` and ``t : α``, we
|
||||
sometimes refer to ``t`` as *data*.
|
||||
|
||||
When the expression ``β`` in ``(x : α) → β`` does not depend on ``x``,
|
||||
it can be written ``α → β``. As usual, the variable ``x`` is bound in
|
||||
``(x : α) → β``, ``fun x : α => t``, and ``let x := t; s``. The
|
||||
expression ``∀ x : α, β`` is alternative syntax for ``(x : α) → β``,
|
||||
and is intended to be used when ``β`` is a proposition. An underscore
|
||||
can be used to generate an internal variable in a binder, as in
|
||||
``fun _ : α => t``.
|
||||
|
||||
*Metavariables*, that is, temporary placeholders, are used in the
|
||||
process of constructing terms. Terms that are added to the
|
||||
environment contain neither metavariable nor variables, which is to
|
||||
say, they are fully elaborated and make sense in the empty context.
|
||||
|
||||
Axioms can be declared using the ``axiom`` keyword.
|
||||
Similarly, objects can be defined in various ways, such as using ``def`` and ``theorem`` keywords.
|
||||
See [Chapter Declarations](./declarations.md) for more information.
|
||||
|
||||
Writing an expression ``(t : α)`` forces Lean to elaborate ``t`` so that it has type ``α`` or report an error if it fails.
|
||||
|
||||
Lean supports anonymous constructor notation, anonymous projections,
|
||||
and various forms of match syntax, including destructuring ``fun`` and
|
||||
``let``. These, as well as notation for common data types (like pairs,
|
||||
lists, and so on) are discussed in [Chapter Declarations](./declarations.md)
|
||||
in connection with inductive types.
|
||||
|
||||
```lean
|
||||
universe u
|
||||
|
||||
#check Sort 0
|
||||
#check Prop
|
||||
#check Sort 1
|
||||
#check Type
|
||||
#check Sort u
|
||||
#check Sort (u+1)
|
||||
|
||||
#check Nat → Bool
|
||||
#check (α : Type u) → List α
|
||||
#check (α : Type u) → (β : Type u) → Sum α β
|
||||
#check fun x : Nat => x
|
||||
#check fun (α : Type u) (x : α) => x
|
||||
#check let x := 5; x * 2
|
||||
#check "hello"
|
||||
#check (fun x => x) true
|
||||
```
|
||||
|
||||
Implicit Arguments
|
||||
==================
|
||||
|
||||
When declaring arguments to defined objects in Lean (for example, with
|
||||
``def``, ``theorem``, ``axiom``, ``constant``, ``inductive``, or
|
||||
``structure``; see [Chapter Declarations](./declarations.md) or when
|
||||
declaring variables in sections (see [Other Commands](./other_commands.md)),
|
||||
arguments can be annotated as *explicit* or *implicit*.
|
||||
This determines how expressions containing the object are interpreted.
|
||||
|
||||
* ``(x : α)`` : an explicit argument of type ``α``
|
||||
* ``{x : α}`` : an implicit argument, eagerly inserted
|
||||
* ``⦃x : α⦄`` or ``{{x : α}}`` : an implicit argument, weakly inserted
|
||||
* ``[x : α]`` : an implicit argument that should be inferred by type class resolution
|
||||
* ``(x : α := v)`` : an optional argument, with default value ``v``
|
||||
* ``(x : α := by tac)`` : an implicit argument, to be synthesized by tactic ``tac``
|
||||
|
||||
The name of the variable can be omitted from a class resolution
|
||||
argument, in which case an internal name is generated.
|
||||
|
||||
When a function has an explicit argument, you can nonetheless ask
|
||||
Lean's elaborator to infer the argument automatically, by entering it
|
||||
as an underscore (``_``). Conversely, writing ``@foo`` indicates that
|
||||
all of the arguments to be ``foo`` are to be given explicitly,
|
||||
independent of how ``foo`` was declared. You can also provide a value
|
||||
for an implicit parameter using named arguments. Named arguments
|
||||
enable you to specify an argument for a parameter by matching the
|
||||
argument with its name rather than with its position in the parameter
|
||||
list. If you don't remember the order of the parameters but know
|
||||
their names, you can send the arguments in any order. You may also
|
||||
provide the value for an implicit parameter whenLean failed to infer
|
||||
it. Named arguments also improve the readability of your code by
|
||||
identifying what each argument represents.
|
||||
|
||||
|
||||
```lean
|
||||
def add (x y : Nat) : Nat :=
|
||||
x + y
|
||||
|
||||
#check add 2 3 -- Nat
|
||||
#eval add 2 3 -- 5
|
||||
|
||||
def id1 (α : Type u) (x : α) : α := x
|
||||
|
||||
#check id1 Nat 3
|
||||
#check id1 _ 3
|
||||
|
||||
def id2 {α : Type u} (x : α) : α := x
|
||||
|
||||
#check id2 3
|
||||
#check @id2 Nat 3
|
||||
#check id2 (α := Nat) 3
|
||||
#check id2
|
||||
#check id2 (α := Nat)
|
||||
|
||||
def id3 {{α : Type u}} (x : α) : α := x
|
||||
|
||||
#check id3 3
|
||||
#check @id3 Nat 3
|
||||
#check (id3 : (α : Type) → α → α)
|
||||
|
||||
class Cls where
|
||||
val : Nat
|
||||
|
||||
instance Cls_five : Cls where
|
||||
val := 5
|
||||
|
||||
def ex2 [c : Cls] : Nat := c.val
|
||||
|
||||
example : ex2 = 5 := rfl
|
||||
|
||||
def ex2a [Cls] : Nat := ex2
|
||||
|
||||
example : ex2a = 5 := rfl
|
||||
|
||||
def ex3 (x : Nat := 5) := x
|
||||
|
||||
#check ex3 2
|
||||
#check ex3
|
||||
|
||||
example : ex3 = 5 := rfl
|
||||
|
||||
def ex4 (x : Nat) (y : Nat := x) : Nat :=
|
||||
x * y
|
||||
|
||||
example : ex4 x = x * x :=
|
||||
rfl
|
||||
```
|
||||
|
||||
Basic Data Types and Assertions
|
||||
===============================
|
||||
|
||||
The core library contains a number of basic data types, such as the
|
||||
natural numbers (`Nat`), the integers (`Int`), the
|
||||
booleans (``Bool``), and common operations on these, as well as the
|
||||
usual logical quantifiers and connectives. Some example are given
|
||||
below. A list of common notations and their precedences can be found
|
||||
in a [file](https://github.com/leanprover/lean4/blob/master/src/Init/Notation.lean)
|
||||
in the core library. The core library also contains a number of basic
|
||||
data type constructors. Definitions can also be found the
|
||||
[Data](https://github.com/leanprover/lean4/blob/master/src/Init/Data)
|
||||
directory of the core library. For more information, see also [Chapter libraries](./libraries.md).
|
||||
|
||||
```
|
||||
/- numbers -/
|
||||
def f1 (a b c : Nat) : Nat :=
|
||||
a^2 + b^2 + c^2
|
||||
|
||||
def p1 (a b c d : Nat) : Prop :=
|
||||
(a + b)^c ≤ d
|
||||
|
||||
def p2 (i j k : Int) : Prop :=
|
||||
i % (j * k) = 0
|
||||
|
||||
|
||||
/- booleans -/
|
||||
|
||||
def f2 (a b c : Bool) : Bool :=
|
||||
a && (b || c)
|
||||
|
||||
/- pairs -/
|
||||
|
||||
#eval (1, 2)
|
||||
|
||||
def p : Nat × Bool := (1, false)
|
||||
|
||||
section
|
||||
variable (a b c : Nat) (p : Nat × bool)
|
||||
|
||||
#check (1, 2)
|
||||
#check p.1 * 2
|
||||
#check p.2 && tt
|
||||
#check ((1, 2, 3) : Nat × Nat × Nat)
|
||||
end
|
||||
|
||||
/- lists -/
|
||||
section
|
||||
variable x y z : Nat
|
||||
variable xs ys zs : list Nat
|
||||
open list
|
||||
|
||||
#check (1 :: xs) ++ (y :: zs) ++ [1,2,3]
|
||||
#check append (cons 1 xs) (cons y zs)
|
||||
#check map (λ x, x^2) [1, 2, 3]
|
||||
end
|
||||
|
||||
/- sets -/
|
||||
section
|
||||
variable s t u : set Nat
|
||||
|
||||
#check ({1, 2, 3} ∩ s) ∪ ({x | x < 7} ∩ t)
|
||||
end
|
||||
|
||||
/- strings and characters -/
|
||||
#check "hello world"
|
||||
#check 'a'
|
||||
|
||||
/- assertions -/
|
||||
#check ∀ a b c n : Nat,
|
||||
a ≠ 0 ∧ b ≠ 0 ∧ c ≠ 0 ∧ n > 2 → a^n + b^n ≠ c^n
|
||||
|
||||
def unbounded (f : Nat → Nat) : Prop := ∀ M, ∃ n, f n ≥ M
|
||||
```
|
||||
.. _constructors_projections_and_matching:
|
||||
|
||||
Constructors, Projections, and Matching
|
||||
=======================================
|
||||
|
||||
Lean's foundation, the *Calculus of Inductive Constructions*, supports the declaration of *inductive types*. Such types can have any number of *constructors*, and an associated *eliminator* (or *recursor*). Inductive types with one constructor, known as *structures*, have *projections*. The full syntax of inductive types is described in [Declarations](declarations.md), but here we describe some syntactic elements that facilitate their use in expressions.
|
||||
|
||||
When Lean can infer the type of an expression and it is an inductive type with one constructor, then one can write ``⟨a1, a2, ..., an⟩`` to apply the constructor without naming it. For example, ``⟨a, b⟩`` denotes ``prod.mk a b`` in a context where the expression can be inferred to be a pair, and ``⟨h₁, h₂⟩`` denotes ``and.intro h₁ h₂`` in a context when the expression can be inferred to be a conjunction. The notation will nest constructions automatically, so ``⟨a1, a2, a3⟩`` is interpreted as ``prod.mk a1 (prod.mk a2 a3)`` when the expression is expected to have a type of the form ``α1 × α2 × α3``. (The latter is interpreted as ``α1 × (α2 × α3)``, since the product associates to the right.)
|
||||
|
||||
Similarly, one can use "dot notation" for projections: one can write ``p.fst`` and ``p.snd`` for ``prod.fst p`` and ``prod.snd p`` when Lean can infer that ``p`` is an element of a product, and ``h.left`` and ``h.right`` for ``and.left h`` and ``and.right h`` when ``h`` is a conjunction.
|
||||
|
||||
The anonymous projector notation can used more generally for any objects defined in a *namespace* (see [Other Commands](other_commands.md)). For example, if ``l`` has type ``list α`` then ``l.map f`` abbreviates ``list.map f l``, in which ``l`` has been placed at the first argument position where ``list.map`` expects a ``list``.
|
||||
|
||||
Finally, for data types with one constructor, one destruct an element by pattern matching using the ``let`` and ``assume`` constructs, as in the examples below. Internally, these are interpreted using the ``match`` construct, which is in turn compiled down for the eliminator for the inductive type, as described in [Declarations](declarations.md).
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
universes u v
|
||||
variable {α : Type u} {β : Type v}
|
||||
|
||||
def p : Nat × ℤ := ⟨1, 2⟩
|
||||
#check p.fst
|
||||
#check p.snd
|
||||
|
||||
def p' : Nat × ℤ × bool := ⟨1, 2, tt⟩
|
||||
#check p'.fst
|
||||
#check p'.snd.fst
|
||||
#check p'.snd.snd
|
||||
|
||||
def swap_pair (p : α × β) : β × α :=
|
||||
⟨p.snd, p.fst⟩
|
||||
|
||||
theorem swap_conj {a b : Prop} (h : a ∧ b) : b ∧ a :=
|
||||
⟨h.right, h.left⟩
|
||||
|
||||
#check [1, 2, 3].append [2, 3, 4]
|
||||
#check [1, 2, 3].map (λ x, x^2)
|
||||
|
||||
example (p q : Prop) : p ∧ q → q ∧ p :=
|
||||
λ h, ⟨h.right, h.left⟩
|
||||
|
||||
def swap_pair' (p : α × β) : β × α :=
|
||||
let (x, y) := p in (y, x)
|
||||
|
||||
theorem swap_conj' {a b : Prop} (h : a ∧ b) : b ∧ a :=
|
||||
let ⟨ha, hb⟩ := h in ⟨hb, ha⟩
|
||||
|
||||
def swap_pair'' : α × β → β × α :=
|
||||
λ ⟨x, y⟩, (y, x)
|
||||
|
||||
theorem swap_conj'' {a b : Prop} : a ∧ b → b ∧ a :=
|
||||
assume ⟨ha, hb⟩, ⟨hb, ha⟩
|
||||
|
||||
Structured Proofs
|
||||
=================
|
||||
|
||||
Syntactic sugar is provided for writing structured proof terms:
|
||||
|
||||
* ``have h : p := s; t`` is sugar for ``(fun h : p => t) s``
|
||||
* ``suffices h : p from s; t`` is sugar for ``(λ h : p => s) t``
|
||||
* ``suffices h : p by s; t`` is sugar for ``(suffixes h : p from by s; t)``
|
||||
* ``show p from t`` is sugar for ``(have this : p := t; this)``
|
||||
* ``show p by tac`` is sugar for ``(show p from by tac)``
|
||||
|
||||
Types can be omitted when they can be inferred by Lean. Lean also
|
||||
allows ``have : p := t; s``, which gives the assumption the
|
||||
name ``this`` in the local context. Similarly, Lean recognizes the
|
||||
variant ``suffices p from s; t``, which use the name ``this`` for the new hypothesis.
|
||||
|
||||
The notation ``‹p›`` is notation for ``(by assumption : p)``, and can
|
||||
therefore be used to apply hypotheses in the local context.
|
||||
|
||||
As noted in [Constructors, Projections and Matching](#constructors_projections_and_matching),
|
||||
anonymous constructors and projections and match syntax can be used in proofs just as in expressions that denote data.
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
example (p q r : Prop) : p → (q ∧ r) → p ∧ q :=
|
||||
assume h₁ : p,
|
||||
assume h₂ : q ∧ r,
|
||||
have h₃ : q, from and.left h₂,
|
||||
show p ∧ q, from and.intro h₁ h₃
|
||||
|
||||
example (p q r : Prop) : p → (q ∧ r) → p ∧ q :=
|
||||
assume : p,
|
||||
assume : q ∧ r,
|
||||
have q, from and.left this,
|
||||
show p ∧ q, from and.intro ‹p› this
|
||||
|
||||
example (p q r : Prop) : p → (q ∧ r) → p ∧ q :=
|
||||
assume h₁ : p,
|
||||
assume h₂ : q ∧ r,
|
||||
suffices h₃ : q, from and.intro h₁ h₃,
|
||||
show q, from and.left h₂
|
||||
|
||||
Lean also supports a calculational environment, which is introduced with the keyword ``calc``. The syntax is as follows:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
calc
|
||||
<expr>_0 'op_1' <expr>_1 ':' <proof>_1
|
||||
'...' 'op_2' <expr>_2 ':' <proof>_2
|
||||
...
|
||||
'...' 'op_n' <expr>_n ':' <proof>_n
|
||||
|
||||
Each ``<proof>_i`` is a proof for ``<expr>_{i-1} op_i <expr>_i``.
|
||||
|
||||
Here is an example:
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
variable (a b c d e : Nat)
|
||||
variable h1 : a = b
|
||||
variable h2 : b = c + 1
|
||||
variable h3 : c = d
|
||||
variable h4 : e = 1 + d
|
||||
|
||||
theorem T : a = e :=
|
||||
calc
|
||||
a = b : h1
|
||||
... = c + 1 : h2
|
||||
... = d + 1 : congr_arg _ h3
|
||||
... = 1 + d : add_comm d (1 : Nat)
|
||||
... = e : eq.symm h4
|
||||
|
||||
The style of writing proofs is most effective when it is used in conjunction with the ``simp`` and ``rewrite`` tactics.
|
||||
|
||||
.. _computation:
|
||||
|
||||
Computation
|
||||
===========
|
||||
|
||||
Two expressions that differ up to a renaming of their bound variables are said to be *α-equivalent*, and are treated as syntactically equivalent by Lean.
|
||||
|
||||
Every expression in Lean has a natural computational interpretation, unless it involves classical elements that block computation, as described in the next section. The system recognizes the following notions of *reduction*:
|
||||
|
||||
* *β-reduction* : An expression ``(λ x, t) s`` β-reduces to ``t[s/x]``, that is, the result of replacing ``x`` by ``s`` in ``t``.
|
||||
* *ζ-reduction* : An expression ``let x := s in t`` ζ-reduces to ``t[s/x]``.
|
||||
* *δ-reduction* : If ``c`` is a defined constant with definition ``t``, then ``c`` δ-reduces to ``t``.
|
||||
* *ι-reduction* : When a function defined by recursion on an inductive type is applied to an element given by an explicit constructor, the result ι-reduces to the specified function value, as described in [Inductive Types](inductive.md).
|
||||
|
||||
The reduction relation is transitive, which is to say, is ``s`` reduces to ``s'`` and ``t`` reduces to ``t'``, then ``s t`` reduces to ``s' t'``, ``λ x, s`` reduces to ``λ x, s'``, and so on. If ``s`` and ``t`` reduce to a common term, they are said to be *definitionally equal*. Definitional equality is defined to be the smallest equivalence relation that satisfies all these properties and also includes α-equivalence and the following two relations:
|
||||
|
||||
* *η-equivalence* : An expression ``(λx, t x)`` is η-equivalent to ``t``, assuming ``x`` does not occur in ``t``.
|
||||
* *proof irrelevance* : If ``p : Prop``, ``s : p``, and ``t : p``, then ``s`` and ``t`` are considered to be equivalent.
|
||||
|
||||
This last fact reflects the intuition that once we have proved a proposition ``p``, we only care that is has been proved; the proof does nothing more than witness the fact that ``p`` is true.
|
||||
|
||||
Definitional equality is a strong notion of equality of values. Lean's logical foundations sanction treating definitionally equal terms as being the same when checking that a term is well-typed and/or that it has a given type.
|
||||
|
||||
The reduction relation is believed to be strongly normalizing, which is to say, every sequence of reductions applied to a term will eventually terminate. The property guarantees that Lean's type-checking algorithm terminates, at least in principle. The consistency of Lean and its soundness with respect to set-theoretic semantics do not depend on either of these properties.
|
||||
|
||||
Lean provides two commands to compute with expressions:
|
||||
|
||||
* ``#reduce t`` : use the kernel type-checking procedures to carry out reductions on ``t`` until no more reductions are possible, and show the result
|
||||
* ``#eval t`` : evaluate ``t`` using a fast bytecode evaluator, and show the result
|
||||
|
||||
Every computable definition in Lean is compiled to bytecode at definition time. Bytecode evaluation is more liberal than kernel evaluation: types and all propositional information are erased, and functions are evaluated using a stack-based virtual machine. As a result, ``#eval`` is more efficient than ``#reduce,`` and can be used to execute complex programs. In contrast, ``#reduce`` is designed to be small and reliable, and to produce type-correct terms at each step. Bytecode is never used in type checking, so as far as soundness and consistency are concerned, only kernel reduction is part of the trusted computing base.
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
#reduce (fun x => x + 3) 5
|
||||
#eval (fun x => x + 3) 5
|
||||
|
||||
#reduce let x := 5; x + 3
|
||||
#eval let x := 5; x + 3
|
||||
|
||||
def f x := x + 3
|
||||
|
||||
#reduce f 5
|
||||
#eval f 5
|
||||
|
||||
#reduce @Nat.rec (λ n => Nat) (0 : Nat)
|
||||
(λ n recval : Nat => recval + n + 1) (5 : Nat)
|
||||
|
||||
def g : Nat → Nat
|
||||
| 0 => 0
|
||||
| (n+1) => g n + n + 1
|
||||
|
||||
#reduce g 5
|
||||
#eval g 5
|
||||
|
||||
#eval g 5000
|
||||
|
||||
example : (fun x => x + 3) 5 = 8 := rfl
|
||||
example : (fun x => f x) = f := rfl
|
||||
example (p : Prop) (h₁ h₂ : p) : h₁ = h₂ := rfl
|
||||
|
||||
Note: the combination of proof irrelevance and singleton ``Prop`` elimination in ι-reduction renders the ideal version of definitional equality, as described above, undecidable. Lean's procedure for checking definitional equality is only an approximation to the ideal. It is not transitive, as illustrated by the example below. Once again, this does not compromise the consistency or soundness of Lean; it only means that Lean is more conservative in the terms it recognizes as well typed, and this does not cause problems in practice. Singleton elimination will be discussed in greater detail in [Inductive Types](inductive.md).
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
def R (x y : unit) := false
|
||||
def accrec := @acc.rec unit R (λ_, unit) (λ _ a ih, ()) ()
|
||||
example (h) : accrec h = accrec (acc.intro _ (λ y, acc.inv h)) :=
|
||||
rfl
|
||||
example (h) : accrec (acc.intro _ (λ y, acc.inv h)) = () := rfl
|
||||
example (h) : accrec h = () := sorry -- rfl fails
|
||||
|
||||
|
||||
Axioms
|
||||
======
|
||||
|
||||
Lean's foundational framework consists of:
|
||||
|
||||
- type universes and dependent function types, as described above
|
||||
|
||||
- inductive definitions, as described in [Inductive Types](inductive.md) and
|
||||
[Inductive Families](declarations.md#inductive-families).
|
||||
|
||||
In addition, the core library defines (and trusts) the following axiomatic extensions:
|
||||
|
||||
- propositional extensionality:
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace hide
|
||||
|
||||
-- BEGIN
|
||||
axiom propext {a b : Prop} : (a ↔ b) → a = b
|
||||
-- END
|
||||
|
||||
end hide
|
||||
|
||||
- quotients:
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace hide
|
||||
-- BEGIN
|
||||
universes u v
|
||||
|
||||
constant quot : Π {α : Sort u}, (α → α → Prop) → Sort u
|
||||
|
||||
constant quot.mk : Π {α : Sort u} (r : α → α → Prop),
|
||||
α → quot r
|
||||
|
||||
axiom quot.ind : ∀ {α : Sort u} {r : α → α → Prop}
|
||||
{β : quot r → Prop},
|
||||
(∀ a, β (quot.mk r a)) →
|
||||
∀ (q : quot r), β q
|
||||
|
||||
constant quot.lift : Π {α : Sort u} {r : α → α → Prop}
|
||||
{β : Sort u} (f : α → β),
|
||||
(∀ a b, r a b → f a = f b) → quot r → β
|
||||
|
||||
axiom quot.sound : ∀ {α : Type u} {r : α → α → Prop}
|
||||
{a b : α},
|
||||
r a b → quot.mk r a = quot.mk r b
|
||||
-- END
|
||||
end hide
|
||||
|
||||
``quot r`` represents the quotient of ``α`` by the smallest equivalence relation containing ``r``. ``quot.mk`` and ``quot.lift`` satisfy the following computation rule:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
quot.lift f h (quot.mk r a) = f a
|
||||
|
||||
- choice:
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace hide
|
||||
universe u
|
||||
|
||||
-- BEGIN
|
||||
axiom choice {α : Sort u} : nonempty α → α
|
||||
-- END
|
||||
|
||||
end hide
|
||||
|
||||
Here ``nonempty α`` is defined as follows:
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace hide
|
||||
universe u
|
||||
|
||||
-- BEGIN
|
||||
class inductive nonempty (α : Sort u) : Prop
|
||||
| intro : α → nonempty
|
||||
-- END
|
||||
|
||||
end hide
|
||||
|
||||
It is equivalent to ``∃ x : α, true``.
|
||||
|
||||
The quotient construction implies function extensionality. The ``choice`` principle, in conjunction with the others, makes the axiomatic foundation classical; in particular, it implies the law of the excluded middle and propositional decidability. Functions that make use of ``choice`` to produce data are incompatible with a computational interpretation, and do not produce bytecode. They have to be declared ``noncomputable``.
|
||||
|
||||
For metaprogramming purposes, Lean also allows the definition of objects which stand outside the object language. These are denoted with the ``meta`` keyword, as described in [Metaprogramming](metaprogramming.md).
|
||||
55
doc/faq.md
55
doc/faq.md
@@ -1,55 +0,0 @@
|
||||
Frequently Asked Questions
|
||||
==========================
|
||||
|
||||
### What is Lean?
|
||||
|
||||
Lean is a new open source theorem prover being developed at Microsoft Research.
|
||||
It is a research project that aims to bridge the gap between interactive and automated theorem proving.
|
||||
Lean can be also used as a programming language. Actually, some Lean features are implemented in Lean itself.
|
||||
|
||||
### Should I use Lean?
|
||||
|
||||
Lean is under heavy development, and we are constantly trying new
|
||||
ideas and tweaking the system. It is a research project and not a product.
|
||||
Things change rapidly, and we constantly break backward compatibility.
|
||||
Lean comes "as is", you should not expect we will fix bugs and/or add new features for your project.
|
||||
We have our own priorities, and will not change them to accommodate your needs.
|
||||
Even if you implement a new feature or fix a bug, we may not want to merge it because
|
||||
it may conflict with our plans for Lean, it may not be performant, we may not want to maintain it,
|
||||
we may be busy, etc. If you really need this new feature or bug fix, we suggest you create your own fork and maintain it yourself.
|
||||
|
||||
### Where is the documentation?
|
||||
|
||||
This is the Lean 4 manual. It is a work in progress, but it will eventually cover the whole language.
|
||||
A public and very active chat room dedicated to Lean is open on [Zulip](https://leanprover.zulipchat.com).
|
||||
It is a good place to interact with other Lean users.
|
||||
|
||||
### Should I use Lean to teach a course?
|
||||
|
||||
Lean has been used to teach courses on logic, type theory and programming languages at CMU and the University of Washington.
|
||||
The lecture notes for the CMU course [Logic and Proof](https://lean-lang.org/logic_and_proof) are available online,
|
||||
but they are for Lean 3.
|
||||
If you decide to teach a course using Lean, we suggest you prepare all material before the beginning of the course, and
|
||||
make sure that Lean attends all your needs. You should not expect we will fix bugs and/or add features needed for your course.
|
||||
|
||||
### Are there IDEs for Lean?
|
||||
|
||||
Yes, see [Setting Up Lean](./setup.md).
|
||||
|
||||
### Is Lean sound? How big is the kernel? Should I trust it?
|
||||
|
||||
Lean has a relatively small kernel.
|
||||
Several independent checkers have been implemented for Lean 3. Two of them are
|
||||
[tc](https://github.com/leanprover/tc) and [trepplein](https://github.com/gebner/trepplein).
|
||||
We expect similar independent checkers will be built for Lean 4.
|
||||
|
||||
### Should I open a new issue?
|
||||
|
||||
We use [GitHub](https://github.com/leanprover/lean4/issues) to track bugs and new features.
|
||||
Bug reports are always welcome, but nitpicking issues are not (e.g., the error message is confusing).
|
||||
See also our [contribution guidelines](https://github.com/leanprover/lean4/blob/master/CONTRIBUTING.md).
|
||||
|
||||
### Is it Lean, LEAN, or L∃∀N?
|
||||
|
||||
We always use "Lean" in writing.
|
||||
When specifying a major version number, we append it together with a single space: Lean 4.
|
||||
151
doc/flake.lock
generated
151
doc/flake.lock
generated
@@ -1,151 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"alectryon": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1654613606,
|
||||
"narHash": "sha256-IGCn1PzTyw8rrwmyWUiw3Jo/dyZVGkMslnHYW7YB8yk=",
|
||||
"owner": "Kha",
|
||||
"repo": "alectryon",
|
||||
"rev": "c3b16f650665745e1da4ddfcc048d3bd639f71d5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "Kha",
|
||||
"ref": "typeid",
|
||||
"repo": "alectryon",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"lean": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-old": "nixpkgs-old"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 0,
|
||||
"narHash": "sha256-saRAtQ6VautVXKDw1XH35qwP0KEBKTKZbg/TRa4N9Vw=",
|
||||
"path": "../.",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"path": "../.",
|
||||
"type": "path"
|
||||
}
|
||||
},
|
||||
"leanInk": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1704976501,
|
||||
"narHash": "sha256-FSBUsbX0HxakSnYRYzRBDN2YKmH9EkA0q9p7TSPEJTI=",
|
||||
"owner": "leanprover",
|
||||
"repo": "LeanInk",
|
||||
"rev": "51821e3c2c032c88e4b2956483899d373ec090c4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "leanprover",
|
||||
"ref": "refs/pull/57/merge",
|
||||
"repo": "LeanInk",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"mdBook": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1660074464,
|
||||
"narHash": "sha256-W30G7AeWBjdJE/CQZJU5vJjaDGZtpmxEKNMEvaYtuF8=",
|
||||
"owner": "leanprover",
|
||||
"repo": "mdBook",
|
||||
"rev": "9321c10c502cd59eea8afc4325a84eab3ddf9391",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "leanprover",
|
||||
"repo": "mdBook",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1710889954,
|
||||
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-old": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1581379743,
|
||||
"narHash": "sha256-i1XCn9rKuLjvCdu2UeXKzGLF6IuQePQKFt4hEKRU5oc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-19.03",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"alectryon": "alectryon",
|
||||
"flake-utils": [
|
||||
"lean",
|
||||
"flake-utils"
|
||||
],
|
||||
"lean": "lean",
|
||||
"leanInk": "leanInk",
|
||||
"mdBook": "mdBook"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
{
|
||||
description = "Lean documentation";
|
||||
|
||||
inputs.lean.url = path:../.;
|
||||
inputs.flake-utils.follows = "lean/flake-utils";
|
||||
inputs.mdBook = {
|
||||
url = "github:leanprover/mdBook";
|
||||
flake = false;
|
||||
};
|
||||
inputs.alectryon = {
|
||||
url = "github:Kha/alectryon/typeid";
|
||||
flake = false;
|
||||
};
|
||||
inputs.leanInk = {
|
||||
url = "github:leanprover/LeanInk/refs/pull/57/merge";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
outputs = inputs@{ self, ... }: inputs.flake-utils.lib.eachDefaultSystem (system:
|
||||
with inputs.lean.packages.${system}.deprecated; with nixpkgs;
|
||||
let
|
||||
doc-src = lib.sourceByRegex ../. ["doc.*" "tests(/lean(/beginEndAsMacro.lean)?)?"];
|
||||
in {
|
||||
packages = rec {
|
||||
lean-mdbook = mdbook.overrideAttrs (drv: rec {
|
||||
name = "lean-${mdbook.name}";
|
||||
src = inputs.mdBook;
|
||||
cargoDeps = drv.cargoDeps.overrideAttrs (_: {
|
||||
inherit src;
|
||||
outputHash = "sha256-CO3A9Kpp4sIvkT9X3p+GTidazk7Fn4jf0AP2PINN44A=";
|
||||
});
|
||||
doCheck = false;
|
||||
});
|
||||
book = stdenv.mkDerivation {
|
||||
name ="lean-doc";
|
||||
src = doc-src;
|
||||
buildInputs = [ lean-mdbook ];
|
||||
buildCommand = ''
|
||||
mkdir $out
|
||||
# necessary for `additional-css`...?
|
||||
cp -r --no-preserve=mode $src/doc/* .
|
||||
# overwrite stub .lean.md files
|
||||
cp -r ${inked}/* .
|
||||
mdbook build -d $out
|
||||
'';
|
||||
};
|
||||
leanInk = (buildLeanPackage {
|
||||
name = "Main";
|
||||
src = inputs.leanInk;
|
||||
deps = [ (buildLeanPackage {
|
||||
name = "LeanInk";
|
||||
src = inputs.leanInk;
|
||||
}) ];
|
||||
executableName = "leanInk";
|
||||
linkFlags = ["-rdynamic"];
|
||||
}).executable;
|
||||
alectryon = python3Packages.buildPythonApplication {
|
||||
name = "alectryon";
|
||||
src = inputs.alectryon;
|
||||
propagatedBuildInputs =
|
||||
[ leanInk lean-all ] ++
|
||||
# https://github.com/cpitclaudel/alectryon/blob/master/setup.cfg
|
||||
(with python3Packages; [ pygments dominate beautifulsoup4 docutils ]);
|
||||
doCheck = false;
|
||||
};
|
||||
renderLeanMod = mod: mod.overrideAttrs (final: prev: {
|
||||
name = "${prev.name}.md";
|
||||
buildInputs = prev.buildInputs ++ [ alectryon ];
|
||||
outputs = [ "out" ];
|
||||
buildCommand = ''
|
||||
dir=$(dirname $relpath)
|
||||
mkdir -p $dir out/$dir
|
||||
if [ -d $src ]; then cp -r $src/. $dir/; else cp $src $leanPath; fi
|
||||
alectryon --frontend lean4+markup $leanPath --backend webpage -o $out/$leanPath.md
|
||||
'';
|
||||
});
|
||||
renderPackage = pkg: symlinkJoin {
|
||||
name = "${pkg.name}-mds";
|
||||
paths = map renderLeanMod (lib.attrValues pkg.mods);
|
||||
};
|
||||
literate = buildLeanPackage {
|
||||
name = "literate";
|
||||
src = ./.;
|
||||
roots = [
|
||||
{ mod = "examples"; glob = "submodules"; }
|
||||
];
|
||||
};
|
||||
inked = renderPackage literate;
|
||||
doc = book;
|
||||
};
|
||||
defaultPackage = self.packages.${system}.doc;
|
||||
});
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
Functional Programming in Lean
|
||||
=======================
|
||||
|
||||
The goal of [this book](https://lean-lang.org/functional_programming_in_lean/) is to be an accessible introduction to using Lean 4 as a programming language.
|
||||
It should be useful both to people who want to use Lean as a general-purpose programming language and to mathematicians who want to develop larger-scale proof automation but do not have a background in functional programming.
|
||||
It does not assume any background with functional programming, though it's probably not a good first book on programming in general.
|
||||
New content will be added once per month until it's done.
|
||||
145
doc/funabst.md
145
doc/funabst.md
@@ -1,145 +0,0 @@
|
||||
## Function Abstraction and Evaluation
|
||||
|
||||
We have seen that if we have ``m n : Nat``, then we have ``(m, n) : Nat × Nat``.
|
||||
This gives us a way of creating pairs of natural numbers.
|
||||
Conversely, if we have ``p : Nat × Nat``, then
|
||||
we have ``p.1 : Nat`` and ``p.2 : Nat``.
|
||||
This gives us a way of "using" a pair, by extracting its two components.
|
||||
|
||||
We already know how to "use" a function ``f : α → β``, namely,
|
||||
we can apply it to an element ``a : α`` to obtain ``f a : β``.
|
||||
But how do we create a function from another expression?
|
||||
|
||||
The companion to application is a process known as "lambda abstraction."
|
||||
Suppose that giving a variable ``x : α`` we can construct an expression ``t : β``.
|
||||
Then the expression ``fun (x : α) => t``, or, equivalently, ``λ (x : α) => t``, is an object of type ``α → β``.
|
||||
Think of this as the function from ``α`` to ``β`` which maps any value ``x`` to the value ``t``,
|
||||
which may depend on ``x``.
|
||||
|
||||
```lean
|
||||
#check fun (x : Nat) => x + 5
|
||||
#check λ (x : Nat) => x + 5
|
||||
#check fun x : Nat => x + 5
|
||||
#check λ x : Nat => x + 5
|
||||
```
|
||||
|
||||
Here are some more examples:
|
||||
|
||||
```lean
|
||||
constant f : Nat → Nat
|
||||
constant h : Nat → Bool → Nat
|
||||
|
||||
#check fun x : Nat => fun y : Bool => h (f x) y -- Nat → Bool → Nat
|
||||
#check fun (x : Nat) (y : Bool) => h (f x) y -- Nat → Bool → Nat
|
||||
#check fun x y => h (f x) y -- Nat → Bool → Nat
|
||||
```
|
||||
|
||||
Lean interprets the final three examples as the same expression; in the last expression,
|
||||
Lean infers the type of ``x`` and ``y`` from the types of ``f`` and ``h``.
|
||||
|
||||
Some mathematically common examples of operations of functions can be described in terms of lambda abstraction:
|
||||
|
||||
```lean
|
||||
constant f : Nat → String
|
||||
constant g : String → Bool
|
||||
constant b : Bool
|
||||
|
||||
#check fun x : Nat => x -- Nat → Nat
|
||||
#check fun x : Nat => b -- Nat → Bool
|
||||
#check fun x : Nat => g (f x) -- Nat → Bool
|
||||
#check fun x => g (f x) -- Nat → Bool
|
||||
```
|
||||
|
||||
Think about what these expressions mean. The expression ``fun x : Nat => x`` denotes the identity function on ``Nat``,
|
||||
the expression ``fun x : α => b`` denotes the constant function that always returns ``b``,
|
||||
and ``fun x : Nat => g (f x)``, denotes the composition of ``f`` and ``g``.
|
||||
We can, in general, leave off the type annotation on a variable and let Lean infer it for us.
|
||||
So, for example, we can write ``fun x => g (f x)`` instead of ``fun x : Nat => g (f x)``.
|
||||
|
||||
We can abstract over the constants `f` and `g` in the previous definitions:
|
||||
|
||||
```lean
|
||||
#check fun (g : String → Bool) (f : Nat → String) (x : Nat) => g (f x)
|
||||
-- (String → Bool) → (Nat → String) → Nat → Bool
|
||||
```
|
||||
|
||||
We can also abstract over types:
|
||||
|
||||
```lean
|
||||
#check fun (α β γ : Type) (g : β → γ) (f : α → β) (x : α) => g (f x)
|
||||
```
|
||||
The last expression, for example, denotes the function that takes three types, ``α``, ``β``, and ``γ``, and two functions, ``g : β → γ`` and ``f : α → β``, and returns the composition of ``g`` and ``f``. (Making sense of the type of this function requires an understanding of dependent products, which we will explain below.) Within a lambda expression ``fun x : α => t``, the variable ``x`` is a "bound variable": it is really a placeholder, whose "scope" does not extend beyond ``t``.
|
||||
For example, the variable ``b`` in the expression ``fun (b : β) (x : α) => b`` has nothing to do with the constant ``b`` declared earlier.
|
||||
In fact, the expression denotes the same function as ``fun (u : β) (z : α), u``. Formally, the expressions that are the same up to a renaming of bound variables are called *alpha equivalent*, and are considered "the same." Lean recognizes this equivalence.
|
||||
|
||||
Notice that applying a term ``t : α → β`` to a term ``s : α`` yields an expression ``t s : β``.
|
||||
Returning to the previous example and renaming bound variables for clarity, notice the types of the following expressions:
|
||||
|
||||
```lean
|
||||
#check (fun x : Nat => x) 1 -- Nat
|
||||
#check (fun x : Nat => true) 1 -- Bool
|
||||
|
||||
constant f : Nat → String
|
||||
constant g : String → Bool
|
||||
|
||||
#check
|
||||
(fun (α β γ : Type) (g : β → γ) (f : α → β) (x : α) => g (f x)) Nat String Bool g f 0
|
||||
-- Bool
|
||||
```
|
||||
|
||||
As expected, the expression ``(fun x : Nat => x) 1`` has type ``Nat``.
|
||||
In fact, more should be true: applying the expression ``(fun x : Nat => x)`` to ``1`` should "return" the value ``1``. And, indeed, it does:
|
||||
|
||||
```lean
|
||||
#reduce (fun x : Nat => x) 1 -- 1
|
||||
#reduce (fun x : Nat => true) 1 -- true
|
||||
|
||||
constant f : Nat → String
|
||||
constant g : String → Bool
|
||||
|
||||
#reduce
|
||||
(fun (α β γ : Type) (g : β → γ) (f : α → β) (x : α) => g (f x)) Nat String Bool g f 0
|
||||
-- g (f 0)
|
||||
```
|
||||
|
||||
The command ``#reduce`` tells Lean to evaluate an expression by *reducing* it to its normal form,
|
||||
which is to say, carrying out all the computational reductions that are sanctioned by its kernel.
|
||||
The process of simplifying an expression ``(fun x => t) s`` to ``t[s/x]`` -- that is, ``t`` with ``s`` substituted for the variable ``x`` --
|
||||
is known as *beta reduction*, and two terms that beta reduce to a common term are called *beta equivalent*.
|
||||
But the ``#reduce`` command carries out other forms of reduction as well:
|
||||
|
||||
```lean
|
||||
constant m : Nat
|
||||
constant n : Nat
|
||||
constant b : Bool
|
||||
|
||||
#reduce (m, n).1 -- m
|
||||
#reduce (m, n).2 -- n
|
||||
|
||||
#reduce true && false -- false
|
||||
#reduce false && b -- false
|
||||
#reduce b && false -- Bool.rec false false b
|
||||
|
||||
#reduce n + 0 -- n
|
||||
#reduce n + 2 -- Nat.succ (Nat.succ n)
|
||||
#reduce 2 + 3 -- 5
|
||||
```
|
||||
|
||||
We explain later how these terms are evaluated.
|
||||
For now, we only wish to emphasize that this is an important feature of dependent type theory:
|
||||
every term has a computational behavior, and supports a notion of reduction, or *normalization*.
|
||||
In principle, two terms that reduce to the same value are called *definitionally equal*.
|
||||
They are considered "the same" by Lean's type checker, and Lean does its best to recognize and support these identifications.
|
||||
The `#reduce` command is mainly useful to understand why two terms are considered the same.
|
||||
|
||||
Lean is also a programming language. It has a compiler to native code and an interpreter.
|
||||
You can use the command `#eval` to execute expressions, and it is the preferred way of testing your functions.
|
||||
Note that `#eval` and `#reduce` are *not* equivalent. The command `#eval` first compiles Lean expressions
|
||||
into an intermediate representation (IR) and then uses an interpreter to execute the generated IR.
|
||||
Some builtin types (e.g., `Nat`, `String`, `Array`) have a more efficient representation in the IR.
|
||||
The IR has support for using foreign functions that are opaque to Lean.
|
||||
|
||||
In contrast, the ``#reduce`` command relies on a reduction engine similar to the one used in Lean's trusted kernel,
|
||||
the part of Lean that is responsible for checking and verifying the correctness of expressions and proofs.
|
||||
It is less efficient than ``#eval``, and treats all foreign functions as opaque constants.
|
||||
We later discuss other differences between the two commands.
|
||||
1209
doc/highlight.js
1209
doc/highlight.js
File diff suppressed because one or more lines are too long
@@ -1,66 +0,0 @@
|
||||
## Introducing Definitions
|
||||
|
||||
The ``def`` command provides one important way of defining new objects.
|
||||
|
||||
```lean
|
||||
|
||||
def foo : (Nat → Nat) → Nat :=
|
||||
fun f => f 0
|
||||
|
||||
#check foo -- (Nat → Nat) → Nat
|
||||
#print foo
|
||||
```
|
||||
|
||||
We can omit the type when Lean has enough information to infer it:
|
||||
|
||||
```lean
|
||||
def foo :=
|
||||
fun (f : Nat → Nat) => f 0
|
||||
```
|
||||
|
||||
The general form of a definition is ``def foo : α := bar``. Lean can usually infer the type ``α``, but it is often a good idea to write it explicitly.
|
||||
This clarifies your intention, and Lean will flag an error if the right-hand side of the definition does not have the right type.
|
||||
|
||||
Lean also allows us to use an alternative format that puts the abstracted variables before the colon and omits the lambda:
|
||||
```lean
|
||||
def double (x : Nat) : Nat :=
|
||||
x + x
|
||||
|
||||
#print double
|
||||
#check double 3
|
||||
#reduce double 3 -- 6
|
||||
#eval double 3 -- 6
|
||||
|
||||
def square (x : Nat) :=
|
||||
x * x
|
||||
|
||||
#print square
|
||||
#check square 3
|
||||
#reduce square 3 -- 9
|
||||
#eval square 3 -- 9
|
||||
|
||||
def doTwice (f : Nat → Nat) (x : Nat) : Nat :=
|
||||
f (f x)
|
||||
|
||||
#eval doTwice double 2 -- 8
|
||||
```
|
||||
|
||||
These definitions are equivalent to the following:
|
||||
|
||||
```lean
|
||||
def double : Nat → Nat :=
|
||||
fun x => x + x
|
||||
|
||||
def square : Nat → Nat :=
|
||||
fun x => x * x
|
||||
|
||||
def doTwice : (Nat → Nat) → Nat → Nat :=
|
||||
fun f x => f (f x)
|
||||
```
|
||||
|
||||
We can even use this approach to specify arguments that are types:
|
||||
|
||||
```lean
|
||||
def compose (α β γ : Type) (g : β → γ) (f : α → β) (x : α) : γ :=
|
||||
g (f x)
|
||||
```
|
||||
@@ -1,369 +0,0 @@
|
||||
# Significant changes from Lean 3
|
||||
|
||||
Lean 4 is not backward compatible with Lean 3.
|
||||
We have rewritten most of the system, and took the opportunity to cleanup the syntax,
|
||||
metaprogramming framework, and elaborator. In this section, we go over the most significant
|
||||
changes.
|
||||
|
||||
## Lambda expressions
|
||||
|
||||
We do not use `,` anymore to separate the binders from the lambda expression body.
|
||||
The Lean 3 syntax for lambda expressions was unconventional, and `,` has been overused in Lean 3.
|
||||
For example, we believe a list of lambda expressions is quite confusing in Lean 3, since `,` is used
|
||||
to separate the elements of a list, and in the lambda expression itself. We now use `=>` as the separator,
|
||||
as an example, `fun x => x` is the identity function. One may still use the symbol `λ` as a shorthand for `fun`.
|
||||
The lambda expression notation has many new features that are not supported in Lean 3.
|
||||
|
||||
## Pattern matching
|
||||
|
||||
In Lean 4, one can easily create new notation that abbreviates commonly used idioms. One of them is a
|
||||
`fun` followed by a `match`. In the following examples, we define a few functions using `fun`+`match` notation.
|
||||
|
||||
```lean
|
||||
# namespace ex1
|
||||
def Prod.str : Nat × Nat → String :=
|
||||
fun (a, b) => "(" ++ toString a ++ ", " ++ toString b ++ ")"
|
||||
|
||||
structure Point where
|
||||
x : Nat
|
||||
y : Nat
|
||||
z : Nat
|
||||
|
||||
def Point.addX : Point → Point → Nat :=
|
||||
fun { x := a, .. } { x := b, .. } => a+b
|
||||
|
||||
def Sum.str : Option Nat → String :=
|
||||
fun
|
||||
| some a => "some " ++ toString a
|
||||
| none => "none"
|
||||
# end ex1
|
||||
```
|
||||
|
||||
## Implicit lambdas
|
||||
|
||||
In Lean 3 stdlib, we find many [instances](https://github.com/leanprover/lean/blob/master/library/init/category/reader.lean#L39) of the dreadful `@`+`_` idiom.
|
||||
It is often used when the expected type is a function type with implicit arguments,
|
||||
and we have a constant (`reader_t.pure` in the example) which also takes implicit arguments. In Lean 4, the elaborator automatically introduces lambdas
|
||||
for consuming implicit arguments. We are still exploring this feature and analyzing its impact, but the experience so far has been very positive. As an example,
|
||||
here is the example in the link above using Lean 4 implicit lambdas.
|
||||
|
||||
```lean
|
||||
# variable (ρ : Type) (m : Type → Type) [Monad m]
|
||||
instance : Monad (ReaderT ρ m) where
|
||||
pure := ReaderT.pure
|
||||
bind := ReaderT.bind
|
||||
```
|
||||
|
||||
Users can disable the implicit lambda feature by using `@` or writing a lambda expression with `{}` or `[]` binder annotations.
|
||||
Here are few examples
|
||||
|
||||
```lean
|
||||
# namespace ex2
|
||||
def id1 : {α : Type} → α → α :=
|
||||
fun x => x
|
||||
|
||||
def listId : List ({α : Type} → α → α) :=
|
||||
(fun x => x) :: []
|
||||
|
||||
-- In this example, implicit lambda introduction has been disabled because
|
||||
-- we use `@` before `fun`
|
||||
def id2 : {α : Type} → α → α :=
|
||||
@fun α (x : α) => id1 x
|
||||
|
||||
def id3 : {α : Type} → α → α :=
|
||||
@fun α x => id1 x
|
||||
|
||||
def id4 : {α : Type} → α → α :=
|
||||
fun x => id1 x
|
||||
|
||||
-- In this example, implicit lambda introduction has been disabled
|
||||
-- because we used the binder annotation `{...}`
|
||||
def id5 : {α : Type} → α → α :=
|
||||
fun {α} x => id1 x
|
||||
# end ex2
|
||||
```
|
||||
|
||||
## Sugar for simple functions
|
||||
|
||||
In Lean 3, we can create simple functions from infix operators by using parentheses. For example, `(+1)` is sugar for `fun x, x + 1`. In Lean 4, we generalize this notation using `·` as a placeholder. Here are a few examples:
|
||||
|
||||
```lean
|
||||
# namespace ex3
|
||||
#check (· + 1)
|
||||
-- fun a => a + 1
|
||||
#check (2 - ·)
|
||||
-- fun a => 2 - a
|
||||
#eval [1, 2, 3, 4, 5].foldl (·*·) 1
|
||||
-- 120
|
||||
|
||||
def f (x y z : Nat) :=
|
||||
x + y + z
|
||||
|
||||
#check (f · 1 ·)
|
||||
-- fun a b => f a 1 b
|
||||
|
||||
#eval [(1, 2), (3, 4), (5, 6)].map (·.1)
|
||||
-- [1, 3, 5]
|
||||
# end ex3
|
||||
```
|
||||
|
||||
As in Lean 3, the notation is activated using parentheses, and the lambda abstraction is created by collecting the nested `·`s.
|
||||
The collection is interrupted by nested parentheses. In the following example, two different lambda expressions are created.
|
||||
|
||||
```lean
|
||||
#check (Prod.mk · (· + 1))
|
||||
-- fun a => (a, fun b => b + 1)
|
||||
```
|
||||
|
||||
## Function applications
|
||||
|
||||
In Lean 4, we have support for named arguments.
|
||||
Named arguments enable you to specify an argument for a parameter by matching the argument with
|
||||
its name rather than with its position in the parameter list.
|
||||
If you don't remember the order of the parameters but know their names,
|
||||
you can send the arguments in any order. You may also provide the value for an implicit parameter when
|
||||
Lean failed to infer it. Named arguments also improve the readability of your code by identifying what
|
||||
each argument represents.
|
||||
|
||||
```lean
|
||||
def sum (xs : List Nat) :=
|
||||
xs.foldl (init := 0) (·+·)
|
||||
|
||||
#eval sum [1, 2, 3, 4]
|
||||
-- 10
|
||||
|
||||
example {a b : Nat} {p : Nat → Nat → Nat → Prop} (h₁ : p a b b) (h₂ : b = a)
|
||||
: p a a b :=
|
||||
Eq.subst (motive := fun x => p a x b) h₂ h₁
|
||||
```
|
||||
In the following examples, we illustrate the interaction between named and default arguments.
|
||||
|
||||
```lean
|
||||
def f (x : Nat) (y : Nat := 1) (w : Nat := 2) (z : Nat) :=
|
||||
x + y + w - z
|
||||
|
||||
example (x z : Nat) : f (z := z) x = x + 1 + 2 - z := rfl
|
||||
|
||||
example (x z : Nat) : f x (z := z) = x + 1 + 2 - z := rfl
|
||||
|
||||
example (x y : Nat) : f x y = fun z => x + y + 2 - z := rfl
|
||||
|
||||
example : f = (fun x z => x + 1 + 2 - z) := rfl
|
||||
|
||||
example (x : Nat) : f x = fun z => x + 1 + 2 - z := rfl
|
||||
|
||||
example (y : Nat) : f (y := 5) = fun x z => x + 5 + 2 - z := rfl
|
||||
|
||||
def g {α} [Add α] (a : α) (b? : Option α := none) (c : α) : α :=
|
||||
match b? with
|
||||
| none => a + c
|
||||
| some b => a + b + c
|
||||
|
||||
variable {α} [Add α]
|
||||
|
||||
example : g = fun (a c : α) => a + c := rfl
|
||||
|
||||
example (x : α) : g (c := x) = fun (a : α) => a + x := rfl
|
||||
|
||||
example (x : α) : g (b? := some x) = fun (a c : α) => a + x + c := rfl
|
||||
|
||||
example (x : α) : g x = fun (c : α) => x + c := rfl
|
||||
|
||||
example (x y : α) : g x y = fun (c : α) => x + y + c := rfl
|
||||
```
|
||||
|
||||
In Lean 4, we can use `..` to provide missing explicit arguments as `_`.
|
||||
This feature combined with named arguments is useful for writing patterns. Here is an example:
|
||||
```lean
|
||||
inductive Term where
|
||||
| var (name : String)
|
||||
| num (val : Nat)
|
||||
| add (fn : Term) (arg : Term)
|
||||
| lambda (name : String) (type : Term) (body : Term)
|
||||
|
||||
def getBinderName : Term → Option String
|
||||
| Term.lambda (name := n) .. => some n
|
||||
| _ => none
|
||||
|
||||
def getBinderType : Term → Option Term
|
||||
| Term.lambda (type := t) .. => some t
|
||||
| _ => none
|
||||
```
|
||||
Ellipsis are also useful when explicit argument can be automatically inferred by Lean, and we want
|
||||
to avoid a sequence of `_`s.
|
||||
```lean
|
||||
example (f : Nat → Nat) (a b c : Nat) : f (a + b + c) = f (a + (b + c)) :=
|
||||
congrArg f (Nat.add_assoc ..)
|
||||
```
|
||||
|
||||
In Lean 4, writing `f(x)` in place of `f x` is no longer allowed, you must use whitespace between the function and its arguments (e.g., `f (x)`).
|
||||
|
||||
## Dependent function types
|
||||
|
||||
Given `α : Type` and `β : α → Type`, `(x : α) → β x` denotes the type of functions `f` with the property that,
|
||||
for each `a : α`, `f a` is an element of `β a`. In other words, the type of the value returned by `f` depends on its input.
|
||||
We say `(x : α) → β x` is a dependent function type. In Lean 3, we write the dependent function type `(x : α) → β x` using
|
||||
one of the following three equivalent notations:
|
||||
`forall x : α, β x` or `∀ x : α, β x` or `Π x : α, β x`.
|
||||
The first two were intended to be used for writing propositions, and the latter for writing code.
|
||||
Although the notation `Π x : α, β x` has historical significance, we have removed it from Lean 4 because
|
||||
it is awkward to use and often confuses new users. We can still write `forall x : α, β x` and `∀ x : α, β x`.
|
||||
|
||||
```lean
|
||||
#check forall (α : Type), α → α
|
||||
#check ∀ (α : Type), α → α
|
||||
#check ∀ α : Type, α → α
|
||||
#check ∀ α, α → α
|
||||
#check (α : Type) → α → α
|
||||
#check {α : Type} → (a : Array α) → (i : Nat) → i < a.size → α
|
||||
#check {α : Type} → [ToString α] → α → String
|
||||
#check forall {α : Type} (a : Array α) (i : Nat), i < a.size → α
|
||||
#check {α β : Type} → α → β → α × β
|
||||
```
|
||||
|
||||
## The `meta` keyword
|
||||
|
||||
In Lean 3, the keyword `meta` is used to mark definitions that can use primitives implemented in C/C++.
|
||||
These metadefinitions can also call themselves recursively, relaxing the termination
|
||||
restriction imposed by ordinary type theory. Metadefinitions may also use unsafe primitives such as
|
||||
`eval_expr (α : Type u) [reflected α] : expr → tactic α`, or primitives that break referential transparency
|
||||
`tactic.unsafe_run_io`.
|
||||
|
||||
The keyword `meta` has been currently removed from Lean 4. However, we may re-introduce it in the future,
|
||||
but with a much more limited purpose: marking meta code that should not be included in the executables produced by Lean.
|
||||
|
||||
The keyword `constant` has been deleted in Lean 4, and `axiom` should be used instead. In Lean 4, the new command `opaque` is used to define an opaque definition. Here are two simple examples:
|
||||
```lean
|
||||
# namespace meta1
|
||||
opaque x : Nat := 1
|
||||
-- The following example will not type check since `x` is opaque
|
||||
-- example : x = 1 := rfl
|
||||
|
||||
-- We can evaluate `x`
|
||||
#eval x
|
||||
-- 1
|
||||
|
||||
-- When no value is provided, the elaborator tries to build one automatically for us
|
||||
-- using the `Inhabited` type class
|
||||
opaque y : Nat
|
||||
# end meta1
|
||||
```
|
||||
|
||||
We can instruct Lean to use a foreign function as the implementation for any definition
|
||||
using the attribute `@[extern "foreign_function"]`. It is the user's responsibility to ensure the
|
||||
foreign implementation is correct.
|
||||
However, a user mistake here will only impact the code generated by Lean, and
|
||||
it will **not** compromise the logical soundness of the system.
|
||||
That is, you cannot prove `False` using the `@[extern]` attribute.
|
||||
We use `@[extern]` with definitions when we want to provide a reference implementation in Lean
|
||||
that can be used for reasoning. When we write a definition such as
|
||||
```lean
|
||||
@[extern "lean_nat_add"]
|
||||
def add : Nat → Nat → Nat
|
||||
| a, Nat.zero => a
|
||||
| a, Nat.succ b => Nat.succ (add a b)
|
||||
```
|
||||
Lean assumes that the foreign function `lean_nat_add` implements the reference implementation above.
|
||||
|
||||
The `unsafe` keyword allows us to define functions using unsafe features such as general recursion,
|
||||
and arbitrary type casting. Regular (safe) functions cannot directly use `unsafe` ones since it would
|
||||
compromise the logical soundness of the system. As in regular programming languages, programs written
|
||||
using unsafe features may crash at runtime. Here are a few unsafe examples:
|
||||
```lean
|
||||
unsafe def unsound : False :=
|
||||
unsound
|
||||
|
||||
#check @unsafeCast
|
||||
-- {α : Type _} → {β : Type _} → α → β
|
||||
|
||||
unsafe def nat2String (x : Nat) : String :=
|
||||
unsafeCast x
|
||||
|
||||
-- The following definition doesn't type check because it is not marked as `unsafe`
|
||||
-- def nat2StringSafe (x : Nat) : String :=
|
||||
-- unsafeCast x
|
||||
```
|
||||
|
||||
The `unsafe` keyword is particularly useful when we want to take advantage of an implementation detail of the
|
||||
Lean execution runtime. For example, we cannot prove in Lean that arrays have a maximum size, but
|
||||
the runtime used to execute Lean programs guarantees that an array cannot have more than 2^64 (2^32) elements
|
||||
in a 64-bit (32-bit) machine. We can take advantage of this fact to provide a more efficient implementation for
|
||||
array functions. However, the efficient version would not be very useful if it can only be used in
|
||||
unsafe code. Thus, Lean 4 provides the attribute `@[implemented_by functionName]`. The idea is to provide
|
||||
an unsafe (and potentially more efficient) version of a safe definition or constant. The function `f`
|
||||
at the attribute `@[implemented_by f]` is very similar to an extern/foreign function,
|
||||
the key difference is that it is implemented in Lean itself. Again, the logical soundness of the system
|
||||
cannot be compromised by using the attribute `implemented_by`, but if the implementation is incorrect your
|
||||
program may crash at runtime. In the following example, we define `withPtrUnsafe a k h` which
|
||||
executes `k` using the memory address where `a` is stored in memory. The argument `h` is proof
|
||||
that `k` is a constant function. Then, we "seal" this unsafe implementation at `withPtr`. The proof `h`
|
||||
ensures the reference implementation `k 0` is correct. For more information, see the article
|
||||
"Sealing Pointer-Based Optimizations Behind Pure Functions".
|
||||
```lean
|
||||
unsafe
|
||||
def withPtrUnsafe {α β : Type} (a : α) (k : USize → β) (h : ∀ u, k u = k 0) : β :=
|
||||
k (ptrAddrUnsafe a)
|
||||
|
||||
@[implemented_by withPtrUnsafe]
|
||||
def withPtr {α β : Type} (a : α) (k : USize → β) (h : ∀ u, k u = k 0) : β :=
|
||||
k 0
|
||||
```
|
||||
|
||||
General recursion is very useful in practice, and it would be impossible to implement Lean 4 without it.
|
||||
The keyword `partial` implements a very simple and efficient approach for supporting general recursion.
|
||||
Simplicity was key here because of the bootstrapping problem. That is, we had to implement Lean in Lean before
|
||||
many of its features were implemented (e.g., the tactic framework or support for wellfounded recursion).
|
||||
Another requirement for us was performance. Functions tagged with `partial` should be as efficient as the ones implemented in mainstream functional programming
|
||||
languages such as OCaml. When the `partial` keyword is used, Lean generates an auxiliary `unsafe` definition that
|
||||
uses general recursion, and then defines an opaque constant that is implemented by this auxiliary definition.
|
||||
This is very simple, efficient, and is sufficient for users that want to use Lean as a regular programming language.
|
||||
A `partial` definition cannot use unsafe features such as `unsafeCast` and `ptrAddrUnsafe`, and it can only be used to
|
||||
implement types we already known to be inhabited. Finally, since we "seal" the auxiliary definition using an opaque
|
||||
constant, we cannot reason about `partial` definitions.
|
||||
|
||||
We are aware that proof assistants such as Isabelle provide a framework for defining partial functions that does not
|
||||
prevent users from proving properties about them. This kind of framework can be implemented in Lean 4. Actually,
|
||||
it can be implemented by users since Lean 4 is an extensible system. The developers current have no plans to implement
|
||||
this kind of support for Lean 4. However, we remark that users can implement it using a function that traverses
|
||||
the auxiliary unsafe definition generated by Lean, and produces a safe one using an approach similar to the one used in Isabelle.
|
||||
|
||||
```lean
|
||||
# namespace partial1
|
||||
partial def f (x : Nat) : IO Unit := do
|
||||
IO.println x
|
||||
if x < 100 then
|
||||
f (x+1)
|
||||
|
||||
#eval f 98
|
||||
# end partial1
|
||||
```
|
||||
|
||||
## Library changes
|
||||
|
||||
These are changes to the library which may trip up Lean 3 users:
|
||||
|
||||
- `List` is no longer a monad.
|
||||
|
||||
## Style changes
|
||||
|
||||
Coding style changes have also been made:
|
||||
|
||||
- Term constants and variables are now `lowerCamelCase` rather than `snake_case`
|
||||
- Type constants are now `UpperCamelCase`, eg `Nat`, `List`. Type variables are still lower case greek letters. Functors are still lower case latin `(m : Type → Type) [Monad m]`.
|
||||
- When defining typeclasses, prefer not to use "has". Eg `ToString` or `Add` instead of `HasToString` or `HasAdd`.
|
||||
- Prefer `return` to `pure` in monad expressions.
|
||||
- Pipes `<|` are preferred to dollars `$` for function application.
|
||||
- Declaration bodies should always be indented:
|
||||
```lean
|
||||
inductive Hello where
|
||||
| foo
|
||||
| bar
|
||||
|
||||
structure Point where
|
||||
x : Nat
|
||||
y : Nat
|
||||
|
||||
def Point.addX : Point → Point → Nat :=
|
||||
fun { x := a, .. } { x := b, .. } => a + b
|
||||
```
|
||||
- In structures and typeclass definitions, prefer `where` to `:=` and don't surround fields with parentheses. (Shown in `Point` above)
|
||||
@@ -1,180 +0,0 @@
|
||||
Lexical Structure
|
||||
=================
|
||||
|
||||
This section describes the detailed lexical structure of the Lean
|
||||
language.
|
||||
|
||||
A Lean program consists of a stream of UTF-8 tokens where each token
|
||||
is one of the following:
|
||||
|
||||
```
|
||||
token: symbol | command | ident | string | raw_string | char | numeral |
|
||||
: decimal | doc_comment | mod_doc_comment | field_notation
|
||||
```
|
||||
|
||||
Tokens can be separated by the whitespace characters space, tab, line
|
||||
feed, and carriage return, as well as comments. Single-line comments
|
||||
start with ``--``, whereas multi-line comments are enclosed by ``/-``
|
||||
and ``-/`` and can be nested.
|
||||
|
||||
Symbols and Commands
|
||||
====================
|
||||
|
||||
.. *(TODO: list built-in symbols and command tokens?)*
|
||||
|
||||
Symbols are static tokens that are used in term notations and
|
||||
commands. They can be both keyword-like (e.g. the `have
|
||||
<structured_proofs>` keyword) or use arbitrary Unicode characters.
|
||||
|
||||
Command tokens are static tokens that prefix any top-level declaration
|
||||
or action. They are usually keyword-like, with transitory commands
|
||||
like `#print <instructions>` prefixed by the ``#`` character. The set
|
||||
of built-in commands is listed in [Other Commands](./other_commands.md).
|
||||
|
||||
Users can dynamically extend the sets of both symbols (via the
|
||||
commands listed in [Quoted Symbols](#quoted-symbols) and command
|
||||
tokens (via the `[user_command] <attributes>` attribute).
|
||||
|
||||
.. _identifiers:
|
||||
|
||||
Identifiers
|
||||
===========
|
||||
|
||||
An *atomic identifier*, or *atomic name*, is (roughly) an alphanumeric
|
||||
string that does not begin with a numeral. A (hierarchical)
|
||||
*identifier*, or *name*, consists of one or more atomic names
|
||||
separated by periods.
|
||||
|
||||
Parts of atomic names can be escaped by enclosing them in pairs of French double quotes ``«»``.
|
||||
|
||||
```lean
|
||||
def Foo.«bar.baz» := 0 -- name parts ["Foo", "bar.baz"]
|
||||
```
|
||||
|
||||
```
|
||||
ident: atomic_ident | ident "." atomic_ident
|
||||
atomic_ident: atomic_ident_start atomic_ident_rest*
|
||||
atomic_ident_start: letterlike | "_" | escaped_ident_part
|
||||
letterlike: [a-zA-Z] | greek | coptic | letterlike_symbols
|
||||
greek: <[α-ωΑ-Ωἀ-῾] except for [λΠΣ]>
|
||||
coptic: [ϊ-ϻ]
|
||||
letterlike_symbols: [℀-⅏]
|
||||
escaped_ident_part: "«" [^«»\r\n\t]* "»"
|
||||
atomic_ident_rest: atomic_ident_start | [0-9'ⁿ] | subscript
|
||||
subscript: [₀-₉ₐ-ₜᵢ-ᵪⱼ]
|
||||
```
|
||||
|
||||
String Literals
|
||||
===============
|
||||
|
||||
String literals are enclosed by double quotes (``"``). They may contain line breaks, which are conserved in the string value. Backslash (`\`) is a special escape character which can be used to the following
|
||||
special characters:
|
||||
- `\\` represents an escaped backslash, so this escape causes one backslash to be included in the string.
|
||||
- `\"` puts a double quote in the string.
|
||||
- `\'` puts an apostrophe in the string.
|
||||
- `\n` puts a new line character in the string.
|
||||
- `\t` puts a tab character in the string.
|
||||
- `\xHH` puts the character represented by the 2 digit hexadecimal into the string. For example
|
||||
"this \x26 that" which become "this & that". Values above 0x80 will be interpreted according to the
|
||||
[Unicode table](https://unicode-table.com/en/) so "\xA9 Copyright 2021" is "© Copyright 2021".
|
||||
- `\uHHHH` puts the character represented by the 4 digit hexadecimal into the string, so the following
|
||||
string "\u65e5\u672c" will become "日本" which means "Japan".
|
||||
- `\` followed by a newline and then any amount of whitespace is a "gap" that is equivalent to the empty string,
|
||||
useful for letting a string literal span across multiple lines. Gaps spanning multiple lines can be confusing,
|
||||
so the parser raises an error if the trailing whitespace contains any newlines.
|
||||
|
||||
So the complete syntax is:
|
||||
|
||||
```
|
||||
string : '"' string_item '"'
|
||||
string_item : string_char | char_escape | string_gap
|
||||
string_char : [^"\\]
|
||||
char_escape : "\" ("\" | '"' | "'" | "n" | "t" | "x" hex_char{2} | "u" hex_char{4})
|
||||
hex_char : [0-9a-fA-F]
|
||||
string_gap : "\" newline whitespace*
|
||||
```
|
||||
|
||||
Raw String Literals
|
||||
===================
|
||||
|
||||
Raw string literals are string literals without any escape character processing.
|
||||
They begin with `r##...#"` (with zero or more `#` characters) and end with `"#...##` (with the same number of `#` characters).
|
||||
The contents of a raw string literal may contain `"##..#` so long as the number of `#` characters
|
||||
is less than the number of `#` characters used to begin the raw string literal.
|
||||
|
||||
```
|
||||
raw_string : raw_string_aux(0) | raw_string_aux(1) | raw_string_aux(2) | ...
|
||||
raw_string_aux(n) : 'r' '#'{n} '"' raw_string_item '"' '#'{n}
|
||||
raw_string_item(n) : raw_string_char | raw_string_quote(n)
|
||||
raw_string_char : [^"]
|
||||
raw_string_quote(n) : '"' '#'{0..n-1}
|
||||
```
|
||||
|
||||
Char Literals
|
||||
=============
|
||||
|
||||
Char literals are enclosed by single quotes (``'``).
|
||||
|
||||
```
|
||||
char : "'" char_item "'"
|
||||
char_item : char_char | char_escape
|
||||
char_char : [^'\\]
|
||||
```
|
||||
|
||||
Numeric Literals
|
||||
================
|
||||
|
||||
Numeric literals can be specified in various bases.
|
||||
|
||||
```
|
||||
numeral : numeral10 | numeral2 | numeral8 | numeral16
|
||||
numeral10 : [0-9]+ ("_"+ [0-9]+)*
|
||||
numeral2 : "0" [bB] ("_"* [0-1]+)+
|
||||
numeral8 : "0" [oO] ("_"* [0-7]+)+
|
||||
numeral16 : "0" [xX] ("_"* hex_char+)+
|
||||
```
|
||||
|
||||
Floating point literals are also possible with optional exponent:
|
||||
|
||||
```
|
||||
float : numeral10 "." numeral10? [eE[+-]numeral10]
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
constant w : Int := 55
|
||||
constant x : Nat := 26085
|
||||
constant y : Nat := 0x65E5
|
||||
constant z : Float := 2.548123e-05
|
||||
constant b : Bool := 0b_11_01_10_00
|
||||
```
|
||||
|
||||
Note: that negative numbers are created by applying the "-" negation prefix operator to the number, for example:
|
||||
|
||||
```
|
||||
constant w : Int := -55
|
||||
```
|
||||
|
||||
Doc Comments
|
||||
============
|
||||
|
||||
A special form of comments, doc comments are used to document modules
|
||||
and declarations.
|
||||
|
||||
```
|
||||
doc_comment: "/--" ([^-] | "-" [^/])* "-/"
|
||||
mod_doc_comment: "/-!" ([^-] | "-" [^/])* "-/"
|
||||
```
|
||||
|
||||
Field Notation
|
||||
==============
|
||||
|
||||
Trailing field notation tokens are used in expressions such as
|
||||
``(1+1).to_string``. Note that ``a.toString`` is a single
|
||||
[Identifier](#identifiers), but may be interpreted as a field
|
||||
notation expression by the parser.
|
||||
|
||||
```
|
||||
field_notation: "." ([0-9]+ | atomic_ident)
|
||||
```
|
||||
@@ -1,134 +0,0 @@
|
||||
# Arithmetic as an embedded domain-specific language
|
||||
|
||||
Let's parse another classic grammar, the grammar of arithmetic expressions with
|
||||
addition, multiplication, integers, and variables. In the process, we'll learn
|
||||
how to:
|
||||
|
||||
- Convert identifiers such as `x` into strings within a macro.
|
||||
- add the ability to "escape" the macro context from within the macro. This is useful to interpret identifiers with their _original_ meaning (predefined values)
|
||||
instead of their new meaning within a macro (treat as a symbol).
|
||||
|
||||
Let's begin with the simplest thing possible. We'll define an AST, and use operators `+` and `*` to denote
|
||||
building an arithmetic AST.
|
||||
|
||||
|
||||
Here's the AST that we will be parsing:
|
||||
|
||||
```lean,ignore
|
||||
{{#include metaprogramming-arith.lean:1:5}}
|
||||
```
|
||||
|
||||
We declare a syntax category to describe the grammar that we will be parsing.
|
||||
See that we control the precedence of `+` and `*` by writing `syntax:50` for addition and `syntax:60` for multiplication,
|
||||
indicating that multiplication binds tighter than addition (higher the number, tighter the binding).
|
||||
This allows us to declare _precedence_ when defining new syntax.
|
||||
|
||||
```lean,ignore
|
||||
{{#include metaprogramming-arith.lean:7:13}}
|
||||
```
|
||||
|
||||
Further, if we look at `syntax:60 arith:60 "+" arith:61 : arith`, the
|
||||
precedence declarations at `arith:60 "+" arith:61` conveys that the left
|
||||
argument must have precedence at least `60` or greater, and the right argument
|
||||
must have precedence at least`61` or greater. Note that this forces left
|
||||
associativity. To understand this, let's compare two hypothetical parses:
|
||||
|
||||
```
|
||||
-- syntax:60 arith:60 "+" arith:61 : arith -- Arith.add
|
||||
-- a + b + c
|
||||
(a:60 + b:61):60 + c
|
||||
a + (b:60 + c:61):60
|
||||
```
|
||||
|
||||
In the parse tree of `a + (b:60 + c:61):60`, we see that the right argument `(b + c)` is given the precedence `60`. However,
|
||||
the rule for addition expects the right argument to have a precedence of **at least** 61, as witnessed by the `arith:61` at
|
||||
the right-hand-side of `syntax:60 arith:60 "+" arith:61 : arith`. Thus, the rule `syntax:60 arith:60 "+" arith:61 : arith`
|
||||
ensures that addition is left associative.
|
||||
|
||||
Since addition is declared arguments of precedence `60/61` and multiplication with `70/71`, this causes multiplication to bind
|
||||
tighter than addition. Once again, let's compare two hypothetical parses:
|
||||
|
||||
```
|
||||
-- syntax:60 arith:60 "+" arith:61 : arith -- Arith.add
|
||||
-- syntax:70 arith:70 "*" arith:71 : arith -- Arith.mul
|
||||
-- a * b + c
|
||||
a * (b:60 + c:61):60
|
||||
(a:70 * b:71):70 + c
|
||||
```
|
||||
|
||||
While parsing `a * (b + c)`, `(b + c)` is assigned a precedence `60` by the addition rule. However, multiplication expects
|
||||
the right argument to have precedence **at least** 71. Thus, this parse is invalid. In contrast, `(a * b) + c` assigns
|
||||
a precedence of `70` to `(a * b)`. This is compatible with addition which expects the left argument to have precedence
|
||||
**at least `60` ** (`70` is greater than `60`). Thus, the string `a * b + c` is parsed as `(a * b) + c`.
|
||||
For more details, please look at the [Lean manual on syntax extensions](./notation.md#notations-and-precedence).
|
||||
|
||||
To go from strings into `Arith`, we define a macro to
|
||||
translate the syntax category `arith` into an `Arith` inductive value that
|
||||
lives in `term`:
|
||||
|
||||
|
||||
```lean,ignore
|
||||
{{#include metaprogramming-arith.lean:15:16}}
|
||||
```
|
||||
|
||||
Our macro rules perform the "obvious" translation:
|
||||
|
||||
```lean,ignore
|
||||
{{#include metaprogramming-arith.lean:18:23}}
|
||||
```
|
||||
|
||||
And some examples:
|
||||
|
||||
```lean,ignore
|
||||
{{#include metaprogramming-arith.lean:25:41}}
|
||||
```
|
||||
|
||||
Writing variables as strings, such as `"x"` gets old; wouldn't it be so much
|
||||
prettier if we could write `x * y`, and have the macro translate this into `Arith.mul (Arith.Symbol "x") (Arith.mul "y")`?
|
||||
We can do this, and this will be our first taste of manipulating macro variables --- we'll use `x.getId` instead of directly evaluating `$x`.
|
||||
We also write a macro rule for `Arith|` that translates an identifier into
|
||||
a string, using `$(Lean.quote (toString x.getId))`:
|
||||
|
||||
```lean,ignore
|
||||
{{#include metaprogramming-arith.lean:43:46}}
|
||||
```
|
||||
|
||||
Let's test and see that we can now write expressions such as `x * y` directly instead of having to write `"x" * "y"`:
|
||||
|
||||
```lean,ignore
|
||||
{{#include metaprogramming-arith.lean:48:51}}
|
||||
```
|
||||
|
||||
We now show an unfortunate consequence of the above definitions. Suppose we want to build `(x + y) + z`.
|
||||
Since we already have defined `xPlusY` as `x + y`, perhaps we should reuse it! Let's try:
|
||||
|
||||
```lean,ignore
|
||||
#check `[Arith| xPlusY + z] -- Arith.add (Arith.symbol "xPlusY") (Arith.symbol "z")
|
||||
```
|
||||
|
||||
Whoops, that didn't work! What happened? Lean treats `xPlusY` _itself_ as an identifier! So we need to add some syntax
|
||||
to be able to "escape" the `Arith|` context. Let's use the syntax `<[ $e:term ]>` to mean: evaluate `$e` as a real term,
|
||||
not an identifier. The macro looks like follows:
|
||||
|
||||
```lean,ignore
|
||||
{{#include metaprogramming-arith.lean:53:56}}
|
||||
```
|
||||
|
||||
Let's try our previous example:
|
||||
|
||||
```lean,ignore
|
||||
{{#include metaprogramming-arith.lean:58:58}}
|
||||
```
|
||||
|
||||
Perfect!
|
||||
|
||||
In this tutorial, we expanded on the previous tutorial to parse a more
|
||||
realistic grammar with multiple levels of precedence, how to parse identifiers directly
|
||||
within a macro, and how to provide an escape from within the macro context.
|
||||
|
||||
#### Full code listing
|
||||
|
||||
```lean
|
||||
{{#include metaprogramming-arith.lean}}
|
||||
```
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
Mission
|
||||
=======
|
||||
|
||||
Empower software developers to design, develop, and reason about programs.
|
||||
Empower mathematicians and scientists to design, develop, and reason about formal models.
|
||||
|
||||
How
|
||||
---
|
||||
|
||||
Lean is an efficient functional programming language based on dependent type theory.
|
||||
It is under heavy development, but it already generates very efficient code.
|
||||
It also has a powerful meta-programming framework, extensible parser, and IDE support based on LSP.
|
||||
@@ -1 +0,0 @@
|
||||
# Other Commands
|
||||
@@ -1,83 +0,0 @@
|
||||
/* Pygments stylesheet generated by Alectryon (style=None) */
|
||||
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||
.highlight .hll, .code .hll { background-color: #ffffcc }
|
||||
.highlight .c, .code .c { color: #555753; font-style: italic } /* Comment */
|
||||
.highlight .err, .code .err { color: #a40000; border: 1px solid #cc0000 } /* Error */
|
||||
.highlight .g, .code .g { color: #000000 } /* Generic */
|
||||
.highlight .k, .code .k { color: #8f5902 } /* Keyword */
|
||||
.highlight .l, .code .l { color: #2e3436 } /* Literal */
|
||||
.highlight .n, .code .n { color: #000000 } /* Name */
|
||||
.highlight .o, .code .o { color: #000000 } /* Operator */
|
||||
.highlight .x, .code .x { color: #2e3436 } /* Other */
|
||||
.highlight .p, .code .p { color: #000000 } /* Punctuation */
|
||||
.highlight .ch, .code .ch { color: #555753; font-weight: bold; font-style: italic } /* Comment.Hashbang */
|
||||
.highlight .cm, .code .cm { color: #555753; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp, .code .cp { color: #3465a4; font-style: italic } /* Comment.Preproc */
|
||||
.highlight .cpf, .code .cpf { color: #555753; font-style: italic } /* Comment.PreprocFile */
|
||||
.highlight .c1, .code .c1 { color: #555753; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs, .code .cs { color: #3465a4; font-weight: bold; font-style: italic } /* Comment.Special */
|
||||
.highlight .gd, .code .gd { color: #a40000 } /* Generic.Deleted */
|
||||
.highlight .ge, .code .ge { color: #000000; font-style: italic } /* Generic.Emph */
|
||||
.highlight .gr, .code .gr { color: #a40000 } /* Generic.Error */
|
||||
.highlight .gh, .code .gh { color: #a40000; font-weight: bold } /* Generic.Heading */
|
||||
.highlight .gi, .code .gi { color: #4e9a06 } /* Generic.Inserted */
|
||||
.highlight .go, .code .go { color: #000000; font-style: italic } /* Generic.Output */
|
||||
.highlight .gp, .code .gp { color: #8f5902 } /* Generic.Prompt */
|
||||
.highlight .gs, .code .gs { color: #000000; font-weight: bold } /* Generic.Strong */
|
||||
.highlight .gu, .code .gu { color: #000000; font-weight: bold } /* Generic.Subheading */
|
||||
.highlight .gt, .code .gt { color: #000000; font-style: italic } /* Generic.Traceback */
|
||||
.highlight .kc, .code .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */
|
||||
.highlight .kd, .code .kd { color: #4e9a06; font-weight: bold } /* Keyword.Declaration */
|
||||
.highlight .kn, .code .kn { color: #4e9a06; font-weight: bold } /* Keyword.Namespace */
|
||||
.highlight .kp, .code .kp { color: #204a87 } /* Keyword.Pseudo */
|
||||
.highlight .kr, .code .kr { color: #8f5902 } /* Keyword.Reserved */
|
||||
.highlight .kt, .code .kt { color: #204a87 } /* Keyword.Type */
|
||||
.highlight .ld, .code .ld { color: #2e3436 } /* Literal.Date */
|
||||
.highlight .m, .code .m { color: #2e3436 } /* Literal.Number */
|
||||
.highlight .s, .code .s { color: #ad7fa8 } /* Literal.String */
|
||||
.highlight .na, .code .na { color: #c4a000 } /* Name.Attribute */
|
||||
.highlight .nb, .code .nb { color: #75507b } /* Name.Builtin */
|
||||
.highlight .nc, .code .nc { color: #204a87 } /* Name.Class */
|
||||
.highlight .no, .code .no { color: #ce5c00 } /* Name.Constant */
|
||||
.highlight .nd, .code .nd { color: #3465a4; font-weight: bold } /* Name.Decorator */
|
||||
.highlight .ni, .code .ni { color: #c4a000; text-decoration: underline } /* Name.Entity */
|
||||
.highlight .ne, .code .ne { color: #cc0000 } /* Name.Exception */
|
||||
.highlight .nf, .code .nf { color: #a40000 } /* Name.Function */
|
||||
.highlight .nl, .code .nl { color: #3465a4; font-weight: bold } /* Name.Label */
|
||||
.highlight .nn, .code .nn { color: #000000 } /* Name.Namespace */
|
||||
.highlight .nx, .code .nx { color: #000000 } /* Name.Other */
|
||||
.highlight .py, .code .py { color: #000000 } /* Name.Property */
|
||||
.highlight .nt, .code .nt { color: #a40000 } /* Name.Tag */
|
||||
.highlight .nv, .code .nv { color: #ce5c00 } /* Name.Variable */
|
||||
.highlight .ow, .code .ow { color: #8f5902 } /* Operator.Word */
|
||||
.highlight .w, .code .w { color: #d3d7cf; text-decoration: underline } /* Text.Whitespace */
|
||||
.highlight .mb, .code .mb { color: #2e3436 } /* Literal.Number.Bin */
|
||||
.highlight .mf, .code .mf { color: #2e3436 } /* Literal.Number.Float */
|
||||
.highlight .mh, .code .mh { color: #2e3436 } /* Literal.Number.Hex */
|
||||
.highlight .mi, .code .mi { color: #2e3436 } /* Literal.Number.Integer */
|
||||
.highlight .mo, .code .mo { color: #2e3436 } /* Literal.Number.Oct */
|
||||
.highlight .sa, .code .sa { color: #ad7fa8 } /* Literal.String.Affix */
|
||||
.highlight .sb, .code .sb { color: #ad7fa8 } /* Literal.String.Backtick */
|
||||
.highlight .sc, .code .sc { color: #ad7fa8; font-weight: bold } /* Literal.String.Char */
|
||||
.highlight .dl, .code .dl { color: #ad7fa8 } /* Literal.String.Delimiter */
|
||||
.highlight .sd, .code .sd { color: #ad7fa8 } /* Literal.String.Doc */
|
||||
.highlight .s2, .code .s2 { color: #ad7fa8 } /* Literal.String.Double */
|
||||
.highlight .se, .code .se { color: #ad7fa8; font-weight: bold } /* Literal.String.Escape */
|
||||
.highlight .sh, .code .sh { color: #ad7fa8; text-decoration: underline } /* Literal.String.Heredoc */
|
||||
.highlight .si, .code .si { color: #ce5c00 } /* Literal.String.Interpol */
|
||||
.highlight .sx, .code .sx { color: #ad7fa8 } /* Literal.String.Other */
|
||||
.highlight .sr, .code .sr { color: #ad7fa8 } /* Literal.String.Regex */
|
||||
.highlight .s1, .code .s1 { color: #ad7fa8 } /* Literal.String.Single */
|
||||
.highlight .ss, .code .ss { color: #8f5902 } /* Literal.String.Symbol */
|
||||
.highlight .bp, .code .bp { color: #5c35cc } /* Name.Builtin.Pseudo */
|
||||
.highlight .fm, .code .fm { color: #a40000 } /* Name.Function.Magic */
|
||||
.highlight .vc, .code .vc { color: #ce5c00 } /* Name.Variable.Class */
|
||||
.highlight .vg, .code .vg { color: #ce5c00; text-decoration: underline } /* Name.Variable.Global */
|
||||
.highlight .vi, .code .vi { color: #ce5c00 } /* Name.Variable.Instance */
|
||||
.highlight .vm, .code .vm { color: #ce5c00 } /* Name.Variable.Magic */
|
||||
.highlight .il, .code .il { color: #2e3436 } /* Literal.Number.Integer.Long */
|
||||
.hljs-doctag { color: green }
|
||||
.hljs-comment { color: green }
|
||||
@@ -1,23 +0,0 @@
|
||||
# Quickstart
|
||||
|
||||
These instructions will walk you through setting up Lean 4 together with VS Code as an editor for Lean 4.
|
||||
See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
|
||||
|
||||
1. Install [VS Code](https://code.visualstudio.com/).
|
||||
|
||||
1. Launch VS Code and install the `Lean 4` extension by clicking on the 'Extensions' sidebar entry and searching for 'Lean 4'.
|
||||
|
||||

|
||||
|
||||
1. Open the Lean 4 setup guide by creating a new text file using 'File > New Text File' (`Ctrl+N` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting 'Documentation… > Docs: Show Setup Guide'.
|
||||
|
||||

|
||||
|
||||
1. Follow the Lean 4 setup guide. It will:
|
||||
|
||||
- walk you through learning resources for Lean,
|
||||
- teach you how to set up Lean's dependencies on your platform,
|
||||
- install Lean 4 for you at the click of a button,
|
||||
- help you set up your first project.
|
||||
|
||||

|
||||
@@ -1,3 +0,0 @@
|
||||
# The Lean Reference Manual
|
||||
|
||||
The latest version of the Lean reference manual is available [here](https://lean-lang.org/doc/reference/latest).
|
||||
@@ -1,23 +0,0 @@
|
||||
Semantic Highlighting
|
||||
---------------------
|
||||
|
||||
The Lean language server provides semantic highlighting information to editors. In order to benefit from this in VSCode, you may need to activate the "Editor > Semantic Highlighting" option in the preferences (this is translates to `"editor.semanticHighlighting.enabled": true,`
|
||||
in `settings.json`). The default option here is to let your color theme decides whether it activates semantic highlighting (the default themes Dark+ and Light+ do activate it for instance).
|
||||
|
||||
However this may be insufficient if your color theme does not distinguish enough syntax categories or distinguishes them very subtly. For instance the default Light+ theme uses color `#001080` for variables. This is awfully close to `#000000` that is used as the default text color. This makes it very easy to miss an accidental use of [auto bound implicit arguments](https://lean-lang.org/lean4/doc/autobound.html). For instance in
|
||||
```lean
|
||||
def my_id (n : nat) := n
|
||||
```
|
||||
maybe `nat` is a typo and `Nat` was intended. If your color theme is good enough then you should see that `n` and `nat` have the same color since they are both marked as variables by semantic highlighting. If you rather write `(n : Nat)` then `n` keeps its variable color but `Nat` gets the default text color.
|
||||
|
||||
If you use such a bad theme, you can fix things by modifying the `Semantic Token Color Customizations` configuration. This cannot be done directly in the preferences dialog but you can click on "Edit in settings.json" to directly edit the settings file. Beware that you must save this file (in the same way you save any file opened in VSCode) before seeing any effect in other tabs or VSCode windows.
|
||||
|
||||
In the main config object, you can add something like
|
||||
```
|
||||
"editor.semanticTokenColorCustomizations": {
|
||||
"[Default Light+]": {"rules": {"function": "#ff0000", "property": "#00ff00", "variable": "#ff00ff"}}
|
||||
},
|
||||
```
|
||||
The colors in this example are not meant to be nice but to be easy to spot in your file when testing. Of course you need to replace `Default Light+` with the name of your theme, and you can customize several themes if you use several themes. VSCode will display small colored boxes next to the HTML color specifications. Hovering on top of a color specification opens a convenient color picker dialog.
|
||||
|
||||
In order to understand what `function`, `property` and `variable` mean in the above example, the easiest path is to open a Lean file and ask VSCode about its classification of various bits of your file. Open the command palette with Ctrl-shift-p (or ⌘-shift-p on a Mac) and search for "Inspect Editor Tokens and Scopes" (typing the word "tokens" should be enough to see it). You can then click on any word in your file and look if there is a "semantic token type" line in the displayed information.
|
||||
65
doc/setup.md
65
doc/setup.md
@@ -1,65 +0,0 @@
|
||||
# Supported Platforms
|
||||
|
||||
### Tier 1
|
||||
|
||||
Platforms built & tested by our CI, available as binary releases via elan (see below)
|
||||
|
||||
* x86-64 Linux with glibc 2.26+
|
||||
* x86-64 macOS 10.15+
|
||||
* aarch64 (Apple Silicon) macOS 10.15+
|
||||
* x86-64 Windows 11 (any version), Windows 10 (version 1903 or higher), Windows Server 2022, Windows Server 2025
|
||||
|
||||
### Tier 2
|
||||
|
||||
Platforms cross-compiled but not tested by our CI, available as binary releases
|
||||
|
||||
Releases may be silently broken due to the lack of automated testing.
|
||||
Issue reports and fixes are welcome.
|
||||
|
||||
* aarch64 Linux with glibc 2.27+
|
||||
* x86 (32-bit) Linux
|
||||
* Emscripten Web Assembly
|
||||
|
||||
<!--
|
||||
### Tier 3
|
||||
|
||||
Platforms that are known to work from manual testing, but do not come with CI or official releases
|
||||
-->
|
||||
|
||||
# Setting Up Lean
|
||||
|
||||
See also the [quickstart](./quickstart.md) instructions for a standard setup with VS Code as the editor.
|
||||
|
||||
Release builds for all supported platforms are available at <https://github.com/leanprover/lean4/releases>.
|
||||
Instead of downloading these and setting up the paths manually, however, it is recommended to use the Lean version manager [`elan`](https://github.com/leanprover/elan) instead:
|
||||
```sh
|
||||
$ elan self update # in case you haven't updated elan in a while
|
||||
# download & activate latest Lean 4 stable release (https://github.com/leanprover/lean4/releases)
|
||||
$ elan default leanprover/lean4:stable
|
||||
```
|
||||
|
||||
## `lake`
|
||||
|
||||
Lean 4 comes with a package manager named `lake`.
|
||||
Use `lake init foo` to initialize a Lean package `foo` in the current directory, and `lake build` to typecheck and build it as well as all its dependencies. Use `lake help` to learn about further commands.
|
||||
The general directory structure of a package `foo` is
|
||||
```sh
|
||||
lakefile.lean # package configuration
|
||||
lean-toolchain # specifies the lean version to use
|
||||
Foo.lean # main file, import via `import Foo`
|
||||
Foo/
|
||||
A.lean # further files, import via e.g. `import Foo.A`
|
||||
A/... # further nesting
|
||||
.lake/ # `lake` build output directory
|
||||
```
|
||||
|
||||
After running `lake build` you will see a binary named `./.lake/build/bin/foo` and when you run it you should see the output:
|
||||
```
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
## Editing
|
||||
|
||||
Lean implements the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) that can be used for interactive development in [Emacs](https://github.com/leanprover/lean4-mode), [VS Code](https://github.com/leanprover-community/vscode-lean4), and possibly other editors.
|
||||
|
||||
Changes must be saved to be visible in other files, which must then be invalidated using an editor command (see links above).
|
||||
@@ -1,91 +0,0 @@
|
||||
## Simple Type Theory
|
||||
|
||||
"Type theory" gets its name from the fact that every expression has an associated *type*.
|
||||
For example, in a given context, ``x + 0`` may denote a natural number and ``f`` may denote a function on the natural numbers.
|
||||
For those that don't like math, a Lean natural number is an arbitrary-precision unsigned integer.
|
||||
|
||||
Here are some examples of how we can declare objects in Lean and check their types.
|
||||
|
||||
```lean
|
||||
/- Declare some constants. -/
|
||||
|
||||
constant m : Nat -- m is a natural number
|
||||
constant n : Nat
|
||||
constant b1 : Bool -- b1 is a Boolean
|
||||
constant b2 : Bool
|
||||
|
||||
/- Check their types. -/
|
||||
|
||||
#check m -- output: Nat
|
||||
#check n
|
||||
#check n + 0 -- Nat
|
||||
#check m * (n + 0) -- Nat
|
||||
#check b1 -- Bool
|
||||
#check b1 && b2 -- "&&" is the Boolean and
|
||||
#check b1 || b2 -- Boolean or
|
||||
#check true -- Boolean "true"
|
||||
```
|
||||
|
||||
Any text between ``/-`` and ``-/`` constitutes a comment block that is ignored by Lean.
|
||||
Similarly, two dashes `--` indicate that the rest of the line contains a comment that is also ignored.
|
||||
Comment blocks can be nested, making it possible to "comment out" chunks of code, just as in many programming languages.
|
||||
|
||||
The ``constant`` command introduce new constant symbols into the working environment.
|
||||
The ``#check`` command asks Lean to report their types; in Lean, auxiliary commands that query the system for
|
||||
information typically begin with the hash symbol. You should try declaring some constants and type checking
|
||||
some expressions on your own. Declaring new objects in this way is a good way to experiment with the system.
|
||||
|
||||
What makes simple type theory powerful is that one can build new types out of others.
|
||||
For example, if ``a`` and ``b`` are types, ``a -> b`` denotes the type of functions from ``a`` to ``b``,
|
||||
and ``a × b`` denotes the type of pairs consisting of an element of ``a``
|
||||
paired with an element of ``b``, also known as the *Cartesian product*.
|
||||
Note that `×` is a Unicode symbol. We believe that judicious use of Unicode improves legibility,
|
||||
and all modern editors have great support for it. In the Lean standard library, we often use
|
||||
Greek letters to denote types, and the Unicode symbol `→` as a more compact version of `->`.
|
||||
|
||||
```lean
|
||||
constant m : Nat
|
||||
constant n : Nat
|
||||
|
||||
constant f : Nat → Nat -- type the arrow as "\to" or "\r"
|
||||
constant f' : Nat -> Nat -- alternative ASCII notation
|
||||
constant p : Nat × Nat -- type the product as "\times"
|
||||
constant q : Prod Nat Nat -- alternative notation
|
||||
constant g : Nat → Nat → Nat
|
||||
constant g' : Nat → (Nat → Nat) -- has the same type as g!
|
||||
constant h : Nat × Nat → Nat
|
||||
constant F : (Nat → Nat) → Nat -- a "functional"
|
||||
|
||||
#check f -- Nat → Nat
|
||||
#check f n -- Nat
|
||||
#check g m n -- Nat
|
||||
#check g m -- Nat → Nat
|
||||
#check (m, n) -- Nat × Nat
|
||||
#check p.1 -- Nat
|
||||
#check p.2 -- Nat
|
||||
#check (m, n).1 -- Nat
|
||||
#check (p.1, n) -- Nat × Nat
|
||||
#check F f -- Nat
|
||||
```
|
||||
|
||||
Once again, you should try some examples on your own.
|
||||
|
||||
Let us dispense with some basic syntax. You can enter the unicode arrow ``→`` by typing ``\to`` or ``\r``.
|
||||
You can also use the ASCII alternative ``->``, so the expressions ``Nat -> Nat`` and ``Nat → Nat`` mean the same thing.
|
||||
Both expressions denote the type of functions that take a natural number as input and return a natural number as output.
|
||||
The unicode symbol ``×`` for the Cartesian product is entered as ``\times``.
|
||||
We will generally use lower-case Greek letters like ``α``, ``β``, and ``γ`` to range over types.
|
||||
You can enter these particular ones with ``\a``, ``\b``, and ``\g``.
|
||||
|
||||
There are a few more things to notice here. First, the application of a function ``f`` to a value ``x`` is denoted ``f x``.
|
||||
Second, when writing type expressions, arrows associate to the *right*; for example, the type of ``g`` is ``Nat → (Nat → Nat)``.
|
||||
Thus we can view ``g`` as a function that takes natural numbers and returns another function that takes a natural number and
|
||||
returns a natural number.
|
||||
In type theory, this is generally more convenient than writing ``g`` as a function that takes a pair of natural numbers as input
|
||||
and returns a natural number as output. For example, it allows us to "partially apply" the function ``g``.
|
||||
The example above shows that ``g m`` has type ``Nat → Nat``, that is, the function that "waits" for a second argument, ``n``,
|
||||
and then returns ``g m n``. Taking a function ``h`` of type ``Nat × Nat → Nat`` and "redefining" it to look like ``g`` is a process
|
||||
known as *currying*, something we will come back to below.
|
||||
|
||||
By now you may also have guessed that, in Lean, ``(m, n)`` denotes the ordered pair of ``m`` and ``n``,
|
||||
and if ``p`` is a pair, ``p.1`` and ``p.2`` denote the two projections.
|
||||
1146
doc/style.md
Normal file
1146
doc/style.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,80 +0,0 @@
|
||||
# Balanced Parentheses as an Embedded Domain Specific Language
|
||||
|
||||
Let's look at how to use macros to extend the Lean 4 parser and embed a language for building _balanced parentheses_.
|
||||
This language accepts strings given by the [BNF grammar](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form)
|
||||
|
||||
```
|
||||
Dyck ::=
|
||||
"(" Dyck ")"
|
||||
| "{" Dyck "}"
|
||||
| end
|
||||
```
|
||||
|
||||
We begin by defining an inductive data type of the grammar we wish to parse:
|
||||
|
||||
```lean,ignore
|
||||
inductive Dyck : Type where
|
||||
| round : Dyck → Dyck -- ( <inner> )
|
||||
| curly : Dyck → Dyck -- { <inner> }
|
||||
| leaf : Dyck
|
||||
```
|
||||
|
||||
We begin by declaring a _syntax category_ using the `declare_syntax_cat <category>` command.
|
||||
This names our grammar and allows us to specify parsing rules associated with our grammar.
|
||||
|
||||
```lean,ignore
|
||||
declare_syntax_cat brack
|
||||
```
|
||||
|
||||
Next, we specify the grammar using the `syntax <parse rule>` command:
|
||||
|
||||
```lean,ignore
|
||||
syntax "end" : brack
|
||||
```
|
||||
|
||||
The above means that the token "end" lives in syntax category `brack`.
|
||||
|
||||
Similarly, we declare the rules `"(" Dyck ")"` and `"{" Dyck "}"` using the rules:
|
||||
|
||||
```lean,ignore
|
||||
syntax "(" brack ")" : brack
|
||||
syntax "{" brack "}" : brack
|
||||
```
|
||||
|
||||
Finally, we need a way to build _Lean 4 terms_ from this grammar -- that is, we must translate out of this
|
||||
grammar into a `Dyck` value, which is a Lean 4 term. For this, we create a new kind of "quotation" that
|
||||
consumes syntax in `brack` and produces a `term`.
|
||||
|
||||
```lean,ignore
|
||||
syntax "`[Dyck| " brack "]" : term
|
||||
```
|
||||
|
||||
To specify the transformation rules, we use `macro_rules` to declare how the syntax `` `[Dyck| <brack>] ``
|
||||
produces terms. This is written using a pattern-matching style syntax, where the left-hand side
|
||||
declares the syntax pattern to be matched, and the right-hand side declares the production. Syntax placeholders (antiquotations)
|
||||
are introduced via the `$<var-name>` syntax. The right-hand side is
|
||||
an arbitrary Lean term that we are producing.
|
||||
|
||||
```lean,ignore
|
||||
macro_rules
|
||||
| `(`[Dyck| end]) => `(Dyck.leaf)
|
||||
| `(`[Dyck| ($b)]) => `(Dyck.round `[Dyck| $b]) -- recurse
|
||||
| `(`[Dyck| {$b}]) => `(Dyck.curly `[Dyck| $b]) -- recurse
|
||||
```
|
||||
|
||||
|
||||
```lean,ignore
|
||||
#check `[Dyck| end] -- Dyck.leaf
|
||||
#check `[Dyck| {(end)}] -- Dyck.curl (Dyck.round Dyck.leaf)
|
||||
```
|
||||
|
||||
In summary, we've seen:
|
||||
- How to declare a syntax category for the Dyck grammar.
|
||||
- How to specify parse trees of this grammar using `syntax`
|
||||
- How to translate out of this grammar into Lean 4 terms using `macro_rules`.
|
||||
|
||||
The full program listing is given below:
|
||||
|
||||
```lean
|
||||
{{#include syntax_example.lean}}
|
||||
```
|
||||
@@ -1,4 +0,0 @@
|
||||
# Syntax Metaprogramming Examples
|
||||
|
||||
- [Balanced Parentheses](./syntax_example.md)
|
||||
- [Arithmetic DSL](./metaprogramming-arith.md)
|
||||
@@ -1,91 +0,0 @@
|
||||
You can copy highlighted code [straight from VS Code](https://code.visualstudio.com/updates/v1_10#_copy-with-syntax-highlighting) to any rich text editor supporting HTML input. For highlighting code in LaTeX, there are two options:
|
||||
* [listings](https://ctan.org/pkg/listings), which is a common package and simple to set up, but you may run into some restrictions of it and LaTeX around Unicode
|
||||
* [`minted`](https://ctan.org/pkg/minted), a LaTeX package wrapping the [Pygments](https://pygments.org/) syntax highlighting library. It needs a few more steps to set up, but provides unrestricted support for Unicode when combined with XeLaTeX or LuaLaTex.
|
||||
|
||||
## Example with `listings`
|
||||
|
||||
Save [`lstlean.tex`](https://raw.githubusercontent.com/leanprover/lean4/master/doc/latex/lstlean.tex) into the same directory, or anywhere in your `TEXINPUTS` path, as the following test file:
|
||||
```latex
|
||||
\documentclass{article}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{listings}
|
||||
\usepackage{amssymb}
|
||||
|
||||
\usepackage{color}
|
||||
\definecolor{keywordcolor}{rgb}{0.7, 0.1, 0.1} % red
|
||||
\definecolor{tacticcolor}{rgb}{0.0, 0.1, 0.6} % blue
|
||||
\definecolor{commentcolor}{rgb}{0.4, 0.4, 0.4} % grey
|
||||
\definecolor{symbolcolor}{rgb}{0.0, 0.1, 0.6} % blue
|
||||
\definecolor{sortcolor}{rgb}{0.1, 0.5, 0.1} % green
|
||||
\definecolor{attributecolor}{rgb}{0.7, 0.1, 0.1} % red
|
||||
|
||||
\def\lstlanguagefiles{lstlean.tex}
|
||||
% set default language
|
||||
\lstset{language=lean}
|
||||
|
||||
\begin{document}
|
||||
\begin{lstlisting}
|
||||
theorem funext {f₁ f₂ : ∀ (x : α), β x} (h : ∀ x, f₁ x = f₂ x) : f₁ = f₂ := by
|
||||
show extfunApp (Quotient.mk f₁) = extfunApp (Quotient.mk f₂)
|
||||
apply congrArg
|
||||
apply Quotient.sound
|
||||
exact h
|
||||
\end{lstlisting}
|
||||
\end{document}
|
||||
```
|
||||
Compile the file via
|
||||
```bash
|
||||
$ pdflatex test.tex
|
||||
```
|
||||
|
||||
* for older LaTeX versions, you might need to use `[utf8x]` instead of `[utf8]` with `inputenc`
|
||||
|
||||
## Example with `minted`
|
||||
|
||||
First [install Pygments](https://pygments.org/download/) (version 2.18 or newer).
|
||||
Then save the following sample LaTeX file `test.tex` into the same directory:
|
||||
|
||||
```latex
|
||||
\documentclass{article}
|
||||
\usepackage{fontspec}
|
||||
% switch to a monospace font supporting more Unicode characters
|
||||
\setmonofont{FreeMono}
|
||||
\usepackage{minted}
|
||||
\newmintinline[lean]{lean4}{bgcolor=white}
|
||||
\newminted[leancode]{lean4}{fontsize=\footnotesize}
|
||||
\usemintedstyle{tango} % a nice, colorful theme
|
||||
|
||||
\begin{document}
|
||||
\begin{leancode}
|
||||
theorem funext {f₁ f₂ : ∀ (x : α), β x} (h : ∀ x, f₁ x = f₂ x) : f₁ = f₂ := by
|
||||
show extfunApp (Quotient.mk' f₁) = extfunApp (Quotient.mk' f₂)
|
||||
apply congrArg
|
||||
apply Quotient.sound
|
||||
exact h
|
||||
\end{leancode}
|
||||
\end{document}
|
||||
```
|
||||
|
||||
You can then compile `test.tex` by executing the following command:
|
||||
|
||||
```bash
|
||||
xelatex --shell-escape test.tex
|
||||
```
|
||||
|
||||
Some remarks:
|
||||
|
||||
- either `xelatex` or `lualatex` is required to handle Unicode characters in the code.
|
||||
- `--shell-escape` is needed to allow `xelatex` to execute `pygmentize` in a shell.
|
||||
- If the chosen monospace font is missing some Unicode symbols, you can direct them to be displayed using a fallback font or other replacement LaTeX code.
|
||||
``` latex
|
||||
\usepackage{newunicodechar}
|
||||
\newfontfamily{\freeserif}{DejaVu Sans}
|
||||
\newunicodechar{✝}{\freeserif{✝}}
|
||||
\newunicodechar{𝓞}{\ensuremath{\mathcal{O}}}
|
||||
```
|
||||
- If you are using an old version of Pygments, you can copy
|
||||
[`lean.py`](https://raw.githubusercontent.com/pygments/pygments/master/pygments/lexers/lean.py) into your working directory,
|
||||
and use `lean4.py:Lean4Lexer -x` instead of `lean4` above.
|
||||
If your version of `minted` is v2.7 or newer, but before v3.0,
|
||||
you will additionally need to follow the workaround described in https://github.com/gpoore/minted/issues/360.
|
||||
393
doc/tactics.md
393
doc/tactics.md
@@ -1,393 +0,0 @@
|
||||
# Tactics
|
||||
|
||||
Tactics are metaprograms, that is, programs that create programs.
|
||||
Lean is implemented in Lean, you can import its implementation using `import Lean`.
|
||||
The `Lean` package is part of the Lean distribution.
|
||||
You can use the functions in the `Lean` package to write your own metaprograms
|
||||
that automate repetitive tasks when writing programs and proofs.
|
||||
|
||||
We provide the **tactic** domain specific language (DSL) for using the tactic framework.
|
||||
The tactic DSL provides commands for creating terms (and proofs). You
|
||||
don't need to import the `Lean` package for using the tactic DSL.
|
||||
Simple extensions can be implemented using macros. More complex extensions require
|
||||
the `Lean` package. Notation used to write Lean terms can be easily lifted to the tactic DSL.
|
||||
|
||||
Tactics are instructions that tell Lean how to construct a term or proof.
|
||||
Tactics operate on holes also known as goals. Each hole represents a missing
|
||||
part of the term you are trying to build. Internally these holes are represented
|
||||
as metavariables. They have a type and a local context. The local context contains
|
||||
all local variables in scope.
|
||||
|
||||
In the following example, we prove the same simple theorem using different tactics.
|
||||
The keyword `by` instructs Lean to use the tactic DSL to construct a term.
|
||||
Our initial goal is a hole with type `p ∨ q → q ∨ p`. The tactic `intro h`
|
||||
fills this hole using the term `fun h => ?m` where `?m` is a new hole we need to solve.
|
||||
This hole has type `q ∨ p`, and the local context contains `h : p ∨ q`.
|
||||
The tactic `cases` fills the hole using `Or.casesOn h (fun h1 => ?m1) (fun h2 => ?m2)`
|
||||
where `?m1` and `?m2` are new holes. The tactic `apply Or.inr` fills the hole `?m1`
|
||||
with the application `Or.inr ?m3`, and `exact h1` fills `?m3` with `h1`.
|
||||
The tactic `assumption` tries to fill a hole by searching the local context for a term with the same type.
|
||||
|
||||
```lean
|
||||
theorem ex1 : p ∨ q → q ∨ p := by
|
||||
intro h
|
||||
cases h with
|
||||
| inl h1 =>
|
||||
apply Or.inr
|
||||
exact h1
|
||||
| inr h2 =>
|
||||
apply Or.inl
|
||||
assumption
|
||||
|
||||
#print ex1
|
||||
/-
|
||||
theorem ex1 : {p q : Prop} → p ∨ q → q ∨ p :=
|
||||
fun {p q : Prop} (h : p ∨ q) =>
|
||||
Or.casesOn h (fun (h1 : p) => Or.inr h1) fun (h2 : q) => Or.inl h2
|
||||
-/
|
||||
|
||||
-- You can use `match-with` in tactics.
|
||||
theorem ex2 : p ∨ q → q ∨ p := by
|
||||
intro h
|
||||
match h with
|
||||
| Or.inl _ => apply Or.inr; assumption
|
||||
| Or.inr h2 => apply Or.inl; exact h2
|
||||
|
||||
-- As we have the `fun+match` syntax sugar for terms,
|
||||
-- we have the `intro+match` syntax sugar
|
||||
theorem ex3 : p ∨ q → q ∨ p := by
|
||||
intro
|
||||
| Or.inl h1 =>
|
||||
apply Or.inr
|
||||
exact h1
|
||||
| Or.inr h2 =>
|
||||
apply Or.inl
|
||||
assumption
|
||||
```
|
||||
|
||||
The examples above are all structured, but Lean 4 still supports unstructured
|
||||
proofs. Unstructured proofs are useful when creating reusable scripts that may
|
||||
discharge different goals.
|
||||
Here is an unstructured version of the example above.
|
||||
|
||||
```lean
|
||||
theorem ex1 : p ∨ q → q ∨ p := by
|
||||
intro h
|
||||
cases h
|
||||
apply Or.inr
|
||||
assumption
|
||||
apply Or.inl
|
||||
assumption
|
||||
done -- fails with an error here if there are unsolvable goals
|
||||
|
||||
theorem ex2 : p ∨ q → q ∨ p := by
|
||||
intro h
|
||||
cases h
|
||||
focus -- instructs Lean to `focus` on the first goal,
|
||||
apply Or.inr
|
||||
assumption
|
||||
-- it will fail if there are still unsolvable goals here
|
||||
focus
|
||||
apply Or.inl
|
||||
assumption
|
||||
|
||||
theorem ex3 : p ∨ q → q ∨ p := by
|
||||
intro h
|
||||
cases h
|
||||
-- You can still use curly braces and semicolons instead of
|
||||
-- whitespace sensitive notation as in the previous example
|
||||
{ apply Or.inr;
|
||||
assumption
|
||||
-- It will fail if there are unsolved goals
|
||||
}
|
||||
{ apply Or.inl;
|
||||
assumption
|
||||
}
|
||||
|
||||
-- Many tactics tag subgoals. The tactic `cases` tag goals using constructor names.
|
||||
-- The tactic `case tag => tactics` instructs Lean to solve the goal
|
||||
-- with the matching tag.
|
||||
theorem ex4 : p ∨ q → q ∨ p := by
|
||||
intro h
|
||||
cases h
|
||||
case inr =>
|
||||
apply Or.inl
|
||||
assumption
|
||||
case inl =>
|
||||
apply Or.inr
|
||||
assumption
|
||||
|
||||
-- Same example for curly braces and semicolons aficionados
|
||||
theorem ex5 : p ∨ q → q ∨ p := by {
|
||||
intro h;
|
||||
cases h;
|
||||
case inr => {
|
||||
apply Or.inl;
|
||||
assumption
|
||||
}
|
||||
case inl => {
|
||||
apply Or.inr;
|
||||
assumption
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rewrite
|
||||
|
||||
TODO
|
||||
|
||||
## Pattern matching
|
||||
|
||||
As a convenience, pattern-matching has been integrated into tactics such as `intro` and `funext`.
|
||||
|
||||
```lean
|
||||
theorem ex1 : s ∧ q ∧ r → p ∧ r → q ∧ p := by
|
||||
intro ⟨_, ⟨hq, _⟩⟩ ⟨hp, _⟩
|
||||
exact ⟨hq, hp⟩
|
||||
|
||||
theorem ex2 :
|
||||
(fun (x : Nat × Nat) (y : Nat × Nat) => x.1 + y.2)
|
||||
=
|
||||
(fun (x : Nat × Nat) (z : Nat × Nat) => z.2 + x.1) := by
|
||||
funext (a, b) (c, d)
|
||||
show a + d = d + a
|
||||
rw [Nat.add_comm]
|
||||
```
|
||||
|
||||
## Induction
|
||||
|
||||
The `induction` tactic now supports user-defined induction principles with
|
||||
multiple targets (aka major premises).
|
||||
|
||||
|
||||
```lean
|
||||
/-
|
||||
theorem Nat.mod.inductionOn
|
||||
{motive : Nat → Nat → Sort u}
|
||||
(x y : Nat)
|
||||
(ind : ∀ x y, 0 < y ∧ y ≤ x → motive (x - y) y → motive x y)
|
||||
(base : ∀ x y, ¬(0 < y ∧ y ≤ x) → motive x y)
|
||||
: motive x y :=
|
||||
-/
|
||||
|
||||
theorem ex (x : Nat) {y : Nat} (h : y > 0) : x % y < y := by
|
||||
induction x, y using Nat.mod.inductionOn with
|
||||
| ind x y h₁ ih =>
|
||||
rw [Nat.mod_eq_sub_mod h₁.2]
|
||||
exact ih h
|
||||
| base x y h₁ =>
|
||||
have : ¬ 0 < y ∨ ¬ y ≤ x := Iff.mp (Decidable.not_and_iff_or_not ..) h₁
|
||||
match this with
|
||||
| Or.inl h₁ => exact absurd h h₁
|
||||
| Or.inr h₁ =>
|
||||
have hgt : y > x := Nat.gt_of_not_le h₁
|
||||
rw [← Nat.mod_eq_of_lt hgt] at hgt
|
||||
assumption
|
||||
```
|
||||
|
||||
## Cases
|
||||
|
||||
TODO
|
||||
|
||||
## Injection
|
||||
|
||||
TODO
|
||||
|
||||
## Split
|
||||
|
||||
The `split` tactic can be used to split the cases of an if-then-else or
|
||||
match into new subgoals, which can then be discharged individually.
|
||||
|
||||
```lean
|
||||
def addMoreIfOdd (n : Nat) := if n % 2 = 0 then n + 1 else n + 2
|
||||
|
||||
/- Examine each branch of the conditional to show that the result
|
||||
is always positive -/
|
||||
example (n : Nat) : 0 < addMoreIfOdd n := by
|
||||
simp only [addMoreIfOdd]
|
||||
split
|
||||
next => exact Nat.zero_lt_succ _
|
||||
next => exact Nat.zero_lt_succ _
|
||||
```
|
||||
|
||||
```lean
|
||||
def binToChar (n : Nat) : Option Char :=
|
||||
match n with
|
||||
| 0 => some '0'
|
||||
| 1 => some '1'
|
||||
| _ => none
|
||||
|
||||
example (n : Nat) : (binToChar n).isSome -> n = 0 ∨ n = 1 := by
|
||||
simp only [binToChar]
|
||||
split
|
||||
next => exact fun _ => Or.inl rfl
|
||||
next => exact fun _ => Or.inr rfl
|
||||
next => intro h; cases h
|
||||
|
||||
/- Hypotheses about previous cases can be accessed by assigning them a
|
||||
name, like `ne_zero` below. Information about the matched term can also
|
||||
be preserved using the `generalizing` tactic: -/
|
||||
example (n : Nat) : (n = 0) -> (binToChar n = some '0') := by
|
||||
simp only [binToChar]
|
||||
split
|
||||
case h_1 => intro _; rfl
|
||||
case h_2 => intro h; cases h
|
||||
/- Here, we can introduce `n ≠ 0` and `n ≠ 1` this case assumes
|
||||
neither of the previous cases matched. -/
|
||||
case h_3 ne_zero _ => intro eq_zero; exact absurd eq_zero ne_zero
|
||||
```
|
||||
|
||||
## Dependent pattern matching
|
||||
|
||||
The `match-with` expression implements dependent pattern matching. You can use it to create concise proofs.
|
||||
|
||||
```lean
|
||||
inductive Mem : α → List α → Prop where
|
||||
| head (a : α) (as : List α) : Mem a (a::as)
|
||||
| tail (a b : α) (bs : List α) : Mem a bs → Mem a (b::bs)
|
||||
|
||||
infix:50 (priority := high) "∈" => Mem
|
||||
|
||||
theorem mem_split {a : α} {as : List α} (h : a ∈ as) : ∃ s t, as = s ++ a :: t :=
|
||||
match a, as, h with
|
||||
| _, _, Mem.head a bs => ⟨[], ⟨bs, rfl⟩⟩
|
||||
| _, _, Mem.tail a b bs h =>
|
||||
match bs, mem_split h with
|
||||
| _, ⟨s, ⟨t, rfl⟩⟩ => ⟨b::s, ⟨t, List.cons_append .. ▸ rfl⟩⟩
|
||||
```
|
||||
|
||||
In the tactic DSL, the right-hand-side of each alternative in a `match-with` is a sequence of tactics instead of a term.
|
||||
Here is a similar proof using the tactic DSL.
|
||||
|
||||
```lean
|
||||
# inductive Mem : α → List α → Prop where
|
||||
# | head (a : α) (as : List α) : Mem a (a::as)
|
||||
# | tail (a b : α) (bs : List α) : Mem a bs → Mem a (b::bs)
|
||||
# infix:50 (priority := high) "∈" => Mem
|
||||
theorem mem_split {a : α} {as : List α} (h : a ∈ as) : ∃ s t, as = s ++ a :: t := by
|
||||
match a, as, h with
|
||||
| _, _, Mem.head a bs => exists []; exists bs; rfl
|
||||
| _, _, Mem.tail a b bs h =>
|
||||
match bs, mem_split h with
|
||||
| _, ⟨s, ⟨t, rfl⟩⟩ =>
|
||||
exists b::s; exists t;
|
||||
rw [List.cons_append]
|
||||
```
|
||||
|
||||
We can use `match-with` nested in tactics.
|
||||
Here is a similar proof that uses the `induction` tactic instead of recursion.
|
||||
|
||||
```lean
|
||||
# inductive Mem : α → List α → Prop where
|
||||
# | head (a : α) (as : List α) : Mem a (a::as)
|
||||
# | tail (a b : α) (bs : List α) : Mem a bs → Mem a (b::bs)
|
||||
# infix:50 (priority := high) "∈" => Mem
|
||||
theorem mem_split {a : α} {as : List α} (h : a ∈ as) : ∃ s t, as = s ++ a :: t := by
|
||||
induction as with
|
||||
| nil => cases h
|
||||
| cons b bs ih => cases h with
|
||||
| head a bs => exact ⟨[], ⟨bs, rfl⟩⟩
|
||||
| tail a b bs h =>
|
||||
match bs, ih h with
|
||||
| _, ⟨s, ⟨t, rfl⟩⟩ =>
|
||||
exists b::s; exists t
|
||||
rw [List.cons_append]
|
||||
```
|
||||
|
||||
You can create your own notation using existing tactics. In the following example,
|
||||
we define a simple `obtain` tactic using macros. We say it is simple because it takes only one
|
||||
discriminant. Later, we show how to create more complex automation using macros.
|
||||
|
||||
```lean
|
||||
# inductive Mem : α → List α → Prop where
|
||||
# | head (a : α) (as : List α) : Mem a (a::as)
|
||||
# | tail (a b : α) (bs : List α) : Mem a bs → Mem a (b::bs)
|
||||
# infix:50 (priority := high) "∈" => Mem
|
||||
macro "obtain " p:term " from " d:term : tactic =>
|
||||
`(tactic| match $d:term with | $p:term => ?_)
|
||||
|
||||
theorem mem_split {a : α} {as : List α} (h : a ∈ as) : ∃ s t, as = s ++ a :: t := by
|
||||
induction as with
|
||||
| cons b bs ih => cases h with
|
||||
| tail a b bs h =>
|
||||
obtain ⟨s, ⟨t, h⟩⟩ from ih h
|
||||
exists b::s; exists t
|
||||
rw [h, List.cons_append]
|
||||
| head a bs => exact ⟨[], ⟨bs, rfl⟩⟩
|
||||
| nil => cases h
|
||||
|
||||
```
|
||||
|
||||
## Extensible tactics
|
||||
|
||||
In the following example, we define the notation `triv` for the tactic DSL using
|
||||
the command `syntax`. Then, we use the command `macro_rules` to specify what should
|
||||
be done when `triv` is used. You can provide different expansions, and the tactic DSL
|
||||
interpreter will try all of them until one succeeds.
|
||||
|
||||
```lean
|
||||
-- Define a new notation for the tactic DSL
|
||||
syntax "triv" : tactic
|
||||
|
||||
macro_rules
|
||||
| `(tactic| triv) => `(tactic| assumption)
|
||||
|
||||
theorem ex1 (h : p) : p := by
|
||||
triv
|
||||
|
||||
-- You cannot prove the following theorem using `triv`
|
||||
-- theorem ex2 (x : α) : x = x := by
|
||||
-- triv
|
||||
|
||||
-- Let's extend `triv`. The `by` DSL interpreter
|
||||
-- tries all possible macro extensions for `triv` until one succeeds
|
||||
macro_rules
|
||||
| `(tactic| triv) => `(tactic| rfl)
|
||||
|
||||
theorem ex2 (x : α) : x = x := by
|
||||
triv
|
||||
|
||||
theorem ex3 (x : α) (h : p) : x = x ∧ p := by
|
||||
apply And.intro <;> triv
|
||||
```
|
||||
|
||||
# `let-rec`
|
||||
|
||||
You can use `let rec` to write local recursive functions. We lifted it to the tactic DSL,
|
||||
and you can use it to create proofs by induction.
|
||||
|
||||
```lean
|
||||
theorem length_replicateTR {α} (n : Nat) (a : α) : (List.replicateTR n a).length = n := by
|
||||
let rec aux (n : Nat) (as : List α)
|
||||
: (List.replicateTR.loop a n as).length = n + as.length := by
|
||||
match n with
|
||||
| 0 => rw [Nat.zero_add]; rfl
|
||||
| n+1 =>
|
||||
show List.length (List.replicateTR.loop a n (a::as)) = Nat.succ n + as.length
|
||||
rw [aux n, List.length_cons, Nat.add_succ, Nat.succ_add]
|
||||
exact aux n []
|
||||
```
|
||||
|
||||
You can also introduce auxiliary recursive declarations using `where` clause after your definition.
|
||||
Lean converts them into a `let rec`.
|
||||
|
||||
```lean
|
||||
theorem length_replicateTR {α} (n : Nat) (a : α) : (List.replicateTR n a).length = n :=
|
||||
loop n []
|
||||
where
|
||||
loop n as : (List.replicateTR.loop a n as).length = n + as.length := by
|
||||
match n with
|
||||
| 0 => rw [Nat.zero_add]; rfl
|
||||
| n+1 =>
|
||||
show List.length (List.replicateTR.loop a n (a::as)) = Nat.succ n + as.length
|
||||
rw [loop n, List.length_cons, Nat.add_succ, Nat.succ_add]
|
||||
```
|
||||
|
||||
# `begin-end` lovers
|
||||
|
||||
If you love Lean 3 `begin ... end` tactic blocks and commas, you can define this notation
|
||||
in Lean 4 using macros in a few lines of code.
|
||||
|
||||
```lean
|
||||
{{#include ../tests/lean/beginEndAsMacro.lean:doc}}
|
||||
```
|
||||
200
doc/tour.md
200
doc/tour.md
@@ -1,200 +0,0 @@
|
||||
# Tour of Lean
|
||||
|
||||
The best way to learn about Lean is to read and write Lean code.
|
||||
This article will act as a tour through some of the key features of the Lean
|
||||
language and give you some code snippets that you can execute on your machine.
|
||||
To learn about setting up a development environment, check out [Setting Up Lean](./setup.md).
|
||||
|
||||
There are two primary concepts in Lean: functions and types.
|
||||
This tour will emphasize features of the language which fall into
|
||||
these two concepts.
|
||||
|
||||
# Functions and Namespaces
|
||||
|
||||
The most fundamental pieces of any Lean program are functions organized into namespaces.
|
||||
[Functions](./functions.md) perform work on inputs to produce outputs,
|
||||
and they are organized under [namespaces](./namespaces.md),
|
||||
which are the primary way you group things in Lean.
|
||||
They are defined using the `def` command,
|
||||
which give the function a name and define its arguments.
|
||||
|
||||
```lean
|
||||
namespace BasicFunctions
|
||||
|
||||
-- The `#eval` command evaluates an expression on the fly and prints the result.
|
||||
#eval 2+2
|
||||
|
||||
-- You use 'def' to define a function. This one accepts a natural number
|
||||
-- and returns a natural number.
|
||||
-- Parentheses are optional for function arguments, except for when
|
||||
-- you use an explicit type annotation.
|
||||
-- Lean can often infer the type of the function's arguments.
|
||||
def sampleFunction1 x := x*x + 3
|
||||
|
||||
-- Apply the function, naming the function return result using 'def'.
|
||||
-- The variable type is inferred from the function return type.
|
||||
def result1 := sampleFunction1 4573
|
||||
|
||||
-- This line uses an interpolated string to print the result. Expressions inside
|
||||
-- braces `{}` are converted into strings using the polymorphic method `toString`
|
||||
#eval println! "The result of squaring the integer 4573 and adding 3 is {result1}"
|
||||
|
||||
-- When needed, annotate the type of a parameter name using '(argument : type)'.
|
||||
def sampleFunction2 (x : Nat) := 2*x*x - x + 3
|
||||
|
||||
def result2 := sampleFunction2 (7 + 4)
|
||||
|
||||
#eval println! "The result of applying the 2nd sample function to (7 + 4) is {result2}"
|
||||
|
||||
-- Conditionals use if/then/else
|
||||
def sampleFunction3 (x : Int) :=
|
||||
if x > 100 then
|
||||
2*x*x - x + 3
|
||||
else
|
||||
2*x*x + x - 37
|
||||
|
||||
#eval println! "The result of applying sampleFunction3 to 2 is {sampleFunction3 2}"
|
||||
|
||||
end BasicFunctions
|
||||
```
|
||||
|
||||
```lean
|
||||
-- Lean has first-class functions.
|
||||
-- `twice` takes two arguments `f` and `a` where
|
||||
-- `f` is a function from natural numbers to natural numbers, and
|
||||
-- `a` is a natural number.
|
||||
def twice (f : Nat → Nat) (a : Nat) :=
|
||||
f (f a)
|
||||
|
||||
-- `fun` is used to declare anonymous functions
|
||||
#eval twice (fun x => x + 2) 10
|
||||
|
||||
-- You can prove theorems about your functions.
|
||||
-- The following theorem states that for any natural number `a`,
|
||||
-- adding 2 twice produces a value equal to `a + 4`.
|
||||
theorem twiceAdd2 (a : Nat) : twice (fun x => x + 2) a = a + 4 :=
|
||||
-- The proof is by reflexivity. Lean "symbolically" reduces both sides of the equality
|
||||
-- until they are identical.
|
||||
rfl
|
||||
|
||||
-- `(· + 2)` is syntax sugar for `(fun x => x + 2)`. The parentheses + `·` notation
|
||||
-- is useful for defining simple anonymous functions.
|
||||
#eval twice (· + 2) 10
|
||||
|
||||
-- Enumerated types are a special case of inductive types in Lean,
|
||||
-- which we will learn about later.
|
||||
-- The following command creates a new type `Weekday`.
|
||||
inductive Weekday where
|
||||
| sunday : Weekday
|
||||
| monday : Weekday
|
||||
| tuesday : Weekday
|
||||
| wednesday : Weekday
|
||||
| thursday : Weekday
|
||||
| friday : Weekday
|
||||
| saturday : Weekday
|
||||
|
||||
-- `Weekday` has 7 constructors/elements.
|
||||
-- The constructors live in the `Weekday` namespace.
|
||||
-- Think of `sunday`, `monday`, …, `saturday` as being distinct elements of `Weekday`,
|
||||
-- with no other distinguishing properties.
|
||||
-- The command `#check` prints the type of a term in Lean.
|
||||
#check Weekday.sunday
|
||||
#check Weekday.monday
|
||||
|
||||
-- The `open` command opens a namespace, making all declarations in it accessible without
|
||||
-- qualification.
|
||||
open Weekday
|
||||
#check sunday
|
||||
#check tuesday
|
||||
|
||||
-- You can define functions by pattern matching.
|
||||
-- The following function converts a `Weekday` into a natural number.
|
||||
def natOfWeekday (d : Weekday) : Nat :=
|
||||
match d with
|
||||
| sunday => 1
|
||||
| monday => 2
|
||||
| tuesday => 3
|
||||
| wednesday => 4
|
||||
| thursday => 5
|
||||
| friday => 6
|
||||
| saturday => 7
|
||||
|
||||
#eval natOfWeekday tuesday
|
||||
|
||||
def isMonday : Weekday → Bool :=
|
||||
-- `fun` + `match` is a common idiom.
|
||||
-- The following expression is syntax sugar for
|
||||
-- `fun d => match d with | monday => true | _ => false`.
|
||||
fun
|
||||
| monday => true
|
||||
| _ => false
|
||||
|
||||
#eval isMonday monday
|
||||
#eval isMonday sunday
|
||||
|
||||
-- Lean has support for type classes and polymorphic methods.
|
||||
-- The `toString` method converts a value into a `String`.
|
||||
#eval toString 10
|
||||
#eval toString (10, 20)
|
||||
|
||||
-- The method `toString` converts values of any type that implements
|
||||
-- the class `ToString`.
|
||||
-- You can implement instances of `ToString` for your own types.
|
||||
instance : ToString Weekday where
|
||||
toString (d : Weekday) : String :=
|
||||
match d with
|
||||
| sunday => "Sunday"
|
||||
| monday => "Monday"
|
||||
| tuesday => "Tuesday"
|
||||
| wednesday => "Wednesday"
|
||||
| thursday => "Thursday"
|
||||
| friday => "Friday"
|
||||
| saturday => "Saturday"
|
||||
|
||||
#eval toString (sunday, 10)
|
||||
|
||||
def Weekday.next (d : Weekday) : Weekday :=
|
||||
match d with
|
||||
| sunday => monday
|
||||
| monday => tuesday
|
||||
| tuesday => wednesday
|
||||
| wednesday => thursday
|
||||
| thursday => friday
|
||||
| friday => saturday
|
||||
| saturday => sunday
|
||||
|
||||
#eval Weekday.next Weekday.wednesday
|
||||
-- Since the `Weekday` namespace has already been opened, you can also write
|
||||
#eval next wednesday
|
||||
|
||||
-- Matching on a parameter like in the previous definition
|
||||
-- is so common that Lean provides syntax sugar for it. The following
|
||||
-- function uses it.
|
||||
def Weekday.previous : Weekday -> Weekday
|
||||
| sunday => saturday
|
||||
| monday => sunday
|
||||
| tuesday => monday
|
||||
| wednesday => tuesday
|
||||
| thursday => wednesday
|
||||
| friday => thursday
|
||||
| saturday => friday
|
||||
|
||||
#eval next (previous wednesday)
|
||||
|
||||
-- We can prove that for any `Weekday` `d`, `next (previous d) = d`
|
||||
theorem Weekday.nextOfPrevious (d : Weekday) : next (previous d) = d :=
|
||||
match d with
|
||||
| sunday => rfl
|
||||
| monday => rfl
|
||||
| tuesday => rfl
|
||||
| wednesday => rfl
|
||||
| thursday => rfl
|
||||
| friday => rfl
|
||||
| saturday => rfl
|
||||
|
||||
-- You can automate definitions such as `Weekday.nextOfPrevious`
|
||||
-- using metaprogramming (or "tactics").
|
||||
theorem Weekday.nextOfPrevious' (d : Weekday) : next (previous d) = d := by
|
||||
cases d -- A proof by case distinction
|
||||
all_goals rfl -- Each case is solved using `rfl`
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
Theorem Proving in Lean
|
||||
=======================
|
||||
|
||||
We strongly encourage you to read the book [Theorem Proving in Lean](https://lean-lang.org/theorem_proving_in_lean4/title_page.html).
|
||||
Many Lean users consider it to be the Lean Bible.
|
||||
129
doc/typeobjs.md
129
doc/typeobjs.md
@@ -1,129 +0,0 @@
|
||||
## Types as objects
|
||||
|
||||
One way in which Lean's dependent type theory extends simple type theory is that types themselves --- entities like ``Nat`` and ``Bool`` ---
|
||||
are first-class citizens, which is to say that they themselves are objects. For that to be the case, each of them also has to have a type.
|
||||
|
||||
```lean
|
||||
#check Nat -- Type
|
||||
#check Bool -- Type
|
||||
#check Nat → Bool -- Type
|
||||
#check Nat × Bool -- Type
|
||||
#check Nat → Nat -- ...
|
||||
#check Nat × Nat → Nat
|
||||
#check Nat → Nat → Nat
|
||||
#check Nat → (Nat → Nat)
|
||||
#check Nat → Nat → Bool
|
||||
#check (Nat → Nat) → Nat
|
||||
```
|
||||
|
||||
We see that each one of the expressions above is an object of type ``Type``. We can also declare new constants and constructors for types:
|
||||
|
||||
```lean
|
||||
constant α : Type
|
||||
constant β : Type
|
||||
constant F : Type → Type
|
||||
constant G : Type → Type → Type
|
||||
|
||||
#check α -- Type
|
||||
#check F α -- Type
|
||||
#check F Nat -- Type
|
||||
#check G α -- Type → Type
|
||||
#check G α β -- Type
|
||||
#check G α Nat -- Type
|
||||
```
|
||||
|
||||
Indeed, we have already seen an example of a function of type ``Type → Type → Type``, namely, the Cartesian product.
|
||||
|
||||
```lean
|
||||
constant α : Type
|
||||
constant β : Type
|
||||
|
||||
#check Prod α β -- Type
|
||||
#check Prod Nat Nat -- Type
|
||||
```
|
||||
|
||||
Here is another example: given any type ``α``, the type ``List α`` denotes the type of lists of elements of type ``α``.
|
||||
|
||||
```lean
|
||||
constant α : Type
|
||||
|
||||
#check List α -- Type
|
||||
#check List Nat -- Type
|
||||
```
|
||||
|
||||
Given that every expression in Lean has a type, it is natural to ask: what type does ``Type`` itself have?
|
||||
|
||||
```lean
|
||||
#check Type -- Type 1
|
||||
```
|
||||
|
||||
We have actually come up against one of the most subtle aspects of Lean's typing system.
|
||||
Lean's underlying foundation has an infinite hierarchy of types:
|
||||
|
||||
```lean
|
||||
#check Type -- Type 1
|
||||
#check Type 1 -- Type 2
|
||||
#check Type 2 -- Type 3
|
||||
#check Type 3 -- Type 4
|
||||
#check Type 4 -- Type 5
|
||||
```
|
||||
|
||||
Think of ``Type 0`` as a universe of "small" or "ordinary" types.
|
||||
``Type 1`` is then a larger universe of types, which contains ``Type 0`` as an element,
|
||||
and ``Type 2`` is an even larger universe of types, which contains ``Type 1`` as an element.
|
||||
The list is indefinite, so that there is a ``Type n`` for every natural number ``n``.
|
||||
``Type`` is an abbreviation for ``Type 0``:
|
||||
|
||||
```lean
|
||||
#check Type
|
||||
#check Type 0
|
||||
```
|
||||
|
||||
There is also another type universe, ``Prop``, which has special properties.
|
||||
|
||||
```lean
|
||||
#check Prop -- Type
|
||||
```
|
||||
|
||||
We will discuss ``Prop`` later.
|
||||
|
||||
We want some operations, however, to be *polymorphic* over type universes. For example, ``List α`` should
|
||||
make sense for any type ``α``, no matter which type universe ``α`` lives in. This explains the type annotation of the function ``List``:
|
||||
|
||||
```lean
|
||||
#check List -- Type u_1 → Type u_1
|
||||
```
|
||||
|
||||
Here ``u_1`` is a variable ranging over type levels. The output of the ``#check`` command means that whenever ``α`` has type ``Type n``, ``List α`` also has type ``Type n``. The function ``Prod`` is similarly polymorphic:
|
||||
|
||||
```lean
|
||||
#check Prod -- Type u_1 → Type u_2 → Type (max u_1 u_2)
|
||||
```
|
||||
|
||||
To define polymorphic constants and variables, Lean allows us to declare universe variables explicitly using the `universe` command:
|
||||
|
||||
```lean
|
||||
universe u
|
||||
constant α : Type u
|
||||
#check α
|
||||
```
|
||||
Equivalently, we can write ``Type _`` to avoid giving the arbitrary universe a name:
|
||||
|
||||
```lean
|
||||
constant α : Type _
|
||||
#check α
|
||||
```
|
||||
|
||||
Several Lean 3 users use the shorthand `Type*` for `Type _`. The `Type*` notation is not builtin in Lean 4, but you can easily define it using a `macro`.
|
||||
```lean
|
||||
macro "Type*" : term => `(Type _)
|
||||
|
||||
def f (α : Type*) (a : α) := a
|
||||
|
||||
def g (α : Type _) (a : α) := a
|
||||
|
||||
#check f
|
||||
#check g
|
||||
```
|
||||
|
||||
We explain later how the `macro` command works.
|
||||
56
doc/types.md
56
doc/types.md
@@ -1,56 +0,0 @@
|
||||
# Types
|
||||
|
||||
Every programming language needs a type system and
|
||||
Lean has a rich extensible inductive type system.
|
||||
|
||||
## Basic Types
|
||||
|
||||
Lean has built in support for the following basic types:
|
||||
|
||||
- [Bool](bool.md) : a `true` or `false` value.
|
||||
- [Int](integers.md) : multiple precision integers (with no overflows!).
|
||||
|
||||
- [Nat](integers.md) : natural numbers, or non-negative integers (also with no overflows!).
|
||||
- [Float](float.md): floating point numbers.
|
||||
- [Char](char.md): a Unicode character.
|
||||
- [String](string.md): a UTF-8 encoded string of characters.
|
||||
- [Array](array.md): a dynamic (aka growable) array of typed objects.
|
||||
- [List](list.md): a linked list of typed objects.
|
||||
- TODO: what else?
|
||||
|
||||
And Lean allows you to create your own custom types using:
|
||||
- [Enumerated Types](enum.md): a special case of inductive types.
|
||||
- [Type Classes](typeclasses.md): a way of creating custom polymorphism.
|
||||
- [Types as objects](typeobjs.md): a way of manipulating types themselves.
|
||||
- [Structures](struct.md): a collection of named and typed fields. A
|
||||
structure is actually special case of inductive datatype.
|
||||
- [Inductive Types](inductive.md): TODO: add one liner...
|
||||
|
||||
## Universes
|
||||
|
||||
Every type in Lean is, by definition, an expression of type `Sort u`
|
||||
for some universe level `u`. A universe level is one of the
|
||||
following:
|
||||
|
||||
* a natural number, `n`
|
||||
* a universe variable, `u` (declared with the command `universe` or `universes`)
|
||||
* an expression `u + n`, where `u` is a universe level and `n` is a natural number
|
||||
* an expression `max u v`, where `u` and `v` are universes
|
||||
* an expression `imax u v`, where `u` and `v` are universe levels
|
||||
|
||||
The last one denotes the universe level `0` if `v` is `0`, and `max u v` otherwise.
|
||||
|
||||
```lean
|
||||
universe u v
|
||||
|
||||
#check Sort u -- Type u
|
||||
#check Sort 5 -- Type 4 : Type 5
|
||||
#check Sort (u + 1) -- Type u : Type (u + 1)
|
||||
#check Sort (u + 3) -- Type (u + 2) : Type (u + 3)
|
||||
#check Sort (max u v) -- Sort (max u v) : Type (max u v)
|
||||
#check Sort (max (u + 3) v) -- Sort (max (u + 3) v) : Type (max (u + 3) v)
|
||||
#check Sort (imax (u + 3) v) -- Sort (imax (u + 3) v) : Type (imax (u + 3) v)
|
||||
#check Prop -- Type
|
||||
#check Type -- Type 1
|
||||
#check Type 1 -- Type 1 : Type 2
|
||||
```
|
||||
@@ -1 +0,0 @@
|
||||
# Using Lean
|
||||
@@ -1,42 +0,0 @@
|
||||
# What is Lean
|
||||
|
||||
Lean is a functional programming language that makes it easy to
|
||||
write correct and maintainable code.
|
||||
You can also use Lean as an interactive theorem prover.
|
||||
|
||||
Lean programming primarily involves defining types and functions.
|
||||
This allows your focus to remain on the problem domain and manipulating its data,
|
||||
rather than the details of programming.
|
||||
|
||||
```lean
|
||||
-- Defines a function that takes a name and produces a greeting.
|
||||
def getGreeting (name : String) := s!"Hello, {name}! Isn't Lean great?"
|
||||
|
||||
-- The `main` function is the entry point of your program.
|
||||
-- Its type is `IO Unit` because it can perform `IO` operations (side effects).
|
||||
def main : IO Unit :=
|
||||
-- Define a list of names
|
||||
let names := ["Sebastian", "Leo", "Daniel"]
|
||||
|
||||
-- Map each name to a greeting
|
||||
let greetings := names.map getGreeting
|
||||
|
||||
-- Print the list of greetings
|
||||
for greeting in greetings do
|
||||
IO.println greeting
|
||||
```
|
||||
|
||||
Lean has numerous features, including:
|
||||
|
||||
- Type inference
|
||||
- First-class functions
|
||||
- Powerful data types
|
||||
- Pattern matching
|
||||
- [Type classes](./typeclass.md)
|
||||
- [Monads](./monads/intro.md)
|
||||
- [Extensible syntax](./syntax.md)
|
||||
- Hygienic macros
|
||||
- [Dependent types](https://lean-lang.org/theorem_proving_in_lean4/dependent_type_theory.html)
|
||||
- [Metaprogramming](./macro_overview.md)
|
||||
- Multithreading
|
||||
- Verification: you can prove properties of your functions using Lean itself
|
||||
@@ -8,12 +8,12 @@ open Lean.JsonRpc
|
||||
Tests language server memory use by repeatedly re-elaborate a given file.
|
||||
|
||||
NOTE: only works on Linux for now.
|
||||
ot to touch the imports for usual files.
|
||||
-/
|
||||
|
||||
def main (args : List String) : IO Unit := do
|
||||
let leanCmd :: file :: iters :: args := args | panic! "usage: script <lean> <file> <#iterations> <server-args>..."
|
||||
let uri := s!"file:///{file}"
|
||||
let file ← IO.FS.realPath file
|
||||
let uri := s!"file://{file}"
|
||||
Ipc.runWith leanCmd (#["--worker", "-DstderrAsMessages=false"] ++ args ++ #[uri]) do
|
||||
-- for use with heaptrack:
|
||||
--Ipc.runWith "heaptrack" (#[leanCmd, "--worker", "-DstderrAsMessages=false"] ++ args ++ #[uri]) do
|
||||
|
||||
@@ -47,10 +47,10 @@ def run_command(command, check=True, capture_output=True):
|
||||
|
||||
|
||||
def clone_repo(repo, temp_dir):
|
||||
"""Clone the repository to a temporary directory using shallow clone."""
|
||||
print(f"Shallow cloning {repo}...")
|
||||
# Keep the shallow clone for efficiency
|
||||
clone_result = run_command(f"gh repo clone {repo} {temp_dir} -- --depth=1", check=False)
|
||||
"""Clone the repository to a temporary directory."""
|
||||
print(f"Cloning {repo}...")
|
||||
# Remove shallow clone for better merge detection
|
||||
clone_result = run_command(f"gh repo clone {repo} {temp_dir}", check=False)
|
||||
if clone_result.returncode != 0:
|
||||
print(f"Failed to clone repository {repo}.")
|
||||
print(f"Error: {clone_result.stderr}")
|
||||
@@ -95,26 +95,16 @@ def check_and_merge(repo, branch, tag, temp_dir):
|
||||
if checkout_result.returncode != 0:
|
||||
return False
|
||||
|
||||
# Try merging the tag in a dry-run to check if it can be merged cleanly
|
||||
print(f"Checking if {tag} can be merged cleanly into {branch}...")
|
||||
merge_check = run_command(f"git merge --no-commit --no-ff {tag}", check=False)
|
||||
# Try merging the tag directly
|
||||
print(f"Merging {tag} into {branch}...")
|
||||
merge_result = run_command(f"git merge {tag} --no-edit", check=False)
|
||||
|
||||
if merge_check.returncode != 0:
|
||||
if merge_result.returncode != 0:
|
||||
print(f"Cannot merge {tag} cleanly into {branch}.")
|
||||
print("Merge conflicts would occur. Aborting merge.")
|
||||
run_command("git merge --abort")
|
||||
return False
|
||||
|
||||
# Abort the test merge
|
||||
run_command("git reset --hard HEAD")
|
||||
|
||||
# Now perform the actual merge and push to remote
|
||||
print(f"Merging {tag} into {branch}...")
|
||||
merge_result = run_command(f"git merge {tag} --no-edit")
|
||||
if merge_result.returncode != 0:
|
||||
print(f"Failed to merge {tag} into {branch}.")
|
||||
return False
|
||||
|
||||
print(f"Pushing changes to remote...")
|
||||
push_result = run_command(f"git push origin {branch}")
|
||||
if push_result.returncode != 0:
|
||||
|
||||
@@ -55,7 +55,8 @@ $CP $GLIBC/lib/libc_nonshared.a stage1/lib/glibc
|
||||
$CP $GLIBC/lib/libpthread_nonshared.a stage1/lib/glibc
|
||||
for f in $GLIBC/lib/{ld,lib{c,dl,m,rt,pthread}}-*; do b=$(basename $f); cp $f stage1/lib/glibc/${b%-*}.so; done
|
||||
OPTIONS=()
|
||||
echo -n " -DLEAN_STANDALONE=ON"
|
||||
# We build cadical using the custom toolchain on Linux to avoid glibc versioning issues
|
||||
echo -n " -DLEAN_STANDALONE=ON -DCADICAL_USE_CUSTOM_CXX=ON"
|
||||
echo -n " -DCMAKE_CXX_COMPILER=$PWD/llvm-host/bin/clang++ -DLEAN_CXX_STDLIB='-Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic'"
|
||||
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/include ${EXTRA_FLAGS:-}'"
|
||||
# use target compiler directly when not cross-compiling
|
||||
@@ -67,8 +68,9 @@ fi
|
||||
# use `-nostdinc` to make sure headers are not visible by default (in particular, not to `#include_next` in the clang headers),
|
||||
# but do not change sysroot so users can still link against system libs
|
||||
echo -n " -DLEANC_INTERNAL_FLAGS='--sysroot ROOT -nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang"
|
||||
# ld.so is usually included by the libc.so linker script but we discard those
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='--sysroot ROOT -L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a ROOT/lib/glibc/libpthread_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -luv -Wl,-Bdynamic ROOT/lib/glibc/ld.so -Wl,--no-as-needed -fuse-ld=lld'"
|
||||
# ld.so is usually included by the libc.so linker script but we discard those. Make sure it is linked to only after `libc.so` like in the original
|
||||
# linker script so that no libc symbols are bound to it instead.
|
||||
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='--sysroot ROOT -L ROOT/lib -L ROOT/lib/glibc -lc -lc_nonshared -Wl,--as-needed -l:ld.so -Wl,--no-as-needed -lpthread_nonshared -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -luv -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
|
||||
# when not using the above flags, link GMP dynamically/as usual
|
||||
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -luv -lpthread -ldl -lrt -Wl,--no-as-needed'"
|
||||
# do not set `LEAN_CC` for tests
|
||||
|
||||
@@ -7,6 +7,7 @@ import base64
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import re # Import re module
|
||||
# Import run_command from merge_remote.py
|
||||
from merge_remote import run_command
|
||||
|
||||
@@ -58,13 +59,29 @@ def release_page_exists(repo_url, tag_name, github_token):
|
||||
response = requests.get(api_url, headers=headers)
|
||||
return response.status_code == 200
|
||||
|
||||
def get_release_notes(repo_url, tag_name, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/releases/tags/{tag_name}"
|
||||
headers = {'Authorization': f'token {github_token}'} if github_token else {}
|
||||
response = requests.get(api_url, headers=headers)
|
||||
if response.status_code == 200:
|
||||
return response.json().get("body", "").strip()
|
||||
return None
|
||||
def get_release_notes(tag_name):
|
||||
"""Fetch release notes page title from lean-lang.org."""
|
||||
# Strip -rcX suffix if present for the URL
|
||||
base_tag = tag_name.split('-')[0]
|
||||
reference_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
|
||||
try:
|
||||
response = requests.get(reference_url)
|
||||
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
|
||||
|
||||
# Extract title using regex
|
||||
match = re.search(r"<title>(.*?)</title>", response.text, re.IGNORECASE | re.DOTALL)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
else:
|
||||
print(f" ⚠️ Could not find <title> tag in {reference_url}")
|
||||
return None
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f" ❌ Error fetching release notes from {reference_url}: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f" ❌ An unexpected error occurred while processing release notes: {e}")
|
||||
return None
|
||||
|
||||
def get_branch_content(repo_url, branch, file_path, github_token):
|
||||
api_url = repo_url.replace("https://github.com/", "https://api.github.com/repos/") + f"/contents/{file_path}?ref={branch}"
|
||||
@@ -255,6 +272,7 @@ def main():
|
||||
branch_name = f"releases/v{version_major}.{version_minor}.0"
|
||||
if not branch_exists(lean_repo_url, branch_name, github_token):
|
||||
print(f" ❌ Branch {branch_name} does not exist")
|
||||
print(f" 🟡 After creating the branch, we'll need to check CMake version settings.")
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ✅ Branch {branch_name} exists")
|
||||
@@ -274,14 +292,22 @@ def main():
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ✅ Release page for {toolchain} exists")
|
||||
release_notes = get_release_notes(lean_repo_url, toolchain, github_token)
|
||||
if not (release_notes and toolchain in release_notes.splitlines()[0].strip()):
|
||||
previous_minor_version = version_minor - 1
|
||||
previous_release = f"v{version_major}.{previous_minor_version}.0"
|
||||
print(f" ❌ Release notes not published. Please run `script/release_notes.py --since {previous_release}` on branch `{branch_name}`.")
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ✅ Release notes look good.")
|
||||
|
||||
# Check the actual release notes page title
|
||||
actual_title = get_release_notes(toolchain)
|
||||
expected_title_prefix = f"Lean {toolchain.lstrip('v')}" # e.g., "Lean 4.19.0" or "Lean 4.19.0-rc1"
|
||||
|
||||
if actual_title is None:
|
||||
# Error already printed by get_release_notes
|
||||
lean4_success = False
|
||||
elif not actual_title.startswith(expected_title_prefix):
|
||||
# Construct URL for the error message (using the base tag)
|
||||
base_tag = toolchain.split('-')[0]
|
||||
check_url = f"https://lean-lang.org/doc/reference/latest/releases/{base_tag}/"
|
||||
print(f" ❌ Release notes page title mismatch. Expected prefix '{expected_title_prefix}', got '{actual_title}'. Check {check_url}")
|
||||
lean4_success = False
|
||||
else:
|
||||
print(f" ✅ Release notes page title looks good ('{actual_title}').")
|
||||
|
||||
repo_status["lean4"] = lean4_success
|
||||
|
||||
@@ -360,10 +386,24 @@ def main():
|
||||
if check_stable and not is_release_candidate(toolchain):
|
||||
if not is_merged_into_stable(url, toolchain, "stable", github_token, verbose):
|
||||
org_repo = extract_org_repo_from_url(url)
|
||||
print(f" ❌ Tag {toolchain} is not merged into stable")
|
||||
print(f" Run `script/merge_remote.py {org_repo} stable {toolchain}` to merge it")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
if args.dry_run:
|
||||
print(f" ❌ Tag {toolchain} is not merged into stable")
|
||||
print(f" Run `script/merge_remote.py {org_repo} stable {toolchain}` to merge it")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
else:
|
||||
print(f" … Tag {toolchain} is not merged into stable. Running `script/merge_remote.py {org_repo} stable {toolchain}`...")
|
||||
|
||||
# Run the script to merge the tag
|
||||
subprocess.run(["script/merge_remote.py", org_repo, "stable", toolchain])
|
||||
|
||||
# Check again if the tag is merged now
|
||||
if not is_merged_into_stable(url, toolchain, "stable", github_token, verbose):
|
||||
print(f" ❌ Manual intervention required.")
|
||||
repo_status[name] = False
|
||||
continue
|
||||
|
||||
# This will print in all successful cases - whether tag was merged initially or was merged successfully
|
||||
print(f" ✅ Tag {toolchain} is merged into stable")
|
||||
|
||||
if check_bump:
|
||||
|
||||
@@ -21,12 +21,19 @@ repositories:
|
||||
branch: master
|
||||
dependencies: []
|
||||
|
||||
- name: lean4-cli
|
||||
url: https://github.com/leanprover/lean4-cli
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies: []
|
||||
|
||||
- name: doc-gen4
|
||||
url: https://github.com/leanprover/doc-gen4
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies: []
|
||||
dependencies: [lean4-cli]
|
||||
|
||||
- name: verso
|
||||
url: https://github.com/leanprover/verso
|
||||
@@ -42,20 +49,13 @@ repositories:
|
||||
branch: main
|
||||
dependencies: [verso]
|
||||
|
||||
- name: lean4-cli
|
||||
url: https://github.com/leanprover/lean4-cli
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies: []
|
||||
|
||||
- name: ProofWidgets4
|
||||
url: https://github.com/leanprover-community/ProofWidgets4
|
||||
toolchain-tag: false
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies:
|
||||
- Batteries
|
||||
- batteries
|
||||
|
||||
- name: aesop
|
||||
url: https://github.com/leanprover-community/aesop
|
||||
@@ -63,7 +63,7 @@ repositories:
|
||||
stable-branch: true
|
||||
branch: master
|
||||
dependencies:
|
||||
- Batteries
|
||||
- batteries
|
||||
|
||||
- name: import-graph
|
||||
url: https://github.com/leanprover-community/import-graph
|
||||
@@ -71,8 +71,8 @@ repositories:
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies:
|
||||
- Cli
|
||||
- Batteries
|
||||
- lean4-cli
|
||||
- batteries
|
||||
|
||||
- name: plausible
|
||||
url: https://github.com/leanprover-community/plausible
|
||||
@@ -88,10 +88,11 @@ repositories:
|
||||
branch: master
|
||||
bump-branch: true
|
||||
dependencies:
|
||||
- Aesop
|
||||
- aesop
|
||||
- ProofWidgets4
|
||||
- lean4checker
|
||||
- Batteries
|
||||
- batteries
|
||||
- lean4-cli
|
||||
- doc-gen4
|
||||
- import-graph
|
||||
- plausible
|
||||
@@ -102,4 +103,4 @@ repositories:
|
||||
stable-branch: true
|
||||
branch: master
|
||||
dependencies:
|
||||
- Mathlib
|
||||
- mathlib4
|
||||
|
||||
@@ -68,7 +68,7 @@ def generate_script(repo, version, config):
|
||||
]
|
||||
|
||||
# Special cases for specific repositories
|
||||
if repo_name == "REPL":
|
||||
if repo_name == "repl":
|
||||
script_lines.extend([
|
||||
"lake update",
|
||||
"cd test/Mathlib",
|
||||
@@ -79,7 +79,7 @@ def generate_script(repo, version, config):
|
||||
"./test.sh"
|
||||
])
|
||||
elif dependencies:
|
||||
script_lines.append('echo "Please update the dependencies in lakefile.{lean,toml}"')
|
||||
script_lines.append('perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*')
|
||||
script_lines.append("lake update")
|
||||
|
||||
script_lines.append("")
|
||||
@@ -89,13 +89,20 @@ def generate_script(repo, version, config):
|
||||
""
|
||||
])
|
||||
|
||||
if re.search(r'rc\d+$', version) and repo_name in ["Batteries", "Mathlib"]:
|
||||
if re.search(r'rc\d+$', version) and repo_name in ["batteries", "mathlib4"]:
|
||||
script_lines.extend([
|
||||
"echo 'This repo has nightly-testing infrastructure'",
|
||||
f"git merge origin/bump/{version.split('-rc')[0]}",
|
||||
"echo 'Please resolve any conflicts.'",
|
||||
""
|
||||
])
|
||||
if re.search(r'rc\d+$', version) and repo_name in ["verso", "reference-manual"]:
|
||||
script_lines.extend([
|
||||
"echo 'This repo does development on nightly-testing: remember to rebase merge the PR.'",
|
||||
f"git merge origin/nightly-testing",
|
||||
"echo 'Please resolve any conflicts.'",
|
||||
""
|
||||
])
|
||||
if repo_name != "Mathlib":
|
||||
script_lines.extend([
|
||||
"lake build && if lake check-test; then lake test; fi",
|
||||
@@ -104,7 +111,7 @@ def generate_script(repo, version, config):
|
||||
|
||||
script_lines.extend([
|
||||
'gh pr create --title "chore: bump toolchain to ' + version + '" --body ""',
|
||||
"echo 'Please review the PR and merge it.'",
|
||||
"echo 'Please review the PR and merge or rebase it.'",
|
||||
""
|
||||
])
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 20)
|
||||
set(LEAN_VERSION_MINOR 21)
|
||||
set(LEAN_VERSION_PATCH 0)
|
||||
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
|
||||
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
|
||||
@@ -511,7 +511,10 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
# import libraries created by the stdlib.make targets
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared")
|
||||
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup")
|
||||
# The second flag is necessary to even *load* dylibs without resolved symbols, as can happen
|
||||
# if a Lake `extern_lib` depends on a symbols defined by the Lean library but is loaded even
|
||||
# before definition.
|
||||
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup -Wl,-no_fixup_chains")
|
||||
endif()
|
||||
# Linux ignores undefined symbols in shared libraries by default
|
||||
|
||||
@@ -686,7 +689,7 @@ add_custom_target(make_stdlib ALL
|
||||
# The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server
|
||||
# for a parallelized nested build, but CMake doesn't let us do that.
|
||||
# We use `lean` from the previous stage, but `leanc`, headers, etc. from the current stage
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean Leanc
|
||||
VERBATIM)
|
||||
|
||||
# if we have LLVM enabled, then build `lean.h.bc` which has the LLVM bitcode
|
||||
@@ -765,7 +768,7 @@ if(${STAGE} GREATER 0 AND EXISTS ${LEAN_SOURCE_DIR}/Leanc.lean AND NOT ${CMAKE_S
|
||||
add_custom_target(leanc ALL
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/leanc
|
||||
DEPENDS leanshared
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Leanc
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanc
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
@@ -820,7 +823,6 @@ endif()
|
||||
|
||||
# Escape for `make`. Yes, twice.
|
||||
string(REPLACE "$" "\\\$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}")
|
||||
configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make)
|
||||
|
||||
# hacky
|
||||
|
||||
@@ -40,5 +40,5 @@ This gadget is supported by
|
||||
It is ineffective in other positions (hyptheses of rewrite rules) or when used by other tactics
|
||||
(e.g. `apply`).
|
||||
-/
|
||||
@[simp ↓]
|
||||
@[simp ↓, expose]
|
||||
def binderNameHint {α : Sort u} {β : Sort v} {γ : Sort w} (v : α) (binder : β) (e : γ) : γ := e
|
||||
|
||||
@@ -42,24 +42,3 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
|
||||
/-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/
|
||||
@[simp] theorem dite_eq_ite [Decidable P] :
|
||||
(dite P (fun _ => a) (fun _ => b)) = ite P a b := rfl
|
||||
|
||||
@[deprecated "Use `ite_eq_right_iff`" (since := "2024-09-18")]
|
||||
theorem ite_some_none_eq_none [Decidable P] :
|
||||
(if P then some x else none) = none ↔ ¬ P := by
|
||||
simp only [ite_eq_right_iff, reduceCtorEq]
|
||||
rfl
|
||||
|
||||
@[deprecated "Use `Option.ite_none_right_eq_some`" (since := "2024-09-18")]
|
||||
theorem ite_some_none_eq_some [Decidable P] :
|
||||
(if P then some x else none) = some y ↔ P ∧ x = y := by
|
||||
split <;> simp_all
|
||||
|
||||
@[deprecated "Use `dite_eq_right_iff" (since := "2024-09-18")]
|
||||
theorem dite_some_none_eq_none [Decidable P] {x : P → α} :
|
||||
(if h : P then some (x h) else none) = none ↔ ¬P := by
|
||||
simp
|
||||
|
||||
@[deprecated "Use `Option.dite_none_right_eq_some`" (since := "2024-09-18")]
|
||||
theorem dite_some_none_eq_some [Decidable P] {x : P → α} {y : α} :
|
||||
(if h : P then some (x h) else none) = some y ↔ ∃ h : P, x h = y := by
|
||||
by_cases h : P <;> simp [h]
|
||||
|
||||
@@ -107,8 +107,8 @@ noncomputable def epsilon {α : Sort u} [h : Nonempty α] (p : α → Prop) : α
|
||||
theorem epsilon_spec_aux {α : Sort u} (h : Nonempty α) (p : α → Prop) : (∃ y, p y) → p (@epsilon α h p) :=
|
||||
(strongIndefiniteDescription p h).property
|
||||
|
||||
theorem epsilon_spec {α : Sort u} {p : α → Prop} (hex : ∃ y, p y) : p (@epsilon α (nonempty_of_exists hex) p) :=
|
||||
epsilon_spec_aux (nonempty_of_exists hex) p hex
|
||||
theorem epsilon_spec {α : Sort u} {p : α → Prop} (hex : ∃ y, p y) : p (@epsilon α hex.nonempty p) :=
|
||||
epsilon_spec_aux hex.nonempty p hex
|
||||
|
||||
theorem epsilon_singleton {α : Sort u} (x : α) : @epsilon α ⟨x⟩ (fun y => y = x) = x :=
|
||||
@epsilon_spec α (fun y => y = x) ⟨x, rfl⟩
|
||||
|
||||
@@ -127,7 +127,7 @@ end Except
|
||||
/--
|
||||
Adds exceptions of type `ε` to a monad `m`.
|
||||
-/
|
||||
def ExceptT (ε : Type u) (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
@[expose] def ExceptT (ε : Type u) (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
m (Except ε α)
|
||||
|
||||
/--
|
||||
|
||||
@@ -18,7 +18,7 @@ Adds exceptions of type `ε` to a monad `m`.
|
||||
Instead of using `Except ε` to model exceptions, this implementation uses continuation passing
|
||||
style. This has different performance characteristics from `ExceptT ε`.
|
||||
-/
|
||||
def ExceptCpsT (ε : Type u) (m : Type u → Type v) (α : Type u) := (β : Type u) → (α → m β) → (ε → m β) → m β
|
||||
@[expose] def ExceptCpsT (ε : Type u) (m : Type u → Type v) (α : Type u) := (β : Type u) → (α → m β) → (ε → m β) → m β
|
||||
|
||||
namespace ExceptCpsT
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ def containsFive (xs : List Nat) : Bool := Id.run do
|
||||
true
|
||||
```
|
||||
-/
|
||||
def Id (type : Type u) : Type u := type
|
||||
@[expose] def Id (type : Type u) : Type u := type
|
||||
|
||||
namespace Id
|
||||
|
||||
@@ -56,7 +56,7 @@ Runs a computation in the identity monad.
|
||||
This function is the identity function. Because its parameter has type `Id α`, it causes
|
||||
`do`-notation in its arguments to use the `Monad Id` instance.
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
@[always_inline, inline, expose]
|
||||
protected def run (x : Id α) : α := x
|
||||
|
||||
instance [OfNat α n] : OfNat (Id α) n :=
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Sebastian Ullrich, Leonardo de Moura, Mario Carneiro
|
||||
module
|
||||
|
||||
prelude
|
||||
import Init.Ext
|
||||
import Init.SimpLemmas
|
||||
import Init.Meta
|
||||
|
||||
@@ -241,13 +242,23 @@ theorem LawfulMonad.mk' (m : Type u → Type v) [Monad m]
|
||||
|
||||
namespace Id
|
||||
|
||||
@[simp] theorem map_eq (x : Id α) (f : α → β) : f <$> x = f x := rfl
|
||||
@[simp] theorem bind_eq (x : Id α) (f : α → id β) : x >>= f = f x := rfl
|
||||
@[simp] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
|
||||
@[ext] theorem ext {x y : Id α} (h : x.run = y.run) : x = y := h
|
||||
|
||||
instance : LawfulMonad Id := by
|
||||
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> 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 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
|
||||
|
||||
-- These lemmas are bad as they abuse the defeq of `Id α` and `α`
|
||||
@[deprecated run_map (since := "2025-03-05")] theorem map_eq (x : Id α) (f : α → β) : f <$> x = f x := rfl
|
||||
@[deprecated run_bind (since := "2025-03-05")] theorem bind_eq (x : Id α) (f : α → id β) : x >>= f = f x := rfl
|
||||
@[deprecated run_pure (since := "2025-03-05")] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
|
||||
|
||||
end Id
|
||||
|
||||
/-! # Option -/
|
||||
|
||||
@@ -7,7 +7,8 @@ module
|
||||
|
||||
prelude
|
||||
import Init.Control.Lawful.Basic
|
||||
import Init.Control.Except
|
||||
import all Init.Control.Except
|
||||
import all Init.Control.State
|
||||
import Init.Control.StateRef
|
||||
import Init.Ext
|
||||
|
||||
@@ -98,7 +99,7 @@ end ExceptT
|
||||
|
||||
instance : LawfulMonad (Except ε) := LawfulMonad.mk'
|
||||
(id_map := fun x => by cases x <;> rfl)
|
||||
(pure_bind := fun _ _ => rfl)
|
||||
(pure_bind := fun _ _ => by rfl)
|
||||
(bind_assoc := fun a _ _ => by cases a <;> rfl)
|
||||
|
||||
instance : LawfulApplicative (Except ε) := inferInstance
|
||||
@@ -247,7 +248,7 @@ instance : LawfulMonad (EStateM ε σ) := .mk'
|
||||
match x s with
|
||||
| .ok _ _ => rfl
|
||||
| .error _ _ => rfl)
|
||||
(pure_bind := fun _ _ => rfl)
|
||||
(pure_bind := fun _ _ => by rfl)
|
||||
(bind_assoc := fun x _ _ => funext <| fun s => by
|
||||
dsimp only [EStateM.instMonad, EStateM.bind]
|
||||
match x s with
|
||||
|
||||
@@ -20,7 +20,7 @@ instance : ToBool (Option α) := ⟨Option.isSome⟩
|
||||
Adds the ability to fail to a monad. Unlike ordinary exceptions, there is no way to signal why a
|
||||
failure occurred.
|
||||
-/
|
||||
def OptionT (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
@[expose] def OptionT (m : Type u → Type v) (α : Type u) : Type v :=
|
||||
m (Option α)
|
||||
|
||||
/--
|
||||
|
||||
@@ -22,7 +22,7 @@ Adds a mutable state of type `σ` to a monad.
|
||||
Actions in the resulting monad are functions that take an initial state and return, in `m`, a tuple
|
||||
of a value and a state.
|
||||
-/
|
||||
def StateT (σ : Type u) (m : Type u → Type v) (α : Type u) : Type (max u v) :=
|
||||
@[expose] def StateT (σ : Type u) (m : Type u → Type v) (α : Type u) : Type (max u v) :=
|
||||
σ → m (α × σ)
|
||||
|
||||
/--
|
||||
@@ -47,7 +47,7 @@ A tuple-based state monad.
|
||||
Actions in `StateM σ` are functions that take an initial state and return a value paired with a
|
||||
final state.
|
||||
-/
|
||||
@[reducible]
|
||||
@[expose, reducible]
|
||||
def StateM (σ α : Type u) : Type u := StateT σ Id α
|
||||
|
||||
instance {σ α} [Subsingleton σ] [Subsingleton α] : Subsingleton (StateM σ α) where
|
||||
|
||||
@@ -18,7 +18,7 @@ The State monad transformer using CPS style.
|
||||
An alternative implementation of a state monad transformer that internally uses continuation passing
|
||||
style instead of tuples.
|
||||
-/
|
||||
def StateCpsT (σ : Type u) (m : Type u → Type v) (α : Type u) := (δ : Type u) → σ → (α → σ → m δ) → m δ
|
||||
@[expose] def StateCpsT (σ : Type u) (m : Type u → Type v) (α : Type u) := (δ : Type u) → σ → (α → σ → m δ) → m δ
|
||||
|
||||
namespace StateCpsT
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ A state monad that uses an actual mutable reference cell (i.e. an `ST.Ref ω σ`
|
||||
|
||||
The macro `StateRefT σ m α` infers `ω` from `m`. It should normally be used instead.
|
||||
-/
|
||||
def StateRefT' (ω : Type) (σ : Type) (m : Type → Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
|
||||
@[expose] def StateRefT' (ω : Type) (σ : Type) (m : Type → Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
|
||||
|
||||
/-! Recall that `StateRefT` is a macro that infers `ω` from the `m`. -/
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import Init.Prelude
|
||||
import Init.SizeOf
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
|
||||
@[expose] section
|
||||
|
||||
universe u v w
|
||||
|
||||
/--
|
||||
@@ -51,6 +53,9 @@ theorem Function.comp_def {α β δ} (f : β → δ) (g : α → β) : f ∘ g =
|
||||
@[simp] theorem Function.false_comp {f : α → β} : ((fun _ => false) ∘ f) = fun _ => false := by
|
||||
rfl
|
||||
|
||||
@[simp] theorem Function.comp_id (f : α → β) : f ∘ id = f := rfl
|
||||
@[simp] theorem Function.id_comp (f : α → β) : id ∘ f = f := rfl
|
||||
|
||||
attribute [simp] namedPattern
|
||||
|
||||
/--
|
||||
@@ -941,7 +946,7 @@ theorem eqRec_heq {α : Sort u} {φ : α → Sort v} {a a' : α} : (h : a = a')
|
||||
| rfl, p => HEq.refl p
|
||||
|
||||
/--
|
||||
Heterogenous equality with an `Eq.rec` application on the left is equivalent to a heterogenous
|
||||
Heterogeneous equality with an `Eq.rec` application on the left is equivalent to a heterogeneous
|
||||
equality on the original term.
|
||||
-/
|
||||
theorem eqRec_heq_iff {α : Sort u} {a : α} {motive : (b : α) → a = b → Sort v}
|
||||
@@ -950,7 +955,7 @@ theorem eqRec_heq_iff {α : Sort u} {a : α} {motive : (b : α) → a = b → So
|
||||
h.rec (fun _ => ⟨id, id⟩) c
|
||||
|
||||
/--
|
||||
Heterogenous equality with an `Eq.rec` application on the right is equivalent to a heterogenous
|
||||
Heterogeneous equality with an `Eq.rec` application on the right is equivalent to a heterogeneous
|
||||
equality on the original term.
|
||||
-/
|
||||
theorem heq_eqRec_iff {α : Sort u} {a : α} {motive : (b : α) → a = b → Sort v}
|
||||
@@ -1207,10 +1212,7 @@ abbrev noConfusionEnum {α : Sort u} {β : Sort v} [inst : DecidableEq β] (f :
|
||||
instance : Inhabited Prop where
|
||||
default := True
|
||||
|
||||
deriving instance Inhabited for NonScalar, PNonScalar, True, ForInStep
|
||||
|
||||
theorem nonempty_of_exists {α : Sort u} {p : α → Prop} : Exists (fun x => p x) → Nonempty α
|
||||
| ⟨w, _⟩ => ⟨w⟩
|
||||
deriving instance Inhabited for NonScalar, PNonScalar, True
|
||||
|
||||
/-! # Subsingleton -/
|
||||
|
||||
@@ -1384,16 +1386,7 @@ instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
|
||||
instance Sum.nonemptyRight [h : Nonempty β] : Nonempty (Sum α β) :=
|
||||
Nonempty.elim h (fun b => ⟨Sum.inr b⟩)
|
||||
|
||||
instance {α : Type u} {β : Type v} [DecidableEq α] [DecidableEq β] : DecidableEq (Sum α β) := fun a b =>
|
||||
match a, b with
|
||||
| Sum.inl a, Sum.inl b =>
|
||||
if h : a = b then isTrue (h ▸ rfl)
|
||||
else isFalse fun h' => Sum.noConfusion h' fun h' => absurd h' h
|
||||
| Sum.inr a, Sum.inr b =>
|
||||
if h : a = b then isTrue (h ▸ rfl)
|
||||
else isFalse fun h' => Sum.noConfusion h' fun h' => absurd h' h
|
||||
| Sum.inr _, Sum.inl _ => isFalse fun h => Sum.noConfusion h
|
||||
| Sum.inl _, Sum.inr _ => isFalse fun h => Sum.noConfusion h
|
||||
deriving instance DecidableEq for Sum
|
||||
|
||||
end
|
||||
|
||||
@@ -2524,9 +2517,6 @@ class Antisymm (r : α → α → Prop) : Prop where
|
||||
/-- An antisymmetric relation `r` satisfies `r a b → r b a → a = b`. -/
|
||||
antisymm (a b : α) : r a b → r b a → a = b
|
||||
|
||||
@[deprecated Antisymm (since := "2024-10-16"), inherit_doc Antisymm]
|
||||
abbrev _root_.Antisymm (r : α → α → Prop) : Prop := Std.Antisymm r
|
||||
|
||||
/-- `Asymm X r` means that the binary relation `r` on `X` is asymmetric, that is,
|
||||
`r a b → ¬ r b a`. -/
|
||||
class Asymm (r : α → α → Prop) : Prop where
|
||||
|
||||
@@ -9,7 +9,7 @@ prelude
|
||||
import Init.Data.Array.Mem
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Count
|
||||
import Init.Data.List.Attach
|
||||
import all Init.Data.List.Attach
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
@@ -56,24 +56,24 @@ well-founded recursion mechanism to prove that the function terminates.
|
||||
-/
|
||||
@[inline] def attach (xs : Array α) : Array {x // x ∈ xs} := xs.attachWith _ fun _ => id
|
||||
|
||||
@[simp] theorem _root_.List.attachWith_toArray {l : List α} {P : α → Prop} {H : ∀ x ∈ l.toArray, P x} :
|
||||
@[simp, grind =] theorem _root_.List.attachWith_toArray {l : List α} {P : α → Prop} {H : ∀ x ∈ l.toArray, P x} :
|
||||
l.toArray.attachWith P H = (l.attachWith P (by simpa using H)).toArray := by
|
||||
simp [attachWith]
|
||||
|
||||
@[simp] theorem _root_.List.attach_toArray {l : List α} :
|
||||
@[simp, grind =] theorem _root_.List.attach_toArray {l : List α} :
|
||||
l.toArray.attach = (l.attachWith (· ∈ l.toArray) (by simp)).toArray := by
|
||||
simp [attach]
|
||||
|
||||
@[simp] theorem _root_.List.pmap_toArray {l : List α} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ l.toArray, P a} :
|
||||
@[simp, grind =] theorem _root_.List.pmap_toArray {l : List α} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ l.toArray, P a} :
|
||||
l.toArray.pmap f H = (l.pmap f (by simpa using H)).toArray := by
|
||||
simp [pmap]
|
||||
|
||||
@[simp] theorem toList_attachWith {xs : Array α} {P : α → Prop} {H : ∀ x ∈ xs, P x} :
|
||||
(xs.attachWith P H).toList = xs.toList.attachWith P (by simpa [mem_toList] using H) := by
|
||||
(xs.attachWith P H).toList = xs.toList.attachWith P (by simpa [mem_toList_iff] using H) := by
|
||||
simp [attachWith]
|
||||
|
||||
@[simp] theorem toList_attach {xs : Array α} :
|
||||
xs.attach.toList = xs.toList.attachWith (· ∈ xs) (by simp [mem_toList]) := by
|
||||
xs.attach.toList = xs.toList.attachWith (· ∈ xs) (by simp [mem_toList_iff]) := by
|
||||
simp [attach]
|
||||
|
||||
@[simp] theorem toList_pmap {xs : Array α} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ xs, P a} :
|
||||
@@ -574,9 +574,12 @@ state, the right approach is usually the tactic `simp [Array.unattach, -Array.ma
|
||||
-/
|
||||
def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array α := xs.map (·.val)
|
||||
|
||||
@[simp] theorem unattach_nil {p : α → Prop} : (#[] : Array { x // p x }).unattach = #[] := by
|
||||
@[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]
|
||||
@@ -590,7 +593,7 @@ def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array
|
||||
unfold unattach
|
||||
simp
|
||||
|
||||
@[simp] theorem _root_.List.unattach_toArray {p : α → Prop} {xs : List { x // p x }} :
|
||||
@[simp, grind =] theorem _root_.List.unattach_toArray {p : α → Prop} {xs : List { x // p x }} :
|
||||
xs.toArray.unattach = xs.unattach.toArray := by
|
||||
simp only [unattach, List.map_toArray, List.unattach]
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import Init.Data.UInt.BasicAux
|
||||
import Init.Data.Repr
|
||||
import Init.Data.ToString.Basic
|
||||
import Init.GetElem
|
||||
import Init.Data.List.ToArrayImpl
|
||||
import Init.Data.Array.Set
|
||||
import all Init.Data.List.ToArrayImpl
|
||||
import all Init.Data.Array.Set
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
@@ -36,8 +36,6 @@ variable {α : Type u}
|
||||
|
||||
namespace Array
|
||||
|
||||
@[deprecated toList (since := "2024-09-10")] abbrev data := @toList
|
||||
|
||||
/-! ### Preliminary theorems -/
|
||||
|
||||
@[simp, grind] theorem size_set {xs : Array α} {i : Nat} {v : α} (h : i < xs.size) :
|
||||
@@ -88,11 +86,11 @@ theorem ext' {xs ys : Array α} (h : xs.toList = ys.toList) : xs = ys := by
|
||||
@[simp] theorem toArrayAux_eq {as : List α} {acc : Array α} : (as.toArrayAux acc).toList = acc.toList ++ as := by
|
||||
induction as generalizing acc <;> simp [*, List.toArrayAux, Array.push, List.append_assoc, List.concat_eq_append]
|
||||
|
||||
@[simp] theorem toArray_toList {xs : Array α} : xs.toList.toArray = xs := rfl
|
||||
@[simp, grind =] theorem toArray_toList {xs : Array α} : xs.toList.toArray = xs := rfl
|
||||
|
||||
@[simp] theorem getElem_toList {xs : Array α} {i : Nat} (h : i < xs.size) : xs.toList[i] = xs[i] := rfl
|
||||
@[simp, grind =] theorem getElem_toList {xs : Array α} {i : Nat} (h : i < xs.size) : xs.toList[i] = xs[i] := rfl
|
||||
|
||||
@[simp] theorem getElem?_toList {xs : Array α} {i : Nat} : xs.toList[i]? = xs[i]? := by
|
||||
@[simp, grind =] theorem getElem?_toList {xs : Array α} {i : Nat} : xs.toList[i]? = xs[i]? := by
|
||||
simp [getElem?_def]
|
||||
|
||||
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
|
||||
@@ -107,13 +105,17 @@ instance : Membership α (Array α) where
|
||||
theorem mem_def {a : α} {as : Array α} : a ∈ as ↔ a ∈ as.toList :=
|
||||
⟨fun | .mk h => h, Array.Mem.mk⟩
|
||||
|
||||
@[simp] theorem mem_toArray {a : α} {l : List α} : a ∈ l.toArray ↔ a ∈ l := by
|
||||
@[simp, grind =] theorem mem_toArray {a : α} {l : List α} : a ∈ l.toArray ↔ a ∈ l := by
|
||||
simp [mem_def]
|
||||
|
||||
@[simp, grind] theorem getElem_mem {xs : Array α} {i : Nat} (h : i < xs.size) : xs[i] ∈ xs := by
|
||||
rw [Array.mem_def, ← getElem_toList]
|
||||
apply List.getElem_mem
|
||||
|
||||
@[simp, grind =] theorem emptyWithCapacity_eq {α n} : @emptyWithCapacity α n = #[] := rfl
|
||||
|
||||
@[simp] theorem mkEmpty_eq {α n} : @mkEmpty α n = #[] := rfl
|
||||
|
||||
end Array
|
||||
|
||||
namespace List
|
||||
@@ -127,18 +129,18 @@ theorem toList_toArray {as : List α} : as.toArray.toList = as := rfl
|
||||
@[deprecated toList_toArray (since := "2025-02-17")]
|
||||
abbrev _root_.Array.toList_toArray := @List.toList_toArray
|
||||
|
||||
@[simp] theorem size_toArray {as : List α} : as.toArray.size = as.length := by simp [Array.size]
|
||||
@[simp, grind] theorem size_toArray {as : List α} : as.toArray.size = as.length := by simp [Array.size]
|
||||
|
||||
@[deprecated size_toArray (since := "2025-02-17")]
|
||||
abbrev _root_.Array.size_toArray := @List.size_toArray
|
||||
|
||||
@[simp] theorem getElem_toArray {xs : List α} {i : Nat} (h : i < xs.toArray.size) :
|
||||
@[simp, grind =] theorem getElem_toArray {xs : List α} {i : Nat} (h : i < xs.toArray.size) :
|
||||
xs.toArray[i] = xs[i]'(by simpa using h) := rfl
|
||||
|
||||
@[simp] theorem getElem?_toArray {xs : List α} {i : Nat} : xs.toArray[i]? = xs[i]? := by
|
||||
@[simp, grind =] theorem getElem?_toArray {xs : List α} {i : Nat} : xs.toArray[i]? = xs[i]? := by
|
||||
simp [getElem?_def]
|
||||
|
||||
@[simp] theorem getElem!_toArray [Inhabited α] {xs : List α} {i : Nat} :
|
||||
@[simp, grind =] theorem getElem!_toArray [Inhabited α] {xs : List α} {i : Nat} :
|
||||
xs.toArray[i]! = xs[i]! := by
|
||||
simp [getElem!_def]
|
||||
|
||||
@@ -148,8 +150,6 @@ namespace Array
|
||||
|
||||
theorem size_eq_length_toList {xs : Array α} : xs.size = xs.toList.length := rfl
|
||||
|
||||
@[deprecated toList_toArray (since := "2024-09-09")] abbrev data_toArray := @List.toList_toArray
|
||||
|
||||
/-! ### Externs -/
|
||||
|
||||
/--
|
||||
@@ -338,6 +338,8 @@ def ofFn {n} (f : Fin n → α) : Array α := go 0 (emptyWithCapacity n) where
|
||||
if h : i < n then go (i+1) (acc.push (f ⟨i, h⟩)) else acc
|
||||
decreasing_by simp_wf; decreasing_trivial_pre_omega
|
||||
|
||||
-- See also `Array.ofFnM` defined in `Init.Data.Array.OfFn`.
|
||||
|
||||
/--
|
||||
Constructs an array that contains all the numbers from `0` to `n`, exclusive.
|
||||
|
||||
@@ -542,7 +544,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def modify (xs : Array α) (i : Nat) (f : α → α) : Array α :=
|
||||
Id.run <| modifyM xs i f
|
||||
Id.run <| modifyM xs i (pure <| f ·)
|
||||
|
||||
set_option linter.indexVariables false in -- Changing `idx` causes bootstrapping issues, haven't investigated.
|
||||
/--
|
||||
@@ -1057,7 +1059,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def foldl {α : Type u} {β : Type v} (f : β → α → β) (init : β) (as : Array α) (start := 0) (stop := as.size) : β :=
|
||||
Id.run <| as.foldlM f init start stop
|
||||
Id.run <| as.foldlM (pure <| f · ·) init start stop
|
||||
|
||||
/--
|
||||
Folds a function over an array from the right, accumulating a value starting with `init`. The
|
||||
@@ -1074,7 +1076,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def foldr {α : Type u} {β : Type v} (f : α → β → β) (init : β) (as : Array α) (start := as.size) (stop := 0) : β :=
|
||||
Id.run <| as.foldrM f init start stop
|
||||
Id.run <| as.foldrM (pure <| f · ·) init start stop
|
||||
|
||||
/--
|
||||
Computes the sum of the elements of an array.
|
||||
@@ -1122,7 +1124,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def map {α : Type u} {β : Type v} (f : α → β) (as : Array α) : Array β :=
|
||||
Id.run <| as.mapM f
|
||||
Id.run <| as.mapM (pure <| f ·)
|
||||
|
||||
instance : Functor Array where
|
||||
map := map
|
||||
@@ -1137,7 +1139,7 @@ valid.
|
||||
-/
|
||||
@[inline]
|
||||
def mapFinIdx {α : Type u} {β : Type v} (as : Array α) (f : (i : Nat) → α → (h : i < as.size) → β) : Array β :=
|
||||
Id.run <| as.mapFinIdxM f
|
||||
Id.run <| as.mapFinIdxM (pure <| f · · ·)
|
||||
|
||||
/--
|
||||
Applies a function to each element of the array along with the index at which that element is found,
|
||||
@@ -1148,7 +1150,7 @@ is valid.
|
||||
-/
|
||||
@[inline]
|
||||
def mapIdx {α : Type u} {β : Type v} (f : Nat → α → β) (as : Array α) : Array β :=
|
||||
Id.run <| as.mapIdxM f
|
||||
Id.run <| as.mapIdxM (pure <| f · ·)
|
||||
|
||||
/--
|
||||
Pairs each element of an array with its index, optionally starting from an index other than `0`.
|
||||
@@ -1196,7 +1198,7 @@ some 10
|
||||
-/
|
||||
@[inline]
|
||||
def findSome? {α : Type u} {β : Type v} (f : α → Option β) (as : Array α) : Option β :=
|
||||
Id.run <| as.findSomeM? f
|
||||
Id.run <| as.findSomeM? (pure <| f ·)
|
||||
|
||||
/--
|
||||
Returns the first non-`none` result of applying the function `f` to each element of the
|
||||
@@ -1230,7 +1232,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def findSomeRev? {α : Type u} {β : Type v} (f : α → Option β) (as : Array α) : Option β :=
|
||||
Id.run <| as.findSomeRevM? f
|
||||
Id.run <| as.findSomeRevM? (pure <| f ·)
|
||||
|
||||
/--
|
||||
Returns the last element of the array for which the predicate `p` returns `true`, or `none` if no
|
||||
@@ -1242,7 +1244,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def findRev? {α : Type} (p : α → Bool) (as : Array α) : Option α :=
|
||||
Id.run <| as.findRevM? p
|
||||
Id.run <| as.findRevM? (pure <| p ·)
|
||||
|
||||
/--
|
||||
Returns the index of the first element for which `p` returns `true`, or `none` if there is no such
|
||||
@@ -1381,7 +1383,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def any (as : Array α) (p : α → Bool) (start := 0) (stop := as.size) : Bool :=
|
||||
Id.run <| as.anyM p start stop
|
||||
Id.run <| as.anyM (pure <| p ·) start stop
|
||||
|
||||
/--
|
||||
Returns `true` if `p` returns `true` for every element of `as`.
|
||||
@@ -1399,7 +1401,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def all (as : Array α) (p : α → Bool) (start := 0) (stop := as.size) : Bool :=
|
||||
Id.run <| as.allM p start stop
|
||||
Id.run <| as.allM (pure <| p ·) start stop
|
||||
|
||||
/--
|
||||
Checks whether `a` is an element of `as`, using `==` to compare elements.
|
||||
@@ -1487,8 +1489,6 @@ The resulting arrays are appended.
|
||||
def flatMapM [Monad m] (f : α → m (Array β)) (as : Array α) : m (Array β) :=
|
||||
as.foldlM (init := empty) fun bs a => do return bs ++ (← f a)
|
||||
|
||||
@[deprecated flatMapM (since := "2024-10-16")] abbrev concatMapM := @flatMapM
|
||||
|
||||
/--
|
||||
Applies a function that returns an array to each element of an array. The resulting arrays are
|
||||
appended.
|
||||
@@ -1501,8 +1501,6 @@ Examples:
|
||||
def flatMap (f : α → Array β) (as : Array α) : Array β :=
|
||||
as.foldl (init := empty) fun bs a => bs ++ f a
|
||||
|
||||
@[deprecated flatMap (since := "2024-10-16")] abbrev concatMap := @flatMap
|
||||
|
||||
/--
|
||||
Appends the contents of array of arrays into a single array. The resulting array contains the same
|
||||
elements as the nested arrays in the same order.
|
||||
@@ -1672,7 +1670,7 @@ Example:
|
||||
-/
|
||||
@[inline]
|
||||
def filterMap (f : α → Option β) (as : Array α) (start := 0) (stop := as.size) : Array β :=
|
||||
Id.run <| as.filterMapM f (start := start) (stop := stop)
|
||||
Id.run <| as.filterMapM (pure <| f ·) (start := start) (stop := stop)
|
||||
|
||||
/--
|
||||
Returns the largest element of the array, as determined by the comparison `lt`, or `none` if
|
||||
@@ -2158,13 +2156,15 @@ Examples:
|
||||
|
||||
/-! ### Repr and ToString -/
|
||||
|
||||
protected def Array.repr {α : Type u} [Repr α] (xs : Array α) : Std.Format :=
|
||||
let _ : Std.ToFormat α := ⟨repr⟩
|
||||
if xs.size == 0 then
|
||||
"#[]"
|
||||
else
|
||||
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList xs) ("," ++ Std.Format.line)) "]"
|
||||
|
||||
instance {α : Type u} [Repr α] : Repr (Array α) where
|
||||
reprPrec xs _ :=
|
||||
let _ : Std.ToFormat α := ⟨repr⟩
|
||||
if xs.size == 0 then
|
||||
"#[]"
|
||||
else
|
||||
Std.Format.bracketFill "#[" (Std.Format.joinSep (toList xs) ("," ++ Std.Format.line)) "]"
|
||||
reprPrec xs _ := Array.repr xs
|
||||
|
||||
instance [ToString α] : ToString (Array α) where
|
||||
toString xs := "#" ++ toString xs.toList
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
|
||||
module
|
||||
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import all Init.Data.Array.Basic
|
||||
import Init.Data.Nat.Linear
|
||||
import Init.NotationExtra
|
||||
|
||||
@@ -88,4 +88,4 @@ pointer equality, and does not allocate a new array if the result of each functi
|
||||
pointer-equal to its argument.
|
||||
-/
|
||||
@[inline] def Array.mapMono (as : Array α) (f : α → α) : Array α :=
|
||||
Id.run <| as.mapMonoM f
|
||||
Id.run <| as.mapMonoM (pure <| f ·)
|
||||
|
||||
@@ -129,6 +129,6 @@ Examples:
|
||||
* `#[].binInsert (· < ·) 1 = #[1]`
|
||||
-/
|
||||
@[inline] def binInsert {α : Type u} (lt : α → α → Bool) (as : Array α) (k : α) : Array α :=
|
||||
Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k
|
||||
Id.run <| binInsertM lt (fun _ => pure k) (fun _ => pure k) as k
|
||||
|
||||
end Array
|
||||
|
||||
@@ -8,6 +8,7 @@ module
|
||||
|
||||
prelude
|
||||
import Init.Data.List.TakeDrop
|
||||
import all Init.Data.Array.Basic
|
||||
|
||||
/-!
|
||||
## Bootstrapping theorems about arrays
|
||||
@@ -52,15 +53,15 @@ theorem foldlM_toList.aux [Monad m]
|
||||
· rename_i i; rw [Nat.succ_add] at H
|
||||
simp [foldlM_toList.aux (j := j+1) H]
|
||||
rw (occs := [2]) [← List.getElem_cons_drop_succ_eq_drop ‹_›]
|
||||
rfl
|
||||
· rw [List.drop_of_length_le (Nat.ge_of_not_lt ‹_›)]; rfl
|
||||
simp
|
||||
· rw [List.drop_of_length_le (Nat.ge_of_not_lt ‹_›)]; simp
|
||||
|
||||
@[simp] theorem foldlM_toList [Monad m]
|
||||
@[simp, grind =] theorem foldlM_toList [Monad m]
|
||||
{f : β → α → m β} {init : β} {xs : Array α} :
|
||||
xs.toList.foldlM f init = xs.foldlM f init := by
|
||||
simp [foldlM, foldlM_toList.aux]
|
||||
|
||||
@[simp] theorem foldl_toList (f : β → α → β) {init : β} {xs : Array α} :
|
||||
@[simp, grind =] theorem foldl_toList (f : β → α → β) {init : β} {xs : Array α} :
|
||||
xs.toList.foldl f init = xs.foldl f init :=
|
||||
List.foldl_eq_foldlM .. ▸ foldlM_toList ..
|
||||
|
||||
@@ -69,42 +70,46 @@ theorem foldrM_eq_reverse_foldlM_toList.aux [Monad m]
|
||||
(xs.toList.take i).reverse.foldlM (fun x y => f y x) init = foldrM.fold f xs 0 i h init := by
|
||||
unfold foldrM.fold
|
||||
match i with
|
||||
| 0 => simp [List.foldlM, List.take]
|
||||
| 0 => simp
|
||||
| i+1 => rw [← List.take_concat_get h]; simp [← aux]
|
||||
|
||||
theorem foldrM_eq_reverse_foldlM_toList [Monad m] {f : α → β → m β} {init : β} {xs : Array α} :
|
||||
xs.foldrM f init = xs.toList.reverse.foldlM (fun x y => f y x) init := by
|
||||
have : xs = #[] ∨ 0 < xs.size :=
|
||||
match xs with | ⟨[]⟩ => .inl rfl | ⟨a::l⟩ => .inr (Nat.zero_lt_succ _)
|
||||
match xs, this with | _, .inl rfl => rfl | xs, .inr h => ?_
|
||||
match xs, this with | _, .inl rfl => simp [foldrM] | xs, .inr h => ?_
|
||||
simp [foldrM, h, ← foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
|
||||
|
||||
@[simp] theorem foldrM_toList [Monad m]
|
||||
@[simp, grind =] theorem foldrM_toList [Monad m]
|
||||
{f : α → β → m β} {init : β} {xs : Array α} :
|
||||
xs.toList.foldrM f init = xs.foldrM f init := by
|
||||
rw [foldrM_eq_reverse_foldlM_toList, List.foldlM_reverse]
|
||||
|
||||
@[simp] theorem foldr_toList (f : α → β → β) {init : β} {xs : Array α} :
|
||||
@[simp, grind =] theorem foldr_toList (f : α → β → β) {init : β} {xs : Array α} :
|
||||
xs.toList.foldr f init = xs.foldr f init :=
|
||||
List.foldr_eq_foldrM .. ▸ foldrM_toList ..
|
||||
|
||||
@[simp] theorem push_toList {xs : Array α} {a : α} : (xs.push a).toList = xs.toList ++ [a] := by
|
||||
@[simp, grind =] theorem toList_push {xs : Array α} {x : α} : (xs.push x).toList = xs.toList ++ [x] := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [push, List.concat_eq_append]
|
||||
|
||||
@[simp] theorem toListAppend_eq {xs : Array α} {l : List α} : xs.toListAppend l = xs.toList ++ l := by
|
||||
@[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]
|
||||
|
||||
@[simp] theorem toListImpl_eq {xs : Array α} : xs.toListImpl = xs.toList := by
|
||||
@[simp, grind =] theorem toListImpl_eq {xs : Array α} : xs.toListImpl = xs.toList := by
|
||||
simp [toListImpl, ← foldr_toList]
|
||||
|
||||
@[simp] theorem toList_pop {xs : Array α} : xs.pop.toList = xs.toList.dropLast := rfl
|
||||
@[simp, grind =] theorem toList_pop {xs : Array α} : xs.pop.toList = xs.toList.dropLast := rfl
|
||||
|
||||
@[deprecated toList_pop (since := "2025-02-17")]
|
||||
abbrev pop_toList := @Array.toList_pop
|
||||
|
||||
@[simp] theorem append_eq_append {xs ys : Array α} : xs.append ys = xs ++ ys := rfl
|
||||
|
||||
@[simp] theorem toList_append {xs ys : Array α} :
|
||||
@[simp, grind =] theorem toList_append {xs ys : Array α} :
|
||||
(xs ++ ys).toList = xs.toList ++ ys.toList := by
|
||||
rw [← append_eq_append]; unfold Array.append
|
||||
rw [← foldl_toList]
|
||||
@@ -112,13 +117,13 @@ abbrev pop_toList := @Array.toList_pop
|
||||
|
||||
@[simp] theorem toList_empty : (#[] : Array α).toList = [] := rfl
|
||||
|
||||
@[simp, grind] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
|
||||
@[simp, grind =] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
|
||||
apply ext'; simp only [toList_append, toList_empty, List.append_nil]
|
||||
|
||||
@[deprecated append_empty (since := "2025-01-13")]
|
||||
abbrev append_nil := @append_empty
|
||||
|
||||
@[simp, grind] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
|
||||
@[simp, grind =] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
|
||||
apply ext'; simp only [toList_append, toList_empty, List.nil_append]
|
||||
|
||||
@[deprecated empty_append (since := "2025-01-13")]
|
||||
@@ -129,7 +134,7 @@ abbrev nil_append := @empty_append
|
||||
|
||||
@[simp] theorem appendList_eq_append {xs : Array α} {l : List α} : xs.appendList l = xs ++ l := rfl
|
||||
|
||||
@[simp] theorem toList_appendList {xs : Array α} {l : List α} :
|
||||
@[simp, grind =] theorem toList_appendList {xs : Array α} {l : List α} :
|
||||
(xs ++ l).toList = xs.toList ++ l := by
|
||||
rw [← appendList_eq_append]; unfold Array.appendList
|
||||
induction l generalizing xs <;> simp [*]
|
||||
@@ -159,34 +164,4 @@ theorem foldl_eq_foldl_toList {f : β → α → β} {init : β} {xs : Array α}
|
||||
xs.foldl f init = xs.toList.foldl f init:= by
|
||||
simp
|
||||
|
||||
@[deprecated foldlM_toList (since := "2024-09-09")]
|
||||
abbrev foldlM_eq_foldlM_data := @foldlM_toList
|
||||
|
||||
@[deprecated foldl_toList (since := "2024-09-09")]
|
||||
abbrev foldl_eq_foldl_data := @foldl_toList
|
||||
|
||||
@[deprecated foldrM_eq_reverse_foldlM_toList (since := "2024-09-09")]
|
||||
abbrev foldrM_eq_reverse_foldlM_data := @foldrM_eq_reverse_foldlM_toList
|
||||
|
||||
@[deprecated foldrM_toList (since := "2024-09-09")]
|
||||
abbrev foldrM_eq_foldrM_data := @foldrM_toList
|
||||
|
||||
@[deprecated foldr_toList (since := "2024-09-09")]
|
||||
abbrev foldr_eq_foldr_data := @foldr_toList
|
||||
|
||||
@[deprecated push_toList (since := "2024-09-09")]
|
||||
abbrev push_data := @push_toList
|
||||
|
||||
@[deprecated toListImpl_eq (since := "2024-09-09")]
|
||||
abbrev toList_eq := @toListImpl_eq
|
||||
|
||||
@[deprecated pop_toList (since := "2024-09-09")]
|
||||
abbrev pop_data := @toList_pop
|
||||
|
||||
@[deprecated toList_append (since := "2024-09-09")]
|
||||
abbrev append_data := @toList_append
|
||||
|
||||
@[deprecated toList_appendList (since := "2024-09-09")]
|
||||
abbrev appendList_data := @toList_appendList
|
||||
|
||||
end Array
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Kim Morrison
|
||||
module
|
||||
|
||||
prelude
|
||||
import all Init.Data.Array.Basic
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Nat.Count
|
||||
|
||||
@@ -25,7 +26,7 @@ section countP
|
||||
|
||||
variable {p q : α → Bool}
|
||||
|
||||
@[simp] theorem _root_.List.countP_toArray {l : List α} : countP p l.toArray = l.countP p := by
|
||||
@[simp, grind =] theorem _root_.List.countP_toArray {l : List α} : countP p l.toArray = l.countP p := by
|
||||
simp [countP]
|
||||
induction l with
|
||||
| nil => rfl
|
||||
@@ -33,7 +34,7 @@ variable {p q : α → Bool}
|
||||
simp only [List.foldr_cons, ih, List.countP_cons]
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem countP_toList {xs : Array α} : xs.toList.countP p = countP p xs := by
|
||||
@[simp, grind =] theorem countP_toList {xs : Array α} : xs.toList.countP p = countP p xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@@ -51,8 +52,8 @@ theorem countP_push {a : α} {xs : Array α} : countP p (xs.push a) = countP p x
|
||||
rcases xs with ⟨xs⟩
|
||||
simp_all
|
||||
|
||||
@[simp] theorem countP_singleton {a : α} : countP p #[a] = if p a then 1 else 0 := by
|
||||
simp [countP_push]
|
||||
theorem countP_singleton {a : α} : countP p #[a] = if p a then 1 else 0 := by
|
||||
simp
|
||||
|
||||
theorem size_eq_countP_add_countP {xs : Array α} : xs.size = countP p xs + countP (fun a => ¬p a) xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -164,10 +165,10 @@ section count
|
||||
|
||||
variable [BEq α]
|
||||
|
||||
@[simp] theorem _root_.List.count_toArray {l : List α} {a : α} : count a l.toArray = l.count a := by
|
||||
@[simp, grind =] theorem _root_.List.count_toArray {l : List α} {a : α} : count a l.toArray = l.count a := by
|
||||
simp [count, List.count_eq_countP]
|
||||
|
||||
@[simp] theorem count_toList {xs : Array α} {a : α} : xs.toList.count a = xs.count a := by
|
||||
@[simp, grind =] theorem count_toList {xs : Array α} {a : α} : xs.toList.count a = xs.count a := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
|
||||
module
|
||||
|
||||
prelude
|
||||
import Init.Data.Array.Basic
|
||||
import all Init.Data.Array.Basic
|
||||
import Init.Data.BEq
|
||||
import Init.Data.List.Nat.BEq
|
||||
import Init.ByCases
|
||||
@@ -68,7 +68,7 @@ theorem isEqv_eq_decide (xs ys : Array α) (r) :
|
||||
Bool.not_eq_true]
|
||||
simpa [isEqv_iff_rel] using h'
|
||||
|
||||
@[simp] theorem isEqv_toList [BEq α] (xs ys : Array α) : (xs.toList.isEqv ys.toList r) = (xs.isEqv ys r) := by
|
||||
@[simp, grind =] theorem isEqv_toList [BEq α] (xs ys : Array α) : (xs.toList.isEqv ys.toList r) = (xs.isEqv ys r) := by
|
||||
simp [isEqv_eq_decide, List.isEqv_eq_decide]
|
||||
|
||||
theorem eq_of_isEqv [DecidableEq α] (xs ys : Array α) (h : Array.isEqv xs ys (fun x y => x = y)) : xs = ys := by
|
||||
@@ -99,17 +99,17 @@ theorem beq_eq_decide [BEq α] (xs ys : Array α) :
|
||||
decide (∀ (i : Nat) (h' : i < xs.size), xs[i] == ys[i]'(h ▸ h')) else false := by
|
||||
simp [BEq.beq, isEqv_eq_decide]
|
||||
|
||||
@[simp] theorem beq_toList [BEq α] (xs ys : Array α) : (xs.toList == ys.toList) = (xs == ys) := by
|
||||
@[simp, grind =] theorem beq_toList [BEq α] (xs ys : Array α) : (xs.toList == ys.toList) = (xs == ys) := by
|
||||
simp [beq_eq_decide, List.beq_eq_decide]
|
||||
|
||||
end Array
|
||||
|
||||
namespace List
|
||||
|
||||
@[simp] theorem isEqv_toArray [BEq α] (as bs : List α) : (as.toArray.isEqv bs.toArray r) = (as.isEqv bs r) := by
|
||||
@[simp, grind =] theorem isEqv_toArray [BEq α] (as bs : List α) : (as.toArray.isEqv bs.toArray r) = (as.isEqv bs r) := by
|
||||
simp [isEqv_eq_decide, Array.isEqv_eq_decide]
|
||||
|
||||
@[simp] theorem beq_toArray [BEq α] (as bs : List α) : (as.toArray == bs.toArray) = (as == bs) := by
|
||||
@[simp, grind =] theorem beq_toArray [BEq α] (as bs : List α) : (as.toArray == bs.toArray) = (as == bs) := by
|
||||
simp [beq_eq_decide, Array.beq_eq_decide]
|
||||
|
||||
end List
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Kim Morrison
|
||||
module
|
||||
|
||||
prelude
|
||||
import all Init.Data.Array.Basic
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Nat.Erase
|
||||
import Init.Data.List.Nat.Basic
|
||||
@@ -23,7 +24,7 @@ open Nat
|
||||
|
||||
/-! ### eraseP -/
|
||||
|
||||
@[simp] theorem eraseP_empty : #[].eraseP p = #[] := by simp
|
||||
theorem eraseP_empty : #[].eraseP p = #[] := by simp
|
||||
|
||||
theorem eraseP_of_forall_mem_not {xs : Array α} (h : ∀ a, a ∈ xs → ¬p a) : xs.eraseP p = xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
|
||||
@@ -238,11 +238,9 @@ theorem extract_append_left {as bs : Array α} :
|
||||
(as ++ bs).extract 0 as.size = as.extract 0 as.size := by
|
||||
simp
|
||||
|
||||
@[simp] theorem extract_append_right {as bs : Array α} :
|
||||
theorem extract_append_right {as bs : Array α} :
|
||||
(as ++ bs).extract as.size (as.size + i) = bs.extract 0 i := by
|
||||
simp only [extract_append, extract_size_left, Nat.sub_self, empty_append]
|
||||
congr 1
|
||||
omega
|
||||
simp
|
||||
|
||||
@[simp] theorem map_extract {as : Array α} {i j : Nat} :
|
||||
(as.extract i j).map f = (as.map f).extract i j := by
|
||||
|
||||
@@ -7,6 +7,7 @@ module
|
||||
|
||||
prelude
|
||||
import Init.Data.List.Nat.Find
|
||||
import all Init.Data.Array.Basic
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.Array.Range
|
||||
@@ -141,9 +142,9 @@ abbrev findSome?_mkArray_of_isNone := @findSome?_replicate_of_isNone
|
||||
|
||||
@[simp] theorem find?_empty : find? p #[] = none := rfl
|
||||
|
||||
@[simp] theorem find?_singleton {a : α} {p : α → Bool} :
|
||||
theorem find?_singleton {a : α} {p : α → Bool} :
|
||||
#[a].find? p = if p a then some a else none := by
|
||||
simp [singleton_eq_toArray_singleton]
|
||||
simp
|
||||
|
||||
@[simp] theorem findRev?_push_of_pos {xs : Array α} (h : p a) :
|
||||
findRev? p (xs.push a) = some a := by
|
||||
@@ -346,7 +347,8 @@ theorem find?_eq_some_iff_getElem {xs : Array α} {p : α → Bool} {b : α} :
|
||||
|
||||
/-! ### findIdx -/
|
||||
|
||||
@[simp] theorem findIdx_empty : findIdx p #[] = 0 := rfl
|
||||
theorem findIdx_empty : findIdx p #[] = 0 := rfl
|
||||
|
||||
theorem findIdx_singleton {a : α} {p : α → Bool} :
|
||||
#[a].findIdx p = if p a then 0 else 1 := by
|
||||
simp
|
||||
@@ -599,7 +601,8 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
|
||||
|
||||
/-! ### findFinIdx? -/
|
||||
|
||||
@[simp] theorem findFinIdx?_empty {p : α → Bool} : findFinIdx? p #[] = none := by simp
|
||||
theorem findFinIdx?_empty {p : α → Bool} : findFinIdx? p #[] = none := by simp
|
||||
|
||||
theorem findFinIdx?_singleton {a : α} {p : α → Bool} :
|
||||
#[a].findFinIdx? p = if p a then some ⟨0, by simp⟩ else none := by
|
||||
simp
|
||||
@@ -698,7 +701,7 @@ The verification API for `idxOf?` is still incomplete.
|
||||
The lemmas below should be made consistent with those for `findIdx?` (and proved using them).
|
||||
-/
|
||||
|
||||
@[simp] theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
|
||||
theorem idxOf?_empty [BEq α] : (#[] : Array α).idxOf? a = none := by simp
|
||||
|
||||
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.idxOf? a = none ↔ a ∉ xs := by
|
||||
@@ -711,14 +714,10 @@ theorem isSome_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
(xs.idxOf? a).isNone = ¬ a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
|
||||
|
||||
/-! ### finIdxOf?
|
||||
|
||||
The verification API for `finIdxOf?` is still incomplete.
|
||||
@@ -729,7 +728,7 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
|
||||
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
|
||||
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
|
||||
|
||||
@[simp] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
|
||||
theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
|
||||
|
||||
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.finIdxOf? a = none ↔ a ∉ xs := by
|
||||
@@ -747,10 +746,8 @@ theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp]
|
||||
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
(xs.finIdxOf? a).isNone = ¬ a ∈ xs := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
end Array
|
||||
|
||||
@@ -27,11 +27,13 @@ theorem extLit {n : Nat}
|
||||
(h : (i : Nat) → (hi : i < n) → xs.getLit i hsz₁ hi = ys.getLit i hsz₂ hi) : xs = ys :=
|
||||
Array.ext (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ ▸ hi₁)
|
||||
|
||||
def toListLitAux (xs : Array α) (n : Nat) (hsz : xs.size = n) : ∀ (i : Nat), i ≤ xs.size → List α → List α
|
||||
-- has to be expose for array literal support
|
||||
@[expose] def toListLitAux (xs : Array α) (n : Nat) (hsz : xs.size = n) : ∀ (i : Nat), i ≤ xs.size → List α → List α
|
||||
| 0, _, acc => acc
|
||||
| (i+1), hi, acc => toListLitAux xs n hsz i (Nat.le_of_succ_le hi) (xs.getLit i hsz (Nat.lt_of_lt_of_eq (Nat.lt_of_lt_of_le (Nat.lt_succ_self i) hi) hsz) :: acc)
|
||||
|
||||
def toArrayLit (xs : Array α) (n : Nat) (hsz : xs.size = n) : Array α :=
|
||||
-- has to be expose for array literal support
|
||||
@[expose] def toArrayLit (xs : Array α) (n : Nat) (hsz : xs.size = n) : Array α :=
|
||||
List.toArray <| toListLitAux xs n hsz n (hsz ▸ Nat.le_refl _) []
|
||||
|
||||
theorem toArrayLit_eq (xs : Array α) (n : Nat) (hsz : xs.size = n) : xs = toArrayLit xs n hsz := by
|
||||
|
||||
@@ -8,11 +8,13 @@ module
|
||||
prelude
|
||||
import Init.Data.Nat.Lemmas
|
||||
import Init.Data.List.Range
|
||||
import all Init.Data.List.Control
|
||||
import Init.Data.List.Nat.TakeDrop
|
||||
import Init.Data.List.Nat.Modify
|
||||
import Init.Data.List.Nat.Basic
|
||||
import Init.Data.List.Monadic
|
||||
import Init.Data.List.OfFn
|
||||
import all Init.Data.Array.Bootstrap
|
||||
import Init.Data.Array.Mem
|
||||
import Init.Data.Array.DecidableEq
|
||||
import Init.Data.Array.Lex.Basic
|
||||
@@ -39,10 +41,10 @@ namespace Array
|
||||
@[simp] theorem toList_eq_nil_iff {xs : Array α} : xs.toList = [] ↔ xs = #[] := by
|
||||
cases xs <;> simp
|
||||
|
||||
@[simp] theorem mem_toList_iff {a : α} {xs : Array α} : a ∈ xs.toList ↔ a ∈ xs := by
|
||||
@[simp, grind =] theorem mem_toList_iff {a : α} {xs : Array α} : a ∈ xs.toList ↔ a ∈ xs := by
|
||||
cases xs <;> simp
|
||||
|
||||
@[simp] theorem length_toList {xs : Array α} : xs.toList.length = xs.size := rfl
|
||||
@[simp, grind =] theorem length_toList {xs : Array α} : xs.toList.length = xs.size := rfl
|
||||
|
||||
theorem eq_toArray : xs = List.toArray as ↔ xs.toList = as := by
|
||||
cases xs
|
||||
@@ -59,14 +61,9 @@ theorem toArray_eq : List.toArray as = xs ↔ as = xs.toList := by
|
||||
|
||||
@[grind] theorem size_empty : (#[] : Array α).size = 0 := rfl
|
||||
|
||||
@[simp] theorem emptyWithCapacity_eq {α n} : @emptyWithCapacity α n = #[] := rfl
|
||||
|
||||
@[deprecated emptyWithCapacity_eq (since := "2025-03-12")]
|
||||
theorem mkEmpty_eq {α n} : @mkEmpty α n = #[] := rfl
|
||||
|
||||
/-! ### size -/
|
||||
|
||||
@[grind →] theorem eq_empty_of_size_eq_zero (h : xs.size = 0) : xs = #[] := by
|
||||
theorem eq_empty_of_size_eq_zero (h : xs.size = 0) : xs = #[] := by
|
||||
cases xs
|
||||
simp_all
|
||||
|
||||
@@ -119,14 +116,11 @@ abbrev size_eq_one := @size_eq_one_iff
|
||||
|
||||
/-! ## L[i] and L[i]? -/
|
||||
|
||||
@[simp] theorem getElem?_eq_none_iff {xs : Array α} : xs[i]? = none ↔ xs.size ≤ i := by
|
||||
by_cases h : i < xs.size
|
||||
· simp [getElem?_pos, h]
|
||||
· rw [getElem?_neg xs i h]
|
||||
simp_all
|
||||
theorem getElem?_eq_none_iff {xs : Array α} : xs[i]? = none ↔ xs.size ≤ i := by
|
||||
simp
|
||||
|
||||
@[simp] theorem none_eq_getElem?_iff {xs : Array α} {i : Nat} : none = xs[i]? ↔ xs.size ≤ i := by
|
||||
simp [eq_comm (a := none)]
|
||||
theorem none_eq_getElem?_iff {xs : Array α} {i : Nat} : none = xs[i]? ↔ xs.size ≤ i := by
|
||||
simp
|
||||
|
||||
theorem getElem?_eq_none {xs : Array α} (h : xs.size ≤ i) : xs[i]? = none := by
|
||||
simp [getElem?_eq_none_iff, h]
|
||||
@@ -136,8 +130,8 @@ grind_pattern Array.getElem?_eq_none => xs.size ≤ i, xs[i]?
|
||||
@[simp] theorem getElem?_eq_getElem {xs : Array α} {i : Nat} (h : i < xs.size) : xs[i]? = some xs[i] :=
|
||||
getElem?_pos ..
|
||||
|
||||
theorem getElem?_eq_some_iff {xs : Array α} : xs[i]? = some b ↔ ∃ h : i < xs.size, xs[i] = b := by
|
||||
simp [getElem?_def]
|
||||
theorem getElem?_eq_some_iff {xs : Array α} : xs[i]? = some b ↔ ∃ h : i < xs.size, xs[i] = b :=
|
||||
_root_.getElem?_eq_some_iff
|
||||
|
||||
@[grind →]
|
||||
theorem getElem_of_getElem? {xs : Array α} : xs[i]? = some a → ∃ h : i < xs.size, xs[i] = a :=
|
||||
@@ -146,13 +140,13 @@ theorem getElem_of_getElem? {xs : Array α} : xs[i]? = some a → ∃ h : i < xs
|
||||
theorem some_eq_getElem?_iff {xs : Array α} : some b = xs[i]? ↔ ∃ h : i < xs.size, xs[i] = b := by
|
||||
rw [eq_comm, getElem?_eq_some_iff]
|
||||
|
||||
@[simp] theorem some_getElem_eq_getElem?_iff (xs : Array α) (i : Nat) (h : i < xs.size) :
|
||||
theorem some_getElem_eq_getElem?_iff (xs : Array α) (i : Nat) (h : i < xs.size) :
|
||||
(some xs[i] = xs[i]?) ↔ True := by
|
||||
simp [h]
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem?_eq_some_getElem_iff (xs : Array α) (i : Nat) (h : i < xs.size) :
|
||||
theorem getElem?_eq_some_getElem_iff (xs : Array α) (i : Nat) (h : i < xs.size) :
|
||||
(xs[i]? = some xs[i]) ↔ True := by
|
||||
simp [h]
|
||||
simp
|
||||
|
||||
theorem getElem_eq_iff {xs : Array α} {i : Nat} {h : i < xs.size} : xs[i] = x ↔ xs[i]? = some x := by
|
||||
simp only [getElem?_eq_some_iff]
|
||||
@@ -188,16 +182,15 @@ theorem getElem_push {xs : Array α} {x : α} {i : Nat} (h : i < (xs.push x).siz
|
||||
· simp at h
|
||||
simp [getElem_push_lt, Nat.le_antisymm (Nat.le_of_lt_succ h) (Nat.ge_of_not_lt h')]
|
||||
|
||||
theorem getElem?_push {xs : Array α} {x} : (xs.push x)[i]? = if i = xs.size then some x else xs[i]? := by
|
||||
@[grind =] theorem getElem?_push {xs : Array α} {x} : (xs.push x)[i]? = if i = xs.size then some x else xs[i]? := by
|
||||
simp [getElem?_def, getElem_push]
|
||||
(repeat' split) <;> first | rfl | omega
|
||||
|
||||
@[simp] theorem getElem?_push_size {xs : Array α} {x} : (xs.push x)[xs.size]? = some x := by
|
||||
simp [getElem?_push]
|
||||
theorem getElem?_push_size {xs : Array α} {x} : (xs.push x)[xs.size]? = some x := by
|
||||
simp
|
||||
|
||||
@[simp] theorem getElem_singleton {a : α} {i : Nat} (h : i < 1) : #[a][i] = a :=
|
||||
match i, h with
|
||||
| 0, _ => rfl
|
||||
theorem getElem_singleton {a : α} {i : Nat} (h : i < 1) : #[a][i] = a := by
|
||||
simp
|
||||
|
||||
@[grind]
|
||||
theorem getElem?_singleton {a : α} {i : Nat} : #[a][i]? = if i = 0 then some a else none := by
|
||||
@@ -244,6 +237,8 @@ theorem back?_pop {xs : Array α} :
|
||||
|
||||
/-! ### push -/
|
||||
|
||||
@[simp] theorem push_empty : #[].push x = #[x] := rfl
|
||||
|
||||
@[simp] theorem push_ne_empty {a : α} {xs : Array α} : xs.push a ≠ #[] := by
|
||||
cases xs
|
||||
simp
|
||||
@@ -423,8 +418,7 @@ theorem eq_empty_iff_forall_not_mem {xs : Array α} : xs = #[] ↔ ∀ a, a ∉
|
||||
theorem eq_of_mem_singleton (h : a ∈ #[b]) : a = b := by
|
||||
simpa using h
|
||||
|
||||
@[simp] theorem mem_singleton {a b : α} : a ∈ #[b] ↔ a = b :=
|
||||
⟨eq_of_mem_singleton, (by simp [·])⟩
|
||||
theorem mem_singleton {a b : α} : a ∈ #[b] ↔ a = b := by simp
|
||||
|
||||
theorem forall_mem_push {p : α → Prop} {xs : Array α} {a : α} :
|
||||
(∀ x, x ∈ xs.push a → p x) ↔ p a ∧ ∀ x, x ∈ xs → p x := by
|
||||
@@ -527,7 +521,7 @@ theorem forall_getElem {xs : Array α} {p : α → Prop} :
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem isEmpty_toList {xs : Array α} : xs.toList.isEmpty = xs.isEmpty := by
|
||||
@[simp, grind =] theorem isEmpty_toList {xs : Array α} : xs.toList.isEmpty = xs.isEmpty := by
|
||||
rcases xs with ⟨_ | _⟩ <;> simp
|
||||
|
||||
theorem isEmpty_eq_false_iff_exists_mem {xs : Array α} :
|
||||
@@ -592,7 +586,7 @@ theorem anyM_loop_cons [Monad m] {p : α → m Bool} {a : α} {as : List α} {st
|
||||
· rw [dif_neg]
|
||||
omega
|
||||
|
||||
@[simp] theorem anyM_toList [Monad m] {p : α → m Bool} {as : Array α} :
|
||||
@[simp, grind =] theorem anyM_toList [Monad m] {p : α → m Bool} {as : Array α} :
|
||||
as.toList.anyM p = as.anyM p :=
|
||||
match as with
|
||||
| ⟨[]⟩ => by simp [anyM, anyM.loop]
|
||||
@@ -609,13 +603,13 @@ theorem anyM_loop_cons [Monad m] {p : α → m Bool} {a : α} {as : List α} {st
|
||||
|
||||
-- Auxiliary for `any_iff_exists`.
|
||||
theorem anyM_loop_iff_exists {p : α → Bool} {as : Array α} {start stop} (h : stop ≤ as.size) :
|
||||
anyM.loop (m := Id) p as stop h start = true ↔
|
||||
(anyM.loop (m := Id) (pure <| p ·) as stop h start).run = true ↔
|
||||
∃ (i : Nat) (_ : i < as.size), start ≤ i ∧ i < stop ∧ p as[i] = true := by
|
||||
unfold anyM.loop
|
||||
split <;> rename_i h₁
|
||||
· dsimp
|
||||
split <;> rename_i h₂
|
||||
· simp only [true_iff]
|
||||
· simp only [true_iff, Id.run_pure]
|
||||
refine ⟨start, by omega, by omega, by omega, h₂⟩
|
||||
· rw [anyM_loop_iff_exists]
|
||||
constructor
|
||||
@@ -632,9 +626,9 @@ termination_by stop - start
|
||||
-- This could also be proved from `SatisfiesM_anyM_iff_exists` in `Batteries.Data.Array.Init.Monadic`
|
||||
theorem any_iff_exists {p : α → Bool} {as : Array α} {start stop} :
|
||||
as.any p start stop ↔ ∃ (i : Nat) (_ : i < as.size), start ≤ i ∧ i < stop ∧ p as[i] := by
|
||||
dsimp [any, anyM, Id.run]
|
||||
dsimp [any, anyM]
|
||||
split
|
||||
· rw [anyM_loop_iff_exists]
|
||||
· rw [anyM_loop_iff_exists (p := p)]
|
||||
· rw [anyM_loop_iff_exists]
|
||||
constructor
|
||||
· rintro ⟨i, hi, ge, _, h⟩
|
||||
@@ -651,7 +645,7 @@ theorem any_iff_exists {p : α → Bool} {as : Array α} {start stop} :
|
||||
rw [Bool.eq_false_iff, Ne, any_eq_true]
|
||||
simp
|
||||
|
||||
@[simp] theorem any_toList {p : α → Bool} {as : Array α} : as.toList.any p = as.any p := by
|
||||
@[simp, grind =] theorem any_toList {p : α → Bool} {as : Array α} : as.toList.any p = as.any p := by
|
||||
rw [Bool.eq_iff_iff, any_eq_true, List.any_eq_true]
|
||||
simp only [List.mem_iff_getElem, getElem_toList]
|
||||
exact ⟨fun ⟨_, ⟨i, w, rfl⟩, h⟩ => ⟨i, w, h⟩, fun ⟨i, w, h⟩ => ⟨_, ⟨i, w, rfl⟩, h⟩⟩
|
||||
@@ -661,7 +655,7 @@ theorem allM_eq_not_anyM_not [Monad m] [LawfulMonad m] {p : α → m Bool} {as :
|
||||
dsimp [allM, anyM]
|
||||
simp
|
||||
|
||||
@[simp] theorem allM_toList [Monad m] [LawfulMonad m] {p : α → m Bool} {as : Array α} :
|
||||
@[simp, grind =] theorem allM_toList [Monad m] [LawfulMonad m] {p : α → m Bool} {as : Array α} :
|
||||
as.toList.allM p = as.allM p := by
|
||||
rw [allM_eq_not_anyM_not]
|
||||
rw [← anyM_toList]
|
||||
@@ -690,7 +684,7 @@ theorem all_iff_forall {p : α → Bool} {as : Array α} {start stop} :
|
||||
rw [Bool.eq_false_iff, Ne, all_eq_true]
|
||||
simp
|
||||
|
||||
@[simp] theorem all_toList {p : α → Bool} {as : Array α} : as.toList.all p = as.all p := by
|
||||
@[simp, grind =] theorem all_toList {p : α → Bool} {as : Array α} : as.toList.all p = as.all p := by
|
||||
rw [Bool.eq_iff_iff, all_eq_true, List.all_eq_true]
|
||||
simp only [List.mem_iff_getElem, getElem_toList]
|
||||
constructor
|
||||
@@ -730,18 +724,18 @@ theorem all_eq_true_iff_forall_mem {xs : Array α} : xs.all p ↔ ∀ x, x ∈ x
|
||||
subst h
|
||||
rw [all_toList]
|
||||
|
||||
theorem _root_.List.anyM_toArray [Monad m] [LawfulMonad m] {p : α → m Bool} {l : List α} :
|
||||
@[grind] theorem _root_.List.anyM_toArray [Monad m] [LawfulMonad m] {p : α → m Bool} {l : List α} :
|
||||
l.toArray.anyM p = l.anyM p := by
|
||||
rw [← anyM_toList]
|
||||
|
||||
theorem _root_.List.any_toArray {p : α → Bool} {l : List α} : l.toArray.any p = l.any p := by
|
||||
@[grind] theorem _root_.List.any_toArray {p : α → Bool} {l : List α} : l.toArray.any p = l.any p := by
|
||||
rw [any_toList]
|
||||
|
||||
theorem _root_.List.allM_toArray [Monad m] [LawfulMonad m] {p : α → m Bool} {l : List α} :
|
||||
@[grind] theorem _root_.List.allM_toArray [Monad m] [LawfulMonad m] {p : α → m Bool} {l : List α} :
|
||||
l.toArray.allM p = l.allM p := by
|
||||
rw [← allM_toList]
|
||||
|
||||
theorem _root_.List.all_toArray {p : α → Bool} {l : List α} : l.toArray.all p = l.all p := by
|
||||
@[grind] theorem _root_.List.all_toArray {p : α → Bool} {l : List α} : l.toArray.all p = l.all p := by
|
||||
rw [all_toList]
|
||||
|
||||
/-- Variant of `any_eq_true` in terms of membership rather than an array index. -/
|
||||
@@ -807,7 +801,7 @@ theorem decide_forall_mem {xs : Array α} {p : α → Prop} [DecidablePred p] :
|
||||
decide (∀ x, x ∈ xs → p x) = xs.all p := by
|
||||
simp [all_eq']
|
||||
|
||||
@[simp] theorem _root_.List.contains_toArray [BEq α] {l : List α} {a : α} :
|
||||
@[simp, grind =] theorem _root_.List.contains_toArray [BEq α] {l : List α} {a : α} :
|
||||
l.toArray.contains a = l.contains a := by
|
||||
simp [Array.contains, List.any_beq]
|
||||
|
||||
@@ -851,7 +845,7 @@ abbrev elem_eq_true_of_mem := @contains_eq_true_of_mem
|
||||
elem a xs = xs.contains a := by
|
||||
simp [elem]
|
||||
|
||||
@[grind] theorem contains_empty [BEq α] : (#[] : Array α).contains a = false := rfl
|
||||
@[grind] theorem contains_empty [BEq α] : (#[] : Array α).contains a = false := by simp
|
||||
|
||||
theorem elem_iff [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
|
||||
elem a xs = true ↔ a ∈ xs := ⟨mem_of_contains_eq_true, contains_eq_true_of_mem⟩
|
||||
@@ -868,8 +862,8 @@ theorem elem_eq_mem [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
|
||||
@[simp, grind] theorem contains_eq_mem [BEq α] [LawfulBEq α] {a : α} {xs : Array α} :
|
||||
xs.contains a = decide (a ∈ xs) := by rw [← elem_eq_contains, elem_eq_mem]
|
||||
|
||||
@[simp, grind] theorem any_empty [BEq α] {p : α → Bool} : (#[] : Array α).any p = false := rfl
|
||||
@[simp, grind] theorem all_empty [BEq α] {p : α → Bool} : (#[] : Array α).all p = true := rfl
|
||||
@[grind] theorem any_empty [BEq α] {p : α → Bool} : (#[] : Array α).any p = false := by simp
|
||||
@[grind] theorem all_empty [BEq α] {p : α → Bool} : (#[] : Array α).all p = true := by simp
|
||||
|
||||
/-- Variant of `any_push` with a side condition on `stop`. -/
|
||||
@[simp, grind] theorem any_push' [BEq α] {xs : Array α} {a : α} {p : α → Bool} (h : stop = xs.size + 1) :
|
||||
@@ -1205,7 +1199,7 @@ where
|
||||
induction l generalizing xs <;> simp [*]
|
||||
simp [H]
|
||||
|
||||
@[simp] theorem _root_.List.map_toArray {f : α → β} {l : List α} :
|
||||
@[simp, grind =] theorem _root_.List.map_toArray {f : α → β} {l : List α} :
|
||||
l.toArray.map f = (l.map f).toArray := by
|
||||
apply ext'
|
||||
simp
|
||||
@@ -1228,7 +1222,7 @@ where
|
||||
@[simp] theorem mapM_empty [Monad m] (f : α → m β) : mapM f #[] = pure #[] := by
|
||||
rw [mapM, mapM.map]; rfl
|
||||
|
||||
@[simp, grind] theorem map_empty {f : α → β} : map f #[] = #[] := mapM_empty f
|
||||
@[grind] theorem map_empty {f : α → β} : map f #[] = #[] := by simp
|
||||
|
||||
@[simp, grind] theorem map_push {f : α → β} {as : Array α} {x : α} :
|
||||
(as.push x).map f = (as.map f).push (f x) := by
|
||||
@@ -1366,17 +1360,17 @@ theorem mapM_eq_mapM_toList [Monad m] [LawfulMonad m] {f : α → m β} {xs : Ar
|
||||
|
||||
@[deprecated "Use `mapM_eq_foldlM` instead" (since := "2025-01-08")]
|
||||
theorem mapM_map_eq_foldl {as : Array α} {f : α → β} {i : Nat} :
|
||||
mapM.map (m := Id) f as i b = as.foldl (start := i) (fun acc a => acc.push (f a)) b := by
|
||||
mapM.map (m := Id) (pure <| f ·) as i b = pure (as.foldl (start := i) (fun acc a => acc.push (f a)) b) := by
|
||||
unfold mapM.map
|
||||
split <;> rename_i h
|
||||
· simp only [Id.bind_eq]
|
||||
dsimp [foldl, Id.run, foldlM]
|
||||
· ext : 1
|
||||
dsimp [foldl, foldlM]
|
||||
rw [mapM_map_eq_foldl, dif_pos (by omega), foldlM.loop, dif_pos h]
|
||||
-- Calling `split` here gives a bad goal.
|
||||
have : size as - i = Nat.succ (size as - i - 1) := by omega
|
||||
rw [this]
|
||||
simp [foldl, foldlM, Id.run, Nat.sub_add_eq]
|
||||
· dsimp [foldl, Id.run, foldlM]
|
||||
simp [foldl, foldlM, Nat.sub_add_eq]
|
||||
· dsimp [foldl, foldlM]
|
||||
rw [dif_pos (by omega), foldlM.loop, dif_neg h]
|
||||
rfl
|
||||
termination_by as.size - i
|
||||
@@ -1428,7 +1422,7 @@ theorem filter_congr {xs ys : Array α} (h : xs = ys)
|
||||
induction xs with simp
|
||||
| cons => split <;> simp [*]
|
||||
|
||||
theorem toList_filter {p : α → Bool} {xs : Array α} :
|
||||
@[grind] theorem toList_filter {p : α → Bool} {xs : Array α} :
|
||||
(xs.filter p).toList = xs.toList.filter p := by
|
||||
simp
|
||||
|
||||
@@ -1437,7 +1431,7 @@ theorem toList_filter {p : α → Bool} {xs : Array α} :
|
||||
apply ext'
|
||||
simp [h]
|
||||
|
||||
theorem _root_.List.filter_toArray {p : α → Bool} {l : List α} :
|
||||
@[grind] theorem _root_.List.filter_toArray {p : α → Bool} {l : List α} :
|
||||
l.toArray.filter p = (l.filter p).toArray := by
|
||||
simp
|
||||
|
||||
@@ -1598,11 +1592,11 @@ theorem filterMap_congr {as bs : Array α} (h : as = bs)
|
||||
as.toList ++ List.filterMap f xs := ?_
|
||||
exact this #[]
|
||||
induction xs
|
||||
· simp_all [Id.run]
|
||||
· simp_all [Id.run, List.filterMap_cons]
|
||||
· simp_all
|
||||
· simp_all [List.filterMap_cons]
|
||||
split <;> simp_all
|
||||
|
||||
theorem toList_filterMap {f : α → Option β} {xs : Array α} :
|
||||
@[grind] theorem toList_filterMap {f : α → Option β} {xs : Array α} :
|
||||
(xs.filterMap f).toList = xs.toList.filterMap f := by
|
||||
simp [toList_filterMap']
|
||||
|
||||
@@ -1612,7 +1606,7 @@ theorem toList_filterMap {f : α → Option β} {xs : Array α} :
|
||||
apply ext'
|
||||
simp [h]
|
||||
|
||||
theorem _root_.List.filterMap_toArray {f : α → Option β} {l : List α} :
|
||||
@[grind] theorem _root_.List.filterMap_toArray {f : α → Option β} {l : List α} :
|
||||
l.toArray.filterMap f = (l.filterMap f).toArray := by
|
||||
simp
|
||||
|
||||
@@ -1813,17 +1807,18 @@ theorem toArray_append {xs : List α} {ys : Array α} :
|
||||
|
||||
theorem singleton_eq_toArray_singleton {a : α} : #[a] = [a].toArray := rfl
|
||||
|
||||
@[simp] theorem empty_append_fun : ((#[] : Array α) ++ ·) = id := by
|
||||
@[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]
|
||||
|
||||
@[grind] theorem mem_append_left {a : α} {xs : Array α} (ys : Array α) (h : a ∈ xs) : a ∈ xs ++ ys :=
|
||||
theorem mem_append_left {a : α} {xs : Array α} (ys : Array α) (h : a ∈ xs) : a ∈ xs ++ ys :=
|
||||
mem_append.2 (Or.inl h)
|
||||
|
||||
@[grind] theorem mem_append_right {a : α} (xs : Array α) {ys : Array α} (h : a ∈ ys) : a ∈ xs ++ ys :=
|
||||
theorem mem_append_right {a : α} (xs : Array α) {ys : Array α} (h : a ∈ ys) : a ∈ xs ++ ys :=
|
||||
mem_append.2 (Or.inr h)
|
||||
|
||||
theorem not_mem_append {a : α} {xs ys : Array α} (h₁ : a ∉ xs) (h₂ : a ∉ ys) : a ∉ xs ++ ys :=
|
||||
@@ -1964,8 +1959,8 @@ theorem append_left_inj {xs₁ xs₂ : Array α} (ys) : xs₁ ++ ys = xs₂ ++ y
|
||||
theorem eq_empty_of_append_eq_empty {xs ys : Array α} (h : xs ++ ys = #[]) : xs = #[] ∧ ys = #[] :=
|
||||
append_eq_empty_iff.mp h
|
||||
|
||||
@[simp] theorem empty_eq_append_iff {xs ys : Array α} : #[] = xs ++ ys ↔ xs = #[] ∧ ys = #[] := by
|
||||
rw [eq_comm, append_eq_empty_iff]
|
||||
theorem empty_eq_append_iff {xs ys : Array α} : #[] = xs ++ ys ↔ xs = #[] ∧ ys = #[] := by
|
||||
simp
|
||||
|
||||
theorem append_ne_empty_of_left_ne_empty {xs ys : Array α} (h : xs ≠ #[]) : xs ++ ys ≠ #[] := by
|
||||
simp_all
|
||||
@@ -2033,7 +2028,7 @@ theorem append_eq_append_iff {ws xs ys zs : Array α} :
|
||||
simp only [List.append_toArray, List.set_toArray, List.set_append]
|
||||
split <;> simp
|
||||
|
||||
@[simp] theorem set_append_left {xs ys : Array α} {i : Nat} {x : α} (h : i < xs.size) :
|
||||
@[simp] theorem set_append_left {xs ys : Array α} {i : Nat} {x : α} (h : i < xs.size) :
|
||||
(xs ++ ys).set i x (by simp; omega) = xs.set i x ++ ys := by
|
||||
simp [set_append, h]
|
||||
|
||||
@@ -2097,7 +2092,7 @@ theorem append_eq_map_iff {f : α → β} :
|
||||
|
||||
@[simp, grind] theorem flatten_empty : (#[] : Array (Array α)).flatten = #[] := by simp [flatten]; rfl
|
||||
|
||||
@[simp] theorem toList_flatten {xss : Array (Array α)} :
|
||||
@[simp, grind] theorem toList_flatten {xss : Array (Array α)} :
|
||||
xss.flatten.toList = (xss.toList.map toList).flatten := by
|
||||
dsimp [flatten]
|
||||
simp only [← foldl_toList]
|
||||
@@ -2108,14 +2103,13 @@ theorem append_eq_map_iff {f : α → β} :
|
||||
| nil => simp
|
||||
| cons as => induction as.toList <;> simp [*]
|
||||
|
||||
@[simp] theorem flatten_map_toArray {L : List (List α)} :
|
||||
(L.toArray.map List.toArray).flatten = L.flatten.toArray := by
|
||||
@[simp] theorem flatten_toArray_map {L : List (List α)} :
|
||||
(L.map List.toArray).toArray.flatten = L.flatten.toArray := by
|
||||
apply ext'
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp] theorem flatten_toArray_map {L : List (List α)} :
|
||||
(L.map List.toArray).toArray.flatten = L.flatten.toArray := by
|
||||
rw [← flatten_map_toArray]
|
||||
theorem flatten_map_toArray {L : List (List α)} :
|
||||
(L.toArray.map List.toArray).flatten = L.flatten.toArray := by
|
||||
simp
|
||||
|
||||
-- We set this to lower priority so that `flatten_toArray_map` is applied first when relevant.
|
||||
@@ -2124,7 +2118,7 @@ theorem append_eq_map_iff {f : α → β} :
|
||||
apply ext'
|
||||
simp
|
||||
|
||||
@[simp] theorem size_flatten {xss : Array (Array α)} : xss.flatten.size = (xss.map size).sum := by
|
||||
@[simp, grind] theorem size_flatten {xss : Array (Array α)} : xss.flatten.size = (xss.map size).sum := by
|
||||
cases xss using array₂_induction
|
||||
simp [Function.comp_def]
|
||||
|
||||
@@ -2143,8 +2137,8 @@ theorem mem_flatten : ∀ {xss : Array (Array α)}, a ∈ xss.flatten ↔ ∃ xs
|
||||
induction xss using array₂_induction
|
||||
simp
|
||||
|
||||
@[simp] theorem empty_eq_flatten_iff {xss : Array (Array α)} : #[] = xss.flatten ↔ ∀ xs ∈ xss, xs = #[] := by
|
||||
rw [eq_comm, flatten_eq_empty_iff]
|
||||
theorem empty_eq_flatten_iff {xss : Array (Array α)} : #[] = xss.flatten ↔ ∀ xs ∈ xss, xs = #[] := by
|
||||
simp
|
||||
|
||||
theorem flatten_ne_empty_iff {xss : Array (Array α)} : xss.flatten ≠ #[] ↔ ∃ xs, xs ∈ xss ∧ xs ≠ #[] := by
|
||||
simp
|
||||
@@ -2284,15 +2278,9 @@ theorem eq_iff_flatten_eq {xss₁ xss₂ : Array (Array α)} :
|
||||
rw [List.map_inj_right]
|
||||
simp +contextual
|
||||
|
||||
@[simp] theorem flatten_toArray_map_toArray {xss : List (List α)} :
|
||||
theorem flatten_toArray_map_toArray {xss : List (List α)} :
|
||||
(xss.map List.toArray).toArray.flatten = xss.flatten.toArray := by
|
||||
simp [flatten]
|
||||
suffices ∀ as, List.foldl (fun acc bs => acc ++ bs) as (List.map List.toArray xss) = as ++ xss.flatten.toArray by
|
||||
simpa using this #[]
|
||||
intro as
|
||||
induction xss generalizing as with
|
||||
| nil => simp
|
||||
| cons xs xss ih => simp [ih]
|
||||
simp
|
||||
|
||||
/-! ### flatMap -/
|
||||
|
||||
@@ -2307,7 +2295,7 @@ theorem flatMap_toList {xs : Array α} {f : α → List β} :
|
||||
rcases xs with ⟨l⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem toList_flatMap {xs : Array α} {f : α → Array β} :
|
||||
@[simp, grind =] theorem toList_flatMap {xs : Array α} {f : α → Array β} :
|
||||
(xs.flatMap f).toList = xs.toList.flatMap fun a => (f a).toList := by
|
||||
rcases xs with ⟨l⟩
|
||||
simp
|
||||
@@ -2322,13 +2310,9 @@ theorem flatMap_toArray_cons {β} {f : α → Array β} {a : α} {as : List α}
|
||||
intro cs
|
||||
induction as generalizing cs <;> simp_all
|
||||
|
||||
@[simp] theorem flatMap_toArray {β} {f : α → Array β} {as : List α} :
|
||||
theorem flatMap_toArray {β} {f : α → Array β} {as : List α} :
|
||||
as.toArray.flatMap f = (as.flatMap (fun a => (f a).toList)).toArray := by
|
||||
induction as with
|
||||
| nil => simp
|
||||
| cons a as ih =>
|
||||
apply ext'
|
||||
simp [ih, flatMap_toArray_cons]
|
||||
simp
|
||||
|
||||
@[simp] theorem flatMap_id {xss : Array (Array α)} : xss.flatMap id = xss.flatten := by simp [flatMap_def]
|
||||
|
||||
@@ -2652,6 +2636,7 @@ abbrev sum_mkArray_nat := @sum_replicate_nat
|
||||
|
||||
/-! ### Preliminaries about `swap` needed for `reverse`. -/
|
||||
|
||||
@[grind]
|
||||
theorem getElem?_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} : (xs.swap i j hi hj)[k]? =
|
||||
if j = k then some xs[i] else if i = k then some xs[j] else xs[k]? := by
|
||||
simp [swap_def, getElem?_set]
|
||||
@@ -2710,15 +2695,15 @@ theorem getElem?_swap {xs : Array α} {i j : Nat} (hi hj) {k : Nat} : (xs.swap i
|
||||
true_and, Nat.not_lt] at h
|
||||
rw [List.getElem?_eq_none_iff.2 ‹_›, List.getElem?_eq_none_iff.2 (xs.toList.length_reverse ▸ ‹_›)]
|
||||
|
||||
@[simp] theorem _root_.List.reverse_toArray {l : List α} : l.toArray.reverse = l.reverse.toArray := by
|
||||
@[simp, grind =] theorem _root_.List.reverse_toArray {l : List α} : l.toArray.reverse = l.reverse.toArray := by
|
||||
apply ext'
|
||||
simp only [toList_reverse]
|
||||
|
||||
@[simp, grind] theorem reverse_push {xs : Array α} {a : α} : (xs.push a).reverse = #[a] ++ xs.reverse := by
|
||||
@[simp, grind =] theorem reverse_push {xs : Array α} {a : α} : (xs.push a).reverse = #[a] ++ xs.reverse := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp, grind] theorem mem_reverse {x : α} {xs : Array α} : x ∈ xs.reverse ↔ x ∈ xs := by
|
||||
@[simp, grind =] theorem mem_reverse {x : α} {xs : Array α} : x ∈ xs.reverse ↔ x ∈ xs := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@@ -2793,7 +2778,7 @@ theorem reverse_eq_iff {xs ys : Array α} : xs.reverse = ys ↔ xs = ys.reverse
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[grind _=_]theorem filterMap_reverse {f : α → Option β} {xs : Array α} : (xs.reverse.filterMap f) = (xs.filterMap f).reverse := by
|
||||
@[grind _=_] theorem filterMap_reverse {f : α → Option β} {xs : Array α} : (xs.reverse.filterMap f) = (xs.filterMap f).reverse := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@@ -2882,7 +2867,7 @@ theorem size_extract_loop {xs ys : Array α} {size start : Nat} :
|
||||
have h := Nat.le_of_not_gt h
|
||||
rw [extract_loop_of_ge (h:=h), Nat.sub_eq_zero_of_le h, Nat.min_zero, Nat.add_zero]
|
||||
|
||||
@[simp, grind] theorem size_extract {xs : Array α} {start stop : Nat} :
|
||||
@[simp, grind =] theorem size_extract {xs : Array α} {start stop : Nat} :
|
||||
(xs.extract start stop).size = min stop xs.size - start := by
|
||||
simp only [extract, Nat.sub_eq, emptyWithCapacity_eq]
|
||||
rw [size_extract_loop, size_empty, Nat.zero_add, Nat.sub_min_sub_right, Nat.min_assoc,
|
||||
@@ -2948,7 +2933,7 @@ theorem getElem_extract_aux {xs : Array α} {start stop : Nat} (h : i < (xs.extr
|
||||
rw [size_extract] at h; apply Nat.add_lt_of_lt_sub'; apply Nat.lt_of_lt_of_le h
|
||||
apply Nat.sub_le_sub_right; apply Nat.min_le_right
|
||||
|
||||
@[simp] theorem getElem_extract {xs : Array α} {start stop : Nat}
|
||||
@[simp, grind =] theorem getElem_extract {xs : Array α} {start stop : Nat}
|
||||
(h : i < (xs.extract start stop).size) :
|
||||
(xs.extract start stop)[i] = xs[start + i]'(getElem_extract_aux h) :=
|
||||
show (extract.loop xs (min stop xs.size - start) start #[])[i]
|
||||
@@ -3003,7 +2988,7 @@ theorem extract_empty_of_size_le_start {xs : Array α} {start stop : Nat} (h : x
|
||||
· simp
|
||||
· simp at h₁
|
||||
|
||||
@[simp] theorem _root_.List.extract_toArray {l : List α} {start stop : Nat} :
|
||||
@[simp, grind =] theorem _root_.List.extract_toArray {l : List α} {start stop : Nat} :
|
||||
l.toArray.extract start stop = (l.extract start stop).toArray := by
|
||||
apply ext'
|
||||
simp
|
||||
@@ -3029,19 +3014,21 @@ theorem take_size {xs : Array α} : xs.take xs.size = xs := by
|
||||
| succ n ih =>
|
||||
simp [shrink.loop, ih]
|
||||
|
||||
@[simp] theorem size_shrink {xs : Array α} {i : Nat} : (xs.shrink i).size = min i xs.size := by
|
||||
-- This doesn't need to be a simp lemma, as shortly we will simplify `shrink` to `take`.
|
||||
theorem size_shrink {xs : Array α} {i : Nat} : (xs.shrink i).size = min i xs.size := by
|
||||
simp [shrink]
|
||||
omega
|
||||
|
||||
@[simp] theorem getElem_shrink {xs : Array α} {i j : Nat} (h : j < (xs.shrink i).size) :
|
||||
(xs.shrink i)[j] = xs[j]'(by simp at h; omega) := by
|
||||
-- This doesn't need to be a simp lemma, as shortly we will simplify `shrink` to `take`.
|
||||
theorem getElem_shrink {xs : Array α} {i j : Nat} (h : j < (xs.shrink i).size) :
|
||||
(xs.shrink i)[j] = xs[j]'(by simp [size_shrink] at h; omega) := by
|
||||
simp [shrink]
|
||||
|
||||
@[simp] theorem toList_shrink {xs : Array α} {i : Nat} : (xs.shrink i).toList = xs.toList.take i := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
@[simp] theorem shrink_eq_take {xs : Array α} {i : Nat} : xs.shrink i = xs.take i := by
|
||||
ext <;> simp
|
||||
ext <;> simp [size_shrink, getElem_shrink]
|
||||
|
||||
theorem toList_shrink {xs : Array α} {i : Nat} : (xs.shrink i).toList = xs.toList.take i := by
|
||||
simp
|
||||
|
||||
/-! ### foldlM and foldrM -/
|
||||
|
||||
@@ -3210,18 +3197,16 @@ theorem foldlM_push [Monad m] [LawfulMonad m] {xs : Array α} {a : α} {f : β
|
||||
rw [foldr, foldrM_start_stop, ← foldrM_toList, List.foldrM_pure, foldr_toList, foldr, ← foldrM_start_stop]
|
||||
|
||||
theorem foldl_eq_foldlM {f : β → α → β} {b} {xs : Array α} {start stop : Nat} :
|
||||
xs.foldl f b start stop = xs.foldlM (m := Id) f b start stop := by
|
||||
simp [foldl, Id.run]
|
||||
xs.foldl f b start stop = (xs.foldlM (m := Id) (pure <| f · ·) b start stop).run := rfl
|
||||
|
||||
theorem foldr_eq_foldrM {f : α → β → β} {b} {xs : Array α} {start stop : Nat} :
|
||||
xs.foldr f b start stop = xs.foldrM (m := Id) f b start stop := by
|
||||
simp [foldr, Id.run]
|
||||
xs.foldr f b start stop = (xs.foldrM (m := Id) (pure <| f · ·) b start stop).run := rfl
|
||||
|
||||
@[simp] theorem id_run_foldlM {f : β → α → Id β} {b} {xs : Array α} {start stop : Nat} :
|
||||
Id.run (xs.foldlM f b start stop) = xs.foldl f b start stop := foldl_eq_foldlM.symm
|
||||
Id.run (xs.foldlM f b start stop) = xs.foldl (f · · |>.run) b start stop := rfl
|
||||
|
||||
@[simp] theorem id_run_foldrM {f : α → β → Id β} {b} {xs : Array α} {start stop : Nat} :
|
||||
Id.run (xs.foldrM f b start stop) = xs.foldr f b start stop := foldr_eq_foldrM.symm
|
||||
Id.run (xs.foldrM f b start stop) = xs.foldr (f · · |>.run) b start stop := rfl
|
||||
|
||||
/-- Variant of `foldlM_reverse` with a side condition for the `stop` argument. -/
|
||||
@[simp] theorem foldlM_reverse' [Monad m] {xs : Array α} {f : β → α → m β} {b} {stop : Nat}
|
||||
@@ -3250,7 +3235,7 @@ theorem foldrM_reverse [Monad m] {xs : Array α} {f : α → β → m β} {b} :
|
||||
|
||||
theorem foldrM_push [Monad m] {f : α → β → m β} {init : β} {xs : Array α} {a : α} :
|
||||
(xs.push a).foldrM f init = f a init >>= xs.foldrM f := by
|
||||
simp only [foldrM_eq_reverse_foldlM_toList, push_toList, List.reverse_append, List.reverse_cons,
|
||||
simp only [foldrM_eq_reverse_foldlM_toList, toList_push, List.reverse_append, List.reverse_cons,
|
||||
List.reverse_nil, List.nil_append, List.singleton_append, List.foldlM_cons, List.foldlM_reverse]
|
||||
|
||||
/--
|
||||
@@ -3262,6 +3247,22 @@ rather than `(arr.push a).size` as the argument.
|
||||
(xs.push a).foldrM f init start = f a init >>= xs.foldrM f := by
|
||||
simp [← foldrM_push, h]
|
||||
|
||||
@[simp, grind] theorem _root_.List.foldrM_push_eq_append [Monad m] [LawfulMonad m] {l : List α} {f : α → m β} {xs : Array β} :
|
||||
l.foldrM (fun x xs => xs.push <$> f x) xs = do return xs ++ (← l.reverse.mapM f).toArray := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons a l ih =>
|
||||
simp [ih]
|
||||
congr 1
|
||||
funext l'
|
||||
congr 1
|
||||
funext x
|
||||
simp
|
||||
|
||||
@[simp, grind] theorem _root_.List.foldlM_push_eq_append [Monad m] [LawfulMonad m] {l : List α} {f : α → m β} {xs : Array β} :
|
||||
l.foldlM (fun xs x => xs.push <$> f x) xs = do return xs ++ (← l.mapM f).toArray := by
|
||||
induction l generalizing xs <;> simp [*]
|
||||
|
||||
/-! ### foldl / foldr -/
|
||||
|
||||
@[grind] theorem foldl_empty {f : β → α → β} {init : β} : (#[].foldl f init) = init := rfl
|
||||
@@ -3358,6 +3359,32 @@ rather than `(arr.push a).size` as the argument.
|
||||
rcases as with ⟨as⟩
|
||||
simp
|
||||
|
||||
@[simp, grind] theorem _root_.List.foldr_push_eq_append {l : List α} {f : α → β} {xs : Array β} :
|
||||
l.foldr (fun x xs => xs.push (f x)) xs = xs ++ (l.reverse.map f).toArray := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
/-- Variant of `List.foldr_push_eq_append` specialized to `f = id`. -/
|
||||
@[simp, grind] theorem _root_.List.foldr_push_eq_append' {l : List α} {xs : Array α} :
|
||||
l.foldr (fun x xs => xs.push x) xs = xs ++ l.reverse.toArray := by
|
||||
induction l <;> simp [*]
|
||||
|
||||
@[simp, grind] theorem _root_.List.foldl_push_eq_append {l : List α} {f : α → β} {xs : Array β} :
|
||||
l.foldl (fun xs x => xs.push (f x)) xs = xs ++ (l.map f).toArray := by
|
||||
induction l generalizing xs <;> simp [*]
|
||||
|
||||
/-- Variant of `List.foldl_push_eq_append` specialized to `f = id`. -/
|
||||
@[simp, grind] theorem _root_.List.foldl_push_eq_append' {l : List α} {xs : Array α} :
|
||||
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']
|
||||
|
||||
@[simp, grind] theorem foldr_append_eq_append {xs : Array α} {f : α → Array β} {ys : Array β} :
|
||||
xs.foldr (f · ++ ·) ys = (xs.map f).flatten ++ ys := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -3479,17 +3506,16 @@ theorem foldrM_append [Monad m] [LawfulMonad m] {f : α → β → m β} {b} {xs
|
||||
|
||||
@[simp] theorem foldr_append' {f : α → β → β} {b} {xs ys : Array α} {start : Nat}
|
||||
(w : start = xs.size + ys.size) :
|
||||
(xs ++ ys).foldr f b start 0 = xs.foldr f (ys.foldr f b) := by
|
||||
subst w
|
||||
simp [foldr_eq_foldrM]
|
||||
(xs ++ ys).foldr f b start 0 = xs.foldr f (ys.foldr f b) :=
|
||||
foldrM_append' w
|
||||
|
||||
@[grind _=_]theorem foldl_append {β : Type _} {f : β → α → β} {b} {xs ys : Array α} :
|
||||
(xs ++ ys).foldl f b = ys.foldl f (xs.foldl f b) := by
|
||||
simp [foldl_eq_foldlM]
|
||||
@[grind _=_] theorem foldl_append {β : Type _} {f : β → α → β} {b} {xs ys : Array α} :
|
||||
(xs ++ ys).foldl f b = ys.foldl f (xs.foldl f b) :=
|
||||
foldlM_append
|
||||
|
||||
@[grind _=_] theorem foldr_append {f : α → β → β} {b} {xs ys : Array α} :
|
||||
(xs ++ ys).foldr f b = xs.foldr f (ys.foldr f b) := by
|
||||
simp [foldr_eq_foldrM]
|
||||
(xs ++ ys).foldr f b = xs.foldr f (ys.foldr f b) :=
|
||||
foldrM_append
|
||||
|
||||
@[simp] theorem foldl_flatten' {f : β → α → β} {b} {xss : Array (Array α)} {stop : Nat}
|
||||
(w : stop = xss.flatten.size) :
|
||||
@@ -3518,21 +3544,22 @@ theorem foldrM_append [Monad m] [LawfulMonad m] {f : α → β → m β} {b} {xs
|
||||
/-- Variant of `foldl_reverse` with a side condition for the `stop` argument. -/
|
||||
@[simp] theorem foldl_reverse' {xs : Array α} {f : β → α → β} {b} {stop : Nat}
|
||||
(w : stop = xs.size) :
|
||||
xs.reverse.foldl f b 0 stop = xs.foldr (fun x y => f y x) b := by
|
||||
simp [w, foldl_eq_foldlM, foldr_eq_foldrM]
|
||||
xs.reverse.foldl f b 0 stop = xs.foldr (fun x y => f y x) b :=
|
||||
foldlM_reverse' w
|
||||
|
||||
/-- Variant of `foldr_reverse` with a side condition for the `start` argument. -/
|
||||
@[simp] theorem foldr_reverse' {xs : Array α} {f : α → β → β} {b} {start : Nat}
|
||||
(w : start = xs.size) :
|
||||
xs.reverse.foldr f b start 0 = xs.foldl (fun x y => f y x) b := by
|
||||
simp [w, foldl_eq_foldlM, foldr_eq_foldrM]
|
||||
xs.reverse.foldr f b start 0 = xs.foldl (fun x y => f y x) b :=
|
||||
foldrM_reverse' w
|
||||
|
||||
@[grind] theorem foldl_reverse {xs : Array α} {f : β → α → β} {b} :
|
||||
xs.reverse.foldl f b = xs.foldr (fun x y => f y x) b := by simp [foldl_eq_foldlM, foldr_eq_foldrM]
|
||||
xs.reverse.foldl f b = xs.foldr (fun x y => f y x) b :=
|
||||
foldlM_reverse
|
||||
|
||||
@[grind] theorem foldr_reverse {xs : Array α} {f : α → β → β} {b} :
|
||||
xs.reverse.foldr f b = xs.foldl (fun x y => f y x) b :=
|
||||
(foldl_reverse ..).symm.trans <| by simp
|
||||
foldrM_reverse
|
||||
|
||||
theorem foldl_eq_foldr_reverse {xs : Array α} {f : β → α → β} {b} :
|
||||
xs.foldl f b = xs.reverse.foldr (fun x y => f y x) b := by simp
|
||||
@@ -3613,7 +3640,7 @@ theorem foldr_rel {xs : Array α} {f g : α → β → β} {a b : β} {r : β
|
||||
theorem back?_eq_some_iff {xs : Array α} {a : α} :
|
||||
xs.back? = some a ↔ ∃ ys : Array α, xs = ys.push a := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp only [List.back?_toArray, List.getLast?_eq_some_iff, toArray_eq, push_toList]
|
||||
simp only [List.back?_toArray, List.getLast?_eq_some_iff, toArray_eq, toList_push]
|
||||
constructor
|
||||
· rintro ⟨ys, rfl⟩
|
||||
exact ⟨ys.toArray, by simp⟩
|
||||
@@ -3738,29 +3765,30 @@ theorem contains_iff_exists_mem_beq [BEq α] {xs : Array α} {a : α} :
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.contains_iff_exists_mem_beq]
|
||||
|
||||
@[grind _=_]
|
||||
theorem contains_iff_mem [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
|
||||
xs.contains a ↔ a ∈ xs := by
|
||||
simp
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind =]
|
||||
theorem contains_toList [BEq α] {xs : Array α} {x : α} :
|
||||
xs.toList.contains x = xs.contains x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind =]
|
||||
theorem contains_map [BEq β] {xs : Array α} {x : β} {f : α → β} :
|
||||
(xs.map f).contains x = xs.any (fun a => x == f a) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind =]
|
||||
theorem contains_filter [BEq α] {xs : Array α} {x : α} {p : α → Bool} :
|
||||
(xs.filter p).contains x = xs.any (fun a => x == a && p a) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind =]
|
||||
theorem contains_filterMap [BEq β] {xs : Array α} {x : β} {f : α → Option β} :
|
||||
(xs.filterMap f).contains x = xs.any (fun a => (f a).any fun b => x == b) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -3773,19 +3801,19 @@ theorem contains_append [BEq α] {xs ys : Array α} {x : α} :
|
||||
rcases ys with ⟨ys⟩
|
||||
simp
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind =]
|
||||
theorem contains_flatten [BEq α] {xs : Array (Array α)} {x : α} :
|
||||
(xs.flatten).contains x = xs.any fun xs => xs.contains x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [Function.comp_def]
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind =]
|
||||
theorem contains_reverse [BEq α] {xs : Array α} {x : α} :
|
||||
(xs.reverse).contains x = xs.contains x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp
|
||||
|
||||
@[simp, grind]
|
||||
@[simp, grind =]
|
||||
theorem contains_flatMap [BEq β] {xs : Array α} {f : α → Array β} {x : β} :
|
||||
(xs.flatMap f).contains x = xs.any fun a => (f a).contains x := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -3798,7 +3826,7 @@ theorem pop_append {xs ys : Array α} :
|
||||
(xs ++ ys).pop = if ys.isEmpty then xs.pop else xs ++ ys.pop := by
|
||||
split <;> simp_all
|
||||
|
||||
@[simp] theorem pop_replicate {n : Nat} {a : α} : (replicate n a).pop = replicate (n - 1) a := by
|
||||
@[simp, grind =] theorem pop_replicate {n : Nat} {a : α} : (replicate n a).pop = replicate (n - 1) a := by
|
||||
ext <;> simp
|
||||
|
||||
@[deprecated pop_replicate (since := "2025-03-18")]
|
||||
@@ -4046,15 +4074,16 @@ abbrev all_mkArray := @all_replicate
|
||||
/-! ### modify -/
|
||||
|
||||
@[simp] theorem size_modify {xs : Array α} {i : Nat} {f : α → α} : (xs.modify i f).size = xs.size := by
|
||||
unfold modify modifyM Id.run
|
||||
unfold modify modifyM
|
||||
split <;> simp
|
||||
|
||||
theorem getElem_modify {xs : Array α} {j i} (h : i < (xs.modify j f).size) :
|
||||
(xs.modify j f)[i] = if j = i then f (xs[i]'(by simpa using h)) else xs[i]'(by simpa using h) := by
|
||||
simp only [modify, modifyM, Id.run, Id.pure_eq]
|
||||
simp only [modify, modifyM]
|
||||
split
|
||||
· simp only [Id.bind_eq, getElem_set]; split <;> simp [*]
|
||||
· rw [if_neg (mt (by rintro rfl; exact h) (by simp_all))]
|
||||
· simp only [getElem_set, Id.run_pure, Id.run_bind]; split <;> simp [*]
|
||||
· simp only [Id.run_pure]
|
||||
rw [if_neg (mt (by rintro rfl; exact h) (by simp_all))]
|
||||
|
||||
@[simp] theorem toList_modify {xs : Array α} {f : α → α} {i : Nat} :
|
||||
(xs.modify i f).toList = xs.toList.modify i f := by
|
||||
@@ -4096,6 +4125,7 @@ theorem getElem_swap' {xs : Array α} {i j : Nat} {hi hj} {k : Nat} (hk : k < xs
|
||||
· 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'
|
||||
@@ -4148,7 +4178,7 @@ theorem swapAt!_def {xs : Array α} {i : Nat} {v : α} (h : i < xs.size) :
|
||||
section replace
|
||||
variable [BEq α]
|
||||
|
||||
@[simp, grind] theorem replace_empty : (#[] : Array α).replace a b = #[] := by rfl
|
||||
@[simp, grind] theorem replace_empty : (#[] : Array α).replace a b = #[] := by simp [replace]
|
||||
|
||||
@[simp, grind] theorem replace_singleton {a b c : α} : #[a].replace b c = #[if a == b then c else a] := by
|
||||
simp only [replace, List.finIdxOf?_toArray, List.finIdxOf?]
|
||||
@@ -4361,7 +4391,10 @@ theorem foldl_toList_eq_map {l : List α} {acc : Array β} {G : α → β} :
|
||||
|
||||
/-! # uset -/
|
||||
|
||||
attribute [simp] uset
|
||||
-- For verification purposes, we use `simp` to replace `uset` with `set`.
|
||||
@[simp, grind =] theorem uset_eq_set {xs : Array α} {v : α} {i : USize} (h : i.toNat < xs.size) :
|
||||
uset xs i v h = set xs i.toNat v h := by
|
||||
simp [uset]
|
||||
|
||||
theorem size_uset {xs : Array α} {v : α} {i : USize} (h : i.toNat < xs.size) :
|
||||
(uset xs i v h).size = xs.size := by
|
||||
@@ -4378,7 +4411,8 @@ theorem getElem!_eq_getD [Inhabited α] {xs : Array α} {i} : xs[i]! = xs.getD i
|
||||
|
||||
/-! # mem -/
|
||||
|
||||
@[simp] theorem mem_toList {a : α} {xs : Array α} : a ∈ xs.toList ↔ a ∈ xs := mem_def.symm
|
||||
@[deprecated mem_toList_iff (since := "2025-05-26")]
|
||||
theorem mem_toList {a : α} {xs : Array α} : a ∈ xs.toList ↔ a ∈ xs := mem_def.symm
|
||||
|
||||
@[deprecated not_mem_empty (since := "2025-03-25")]
|
||||
theorem not_mem_nil (a : α) : ¬ a ∈ #[] := nofun
|
||||
@@ -4421,13 +4455,13 @@ theorem getElem?_push_eq {xs : Array α} {x : α} : (xs.push x)[xs.size]? = some
|
||||
|
||||
/-! ### forIn -/
|
||||
|
||||
@[simp] theorem forIn_toList [Monad m] {xs : Array α} {b : β} {f : α → β → m (ForInStep β)} :
|
||||
@[simp, grind =] theorem forIn_toList [Monad m] {xs : Array α} {b : β} {f : α → β → m (ForInStep β)} :
|
||||
forIn xs.toList b f = forIn xs b f := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem forIn'_toList [Monad m] {xs : Array α} {b : β} {f : (a : α) → a ∈ xs.toList → β → m (ForInStep β)} :
|
||||
forIn' xs.toList b f = forIn' xs b (fun a m b => f a (mem_toList.mpr m) b) := by
|
||||
@[simp, grind =] theorem forIn'_toList [Monad m] {xs : Array α} {b : β} {f : (a : α) → a ∈ xs.toList → β → m (ForInStep β)} :
|
||||
forIn' xs.toList b f = forIn' xs b (fun a m b => f a (mem_toList_iff.mpr m) b) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@@ -4439,7 +4473,7 @@ abbrev contains_def [DecidableEq α] {a : α} {xs : Array α} : xs.contains a
|
||||
|
||||
/-! ### isPrefixOf -/
|
||||
|
||||
@[simp] theorem isPrefixOf_toList [BEq α] {xs ys : Array α} :
|
||||
@[simp, grind =] theorem isPrefixOf_toList [BEq α] {xs ys : Array α} :
|
||||
xs.toList.isPrefixOf ys.toList = xs.isPrefixOf ys := by
|
||||
cases xs
|
||||
cases ys
|
||||
@@ -4480,32 +4514,32 @@ abbrev contains_def [DecidableEq α] {a : α} {xs : Array α} : xs.contains a
|
||||
|
||||
/-! ### findSomeM?, findM?, findSome?, find? -/
|
||||
|
||||
@[simp] theorem findSomeM?_toList [Monad m] [LawfulMonad m] {p : α → m (Option β)} {xs : Array α} :
|
||||
@[simp, grind =] theorem findSomeM?_toList [Monad m] [LawfulMonad m] {p : α → m (Option β)} {xs : Array α} :
|
||||
xs.toList.findSomeM? p = xs.findSomeM? p := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem findM?_toList [Monad m] [LawfulMonad m] {p : α → m Bool} {xs : Array α} :
|
||||
@[simp, grind =] theorem findM?_toList [Monad m] [LawfulMonad m] {p : α → m Bool} {xs : Array α} :
|
||||
xs.toList.findM? p = xs.findM? p := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem findSome?_toList {p : α → Option β} {xs : Array α} :
|
||||
@[simp, grind =] theorem findSome?_toList {p : α → Option β} {xs : Array α} :
|
||||
xs.toList.findSome? p = xs.findSome? p := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem find?_toList {p : α → Bool} {xs : Array α} :
|
||||
@[simp, grind =] theorem find?_toList {p : α → Bool} {xs : Array α} :
|
||||
xs.toList.find? p = xs.find? p := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem finIdxOf?_toList [BEq α] {a : α} {xs : Array α} :
|
||||
@[simp, grind =] theorem finIdxOf?_toList [BEq α] {a : α} {xs : Array α} :
|
||||
xs.toList.finIdxOf? a = (xs.finIdxOf? a).map (Fin.cast (by simp)) := by
|
||||
cases xs
|
||||
simp
|
||||
|
||||
@[simp] theorem findFinIdx?_toList {p : α → Bool} {xs : Array α} :
|
||||
@[simp, grind =] theorem findFinIdx?_toList {p : α → Bool} {xs : Array α} :
|
||||
xs.toList.findFinIdx? p = (xs.findFinIdx? p).map (Fin.cast (by simp)) := by
|
||||
cases xs
|
||||
simp
|
||||
@@ -4524,10 +4558,10 @@ Our goal is to have `simp` "pull `List.toArray` outwards" as much as possible.
|
||||
|
||||
theorem toListRev_toArray {l : List α} : l.toArray.toListRev = l.reverse := by simp
|
||||
|
||||
@[simp] theorem take_toArray {l : List α} {i : Nat} : l.toArray.take i = (l.take i).toArray := by
|
||||
apply Array.ext <;> simp
|
||||
@[grind =] theorem take_toArray {l : List α} {i : Nat} : l.toArray.take i = (l.take i).toArray := by
|
||||
simp
|
||||
|
||||
@[simp] theorem mapM_toArray [Monad m] [LawfulMonad m] {f : α → m β} {l : List α} :
|
||||
@[simp, grind =] theorem mapM_toArray [Monad m] [LawfulMonad m] {f : α → m β} {l : List α} :
|
||||
l.toArray.mapM f = List.toArray <$> l.mapM f := by
|
||||
simp only [← mapM'_eq_mapM, mapM_eq_foldlM]
|
||||
suffices ∀ xs : Array β,
|
||||
@@ -4544,12 +4578,12 @@ theorem toListRev_toArray {l : List α} : l.toArray.toListRev = l.reverse := by
|
||||
theorem uset_toArray {l : List α} {i : USize} {a : α} {h : i.toNat < l.toArray.size} :
|
||||
l.toArray.uset i a h = (l.set i.toNat a).toArray := by simp
|
||||
|
||||
@[simp] theorem modify_toArray {f : α → α} {l : List α} {i : Nat} :
|
||||
@[simp, grind =] theorem modify_toArray {f : α → α} {l : List α} {i : Nat} :
|
||||
l.toArray.modify i f = (l.modify i f).toArray := by
|
||||
apply ext'
|
||||
simp
|
||||
|
||||
@[simp] theorem flatten_toArray {L : List (List α)} :
|
||||
@[simp, grind =] theorem flatten_toArray {L : List (List α)} :
|
||||
(L.toArray.map List.toArray).flatten = L.flatten.toArray := by
|
||||
apply ext'
|
||||
simp [Function.comp_def]
|
||||
@@ -4591,12 +4625,12 @@ namespace Array
|
||||
@[simp] theorem findSomeRev?_eq_findSome?_reverse {f : α → Option β} {xs : Array α} :
|
||||
xs.findSomeRev? f = xs.reverse.findSome? f := by
|
||||
cases xs
|
||||
simp [findSomeRev?, Id.run]
|
||||
simp [findSomeRev?]
|
||||
|
||||
@[simp] theorem findRev?_eq_find?_reverse {f : α → Bool} {xs : Array α} :
|
||||
xs.findRev? f = xs.reverse.find? f := by
|
||||
cases xs
|
||||
simp [findRev?, Id.run]
|
||||
simp [findRev?]
|
||||
|
||||
/-! ### unzip -/
|
||||
|
||||
@@ -4624,11 +4658,11 @@ end Array
|
||||
|
||||
namespace List
|
||||
|
||||
@[simp] theorem unzip_toArray {as : List (α × β)} :
|
||||
@[simp, grind =] theorem unzip_toArray {as : List (α × β)} :
|
||||
as.toArray.unzip = Prod.map List.toArray List.toArray as.unzip := by
|
||||
ext1 <;> simp
|
||||
|
||||
@[simp] theorem firstM_toArray [Alternative m] {as : List α} {f : α → m β} :
|
||||
@[simp, grind =] theorem firstM_toArray [Alternative m] {as : List α} {f : α → m β} :
|
||||
as.toArray.firstM f = as.firstM f := by
|
||||
unfold Array.firstM
|
||||
suffices ∀ i, i ≤ as.length → firstM.go f as.toArray (as.length - i) = firstM f (as.drop (as.length - i)) by
|
||||
@@ -4698,11 +4732,6 @@ theorem get!_eq_getD_getElem? [Inhabited α] (xs : Array α) (i : Nat) :
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_getElem? := @get!_eq_getD_getElem?
|
||||
|
||||
|
||||
@[deprecated mem_of_back? (since := "2024-10-21")] abbrev mem_of_back?_eq_some := @mem_of_back?
|
||||
|
||||
@[deprecated getElem?_size_le (since := "2024-10-21")] abbrev get?_len_le := @getElem?_size_le
|
||||
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated "`Array.get?` is deprecated, use `a[i]?` instead." (since := "2025-02-12")]
|
||||
theorem get?_eq_get?_toList (xs : Array α) (i : Nat) : xs.get? i = xs.toList.get? i := by
|
||||
@@ -4711,45 +4740,11 @@ theorem get?_eq_get?_toList (xs : Array α) (i : Nat) : xs.get? i = xs.toList.ge
|
||||
set_option linter.deprecated false in
|
||||
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_get? := @get!_eq_getD_getElem?
|
||||
|
||||
@[deprecated getElem?_push_lt (since := "2024-10-21")] abbrev get?_push_lt := @getElem?_push_lt
|
||||
|
||||
@[deprecated getElem?_push_eq (since := "2024-10-21")] abbrev get?_push_eq := @getElem?_push_eq
|
||||
|
||||
@[deprecated getElem?_push (since := "2024-10-21")] abbrev get?_push := @getElem?_push
|
||||
|
||||
@[deprecated getElem?_size (since := "2024-10-21")] abbrev get?_size := @getElem?_size
|
||||
|
||||
@[deprecated getElem_set_self (since := "2025-01-17")]
|
||||
theorem get_set_eq (xs : Array α) (i : Nat) (v : α) (h : i < xs.size) :
|
||||
(xs.set i v h)[i]'(by simp [h]) = v := by
|
||||
simp only [set, ← getElem_toList, List.getElem_set_self]
|
||||
|
||||
@[deprecated foldl_toList_eq_flatMap (since := "2024-10-16")]
|
||||
abbrev foldl_toList_eq_bind := @foldl_toList_eq_flatMap
|
||||
|
||||
@[deprecated foldl_toList_eq_flatMap (since := "2024-10-16")]
|
||||
abbrev foldl_data_eq_bind := @foldl_toList_eq_flatMap
|
||||
|
||||
@[deprecated getElem_mem (since := "2024-10-17")]
|
||||
abbrev getElem?_mem := @getElem_mem
|
||||
|
||||
@[deprecated getElem_fin_eq_getElem_toList (since := "2024-10-17")]
|
||||
abbrev getElem_fin_eq_toList_get := @getElem_fin_eq_getElem_toList
|
||||
|
||||
@[deprecated "Use reverse direction of `getElem?_toList`" (since := "2024-10-17")]
|
||||
abbrev getElem?_eq_toList_getElem? := @getElem?_toList
|
||||
|
||||
@[deprecated getElem?_swap (since := "2024-10-17")] abbrev get?_swap := @getElem?_swap
|
||||
|
||||
@[deprecated getElem_push (since := "2024-10-21")] abbrev get_push := @getElem_push
|
||||
@[deprecated getElem_push_lt (since := "2024-10-21")] abbrev get_push_lt := @getElem_push_lt
|
||||
@[deprecated getElem_push_eq (since := "2024-10-21")] abbrev get_push_eq := @getElem_push_eq
|
||||
|
||||
@[deprecated back!_eq_back? (since := "2024-10-31")] abbrev back_eq_back? := @back!_eq_back?
|
||||
@[deprecated back!_push (since := "2024-10-31")] abbrev back_push := @back!_push
|
||||
@[deprecated eq_push_pop_back!_of_size_ne_zero (since := "2024-10-31")]
|
||||
abbrev eq_push_pop_back_of_size_ne_zero := @eq_push_pop_back!_of_size_ne_zero
|
||||
|
||||
@[deprecated set!_is_setIfInBounds (since := "2024-11-24")] abbrev set_is_setIfInBounds := @set!_eq_setIfInBounds
|
||||
@[deprecated size_setIfInBounds (since := "2024-11-24")] abbrev size_setD := @size_setIfInBounds
|
||||
@[deprecated getElem_setIfInBounds_eq (since := "2024-11-24")] abbrev getElem_setD_eq := @getElem_setIfInBounds_self
|
||||
|
||||
@@ -6,6 +6,7 @@ Author: Kim Morrison
|
||||
module
|
||||
|
||||
prelude
|
||||
import all Init.Data.Array.Lex.Basic
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.List.Lex
|
||||
|
||||
@@ -16,28 +17,24 @@ namespace Array
|
||||
|
||||
/-! ### Lexicographic ordering -/
|
||||
|
||||
@[simp] theorem _root_.List.lt_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray < l₂.toArray ↔ l₁ < l₂ := Iff.rfl
|
||||
@[simp] theorem _root_.List.le_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray ≤ l₂.toArray ↔ l₁ ≤ l₂ := Iff.rfl
|
||||
@[simp, grind =] theorem _root_.List.lt_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray < l₂.toArray ↔ l₁ < l₂ := Iff.rfl
|
||||
@[simp, grind =] theorem _root_.List.le_toArray [LT α] {l₁ l₂ : List α} : l₁.toArray ≤ l₂.toArray ↔ l₁ ≤ l₂ := Iff.rfl
|
||||
|
||||
@[simp] theorem lt_toList [LT α] {xs ys : Array α} : xs.toList < ys.toList ↔ xs < ys := Iff.rfl
|
||||
@[simp] theorem le_toList [LT α] {xs ys : Array α} : xs.toList ≤ ys.toList ↔ xs ≤ ys := Iff.rfl
|
||||
@[simp, grind =] theorem lt_toList [LT α] {xs ys : Array α} : xs.toList < ys.toList ↔ xs < ys := Iff.rfl
|
||||
@[simp, grind =] theorem le_toList [LT α] {xs ys : Array α} : xs.toList ≤ ys.toList ↔ xs ≤ ys := Iff.rfl
|
||||
|
||||
protected theorem not_lt_iff_ge [LT α] {l₁ l₂ : List α} : ¬ l₁ < l₂ ↔ l₂ ≤ l₁ := Iff.rfl
|
||||
protected theorem not_le_iff_gt [DecidableEq α] [LT α] [DecidableLT α] {l₁ l₂ : List α} :
|
||||
¬ l₁ ≤ l₂ ↔ l₂ < l₁ :=
|
||||
protected theorem not_lt_iff_ge [LT α] {xs ys : Array α} : ¬ xs < ys ↔ ys ≤ xs := Iff.rfl
|
||||
protected theorem not_le_iff_gt [DecidableEq α] [LT α] [DecidableLT α] {xs ys : Array α} :
|
||||
¬ xs ≤ ys ↔ ys < xs :=
|
||||
Decidable.not_not
|
||||
|
||||
@[simp] theorem lex_empty [BEq α] {lt : α → α → Bool} {xs : Array α} : xs.lex #[] lt = false := by
|
||||
simp [lex, Id.run]
|
||||
|
||||
@[simp] theorem singleton_lex_singleton [BEq α] {lt : α → α → Bool} : #[a].lex #[b] lt = lt a b := by
|
||||
simp only [lex, List.getElem_toArray, List.getElem_singleton]
|
||||
cases lt a b <;> cases a != b <;> simp [Id.run]
|
||||
simp [lex]
|
||||
|
||||
private theorem cons_lex_cons [BEq α] {lt : α → α → Bool} {a b : α} {xs ys : Array α} :
|
||||
(#[a] ++ xs).lex (#[b] ++ ys) lt =
|
||||
(lt a b || a == b && xs.lex ys lt) := by
|
||||
simp only [lex, Id.run]
|
||||
simp only [lex]
|
||||
simp only [Std.Range.forIn'_eq_forIn'_range', size_append, List.size_toArray, List.length_singleton,
|
||||
Nat.add_comm 1]
|
||||
simp [Nat.add_min_add_right, List.range'_succ, getElem_append_left, List.range'_succ_left,
|
||||
@@ -47,17 +44,20 @@ private theorem cons_lex_cons [BEq α] {lt : α → α → Bool} {a b : α} {xs
|
||||
cases a == b <;> simp
|
||||
· simp
|
||||
|
||||
@[simp] theorem _root_.List.lex_toArray [BEq α] {lt : α → α → Bool} {l₁ l₂ : List α} :
|
||||
@[simp, grind =] theorem _root_.List.lex_toArray [BEq α] {lt : α → α → Bool} {l₁ l₂ : List α} :
|
||||
l₁.toArray.lex l₂.toArray lt = l₁.lex l₂ lt := by
|
||||
induction l₁ generalizing l₂ with
|
||||
| nil => cases l₂ <;> simp [lex, Id.run]
|
||||
| nil => cases l₂ <;> simp [lex]
|
||||
| cons x l₁ ih =>
|
||||
cases l₂ with
|
||||
| nil => simp [lex, Id.run]
|
||||
| nil => simp [lex]
|
||||
| cons y l₂ =>
|
||||
rw [List.toArray_cons, List.toArray_cons y, cons_lex_cons, List.lex, ih]
|
||||
|
||||
@[simp] theorem lex_toList [BEq α] {lt : α → α → Bool} {xs ys : Array α} :
|
||||
theorem singleton_lex_singleton [BEq α] {lt : α → α → Bool} : #[a].lex #[b] lt = lt a b := by
|
||||
simp
|
||||
|
||||
@[simp, grind =] theorem lex_toList [BEq α] {lt : α → α → Bool} {xs ys : Array α} :
|
||||
xs.toList.lex ys.toList lt = xs.lex ys lt := by
|
||||
cases xs <;> cases ys <;> simp
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@ Authors: Mario Carneiro, Kim Morrison
|
||||
module
|
||||
|
||||
prelude
|
||||
import all Init.Data.Array.Basic
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.Array.OfFn
|
||||
import Init.Data.List.MapIdx
|
||||
import all Init.Data.List.MapIdx
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
@@ -26,7 +27,7 @@ theorem mapFinIdx_induction (xs : Array α) (f : (i : Nat) → α → (h : i < x
|
||||
motive xs.size ∧ ∃ eq : (Array.mapFinIdx xs f).size = xs.size,
|
||||
∀ i h, p i ((Array.mapFinIdx xs f)[i]) h := by
|
||||
let rec go {bs i j h} (h₁ : j = bs.size) (h₂ : ∀ i h h', p i bs[i] h) (hm : motive j) :
|
||||
let as : Array β := Array.mapFinIdxM.map (m := Id) xs f i j h bs
|
||||
let as : Array β := Id.run <| Array.mapFinIdxM.map xs (pure <| f · · ·) i j h bs
|
||||
motive xs.size ∧ ∃ eq : as.size = xs.size, ∀ i h, p i as[i] h := by
|
||||
induction i generalizing j bs with simp [mapFinIdxM.map]
|
||||
| zero =>
|
||||
@@ -111,11 +112,11 @@ end Array
|
||||
|
||||
namespace List
|
||||
|
||||
@[simp] theorem mapFinIdx_toArray {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
@[simp, grind =] theorem mapFinIdx_toArray {l : List α} {f : (i : Nat) → α → (h : i < l.length) → β} :
|
||||
l.toArray.mapFinIdx f = (l.mapFinIdx f).toArray := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp] theorem mapIdx_toArray {f : Nat → α → β} {l : List α} :
|
||||
@[simp, grind =] theorem mapIdx_toArray {f : Nat → α → β} {l : List α} :
|
||||
l.toArray.mapIdx f = (l.mapIdx f).toArray := by
|
||||
ext <;> simp
|
||||
|
||||
@@ -132,7 +133,7 @@ namespace Array
|
||||
@[deprecated getElem_zipIdx (since := "2025-01-21")]
|
||||
abbrev getElem_zipWithIndex := @getElem_zipIdx
|
||||
|
||||
@[simp] theorem zipIdx_toArray {l : List α} {k : Nat} :
|
||||
@[simp, grind =] theorem zipIdx_toArray {l : List α} {k : Nat} :
|
||||
l.toArray.zipIdx k = (l.zipIdx k).toArray := by
|
||||
ext i hi₁ hi₂ <;> simp [Nat.add_comm]
|
||||
|
||||
@@ -454,7 +455,7 @@ end Array
|
||||
|
||||
namespace List
|
||||
|
||||
theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
|
||||
@[grind] theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
|
||||
{f : (i : Nat) → α → (h : i < l.length) → m β} :
|
||||
l.toArray.mapFinIdxM f = toArray <$> l.mapFinIdxM f := by
|
||||
let rec go (i : Nat) (acc : Array β) (inv : i + acc.size = l.length) :
|
||||
@@ -475,7 +476,7 @@ theorem mapFinIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
|
||||
simp only [Array.mapFinIdxM, mapFinIdxM]
|
||||
exact go _ #[] _
|
||||
|
||||
theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
|
||||
@[grind] theorem mapIdxM_toArray [Monad m] [LawfulMonad m] {l : List α}
|
||||
{f : Nat → α → m β} :
|
||||
l.toArray.mapIdxM f = toArray <$> l.mapIdxM f := by
|
||||
let rec go (bs : List α) (acc : Array β) (inv : bs.length + acc.size = l.length) :
|
||||
|
||||
@@ -6,6 +6,8 @@ Authors: Kim Morrison
|
||||
module
|
||||
|
||||
prelude
|
||||
import all Init.Data.List.Control
|
||||
import all Init.Data.Array.Basic
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Attach
|
||||
import Init.Data.List.Monadic
|
||||
@@ -23,15 +25,29 @@ open Nat
|
||||
|
||||
/-! ## Monadic operations -/
|
||||
|
||||
theorem map_toList_inj [Monad m] [LawfulMonad m]
|
||||
{xs : m (Array α)} {ys : m (Array α)} :
|
||||
toList <$> xs = toList <$> ys ↔ xs = ys := by
|
||||
simp
|
||||
|
||||
/-! ### mapM -/
|
||||
|
||||
@[simp] theorem mapM_pure [Monad m] [LawfulMonad m] {xs : Array α} {f : α → β} :
|
||||
xs.mapM (m := m) (pure <| f ·) = pure (xs.map f) := by
|
||||
induction xs; simp_all
|
||||
|
||||
@[simp] theorem mapM_id {xs : Array α} {f : α → Id β} : xs.mapM f = xs.map f :=
|
||||
@[simp] 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] 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⟩
|
||||
simp
|
||||
|
||||
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] {f : α → m β} {xs ys : Array α} :
|
||||
(xs ++ ys).mapM f = (return (← xs.mapM f) ++ (← ys.mapM f)) := by
|
||||
rcases xs with ⟨xs⟩
|
||||
@@ -179,12 +195,18 @@ theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.forIn'_pure_yield_eq_foldl, List.foldl_map]
|
||||
|
||||
@[simp] theorem forIn'_yield_eq_foldl
|
||||
theorem idRun_forIn'_yield_eq_foldl
|
||||
{xs : Array α} (f : (a : α) → a ∈ xs → β → Id β) (init : β) :
|
||||
(forIn' xs init (fun a m b => .yield <$> f a m b)).run =
|
||||
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 := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldl_map]
|
||||
xs.attach.foldl (fun b ⟨a, h⟩ => f a h b) init :=
|
||||
forIn'_pure_yield_eq_foldl _ _
|
||||
|
||||
@[simp] theorem forIn'_map [Monad m] [LawfulMonad m]
|
||||
{xs : Array α} (g : α → β) (f : (b : β) → b ∈ xs.map g → γ → m (ForInStep γ)) :
|
||||
@@ -221,12 +243,18 @@ theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.forIn_pure_yield_eq_foldl, List.foldl_map]
|
||||
|
||||
@[simp] theorem forIn_yield_eq_foldl
|
||||
theorem idRun_forIn_yield_eq_foldl
|
||||
{xs : Array α} (f : α → β → Id β) (init : β) :
|
||||
(forIn xs init (fun a b => .yield <$> f a b)).run =
|
||||
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 := by
|
||||
rcases xs with ⟨xs⟩
|
||||
simp [List.foldl_map]
|
||||
xs.foldl (fun b a => f a b) init :=
|
||||
forIn_pure_yield_eq_foldl _ _
|
||||
|
||||
@[simp] theorem forIn_map [Monad m] [LawfulMonad m]
|
||||
{xs : Array α} {g : α → β} {f : β → γ → m (ForInStep γ)} :
|
||||
@@ -264,7 +292,7 @@ end Array
|
||||
|
||||
namespace List
|
||||
|
||||
theorem filterM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bool} :
|
||||
@[grind =] theorem filterM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bool} :
|
||||
l.toArray.filterM p = toArray <$> l.filterM p := by
|
||||
simp only [Array.filterM, filterM, foldlM_toArray, bind_pure_comp, Functor.map_map]
|
||||
conv => lhs; rw [← reverse_nil]
|
||||
@@ -284,7 +312,7 @@ theorem filterM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bo
|
||||
subst w
|
||||
rw [filterM_toArray]
|
||||
|
||||
theorem filterRevM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bool} :
|
||||
@[grind =] theorem filterRevM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m Bool} :
|
||||
l.toArray.filterRevM p = toArray <$> l.filterRevM p := by
|
||||
simp [Array.filterRevM, filterRevM]
|
||||
rw [← foldlM_reverse, ← foldlM_toArray, ← Array.filterM, filterM_toArray]
|
||||
@@ -296,7 +324,7 @@ theorem filterRevM_toArray [Monad m] [LawfulMonad m] {l : List α} {p : α → m
|
||||
subst w
|
||||
rw [filterRevM_toArray]
|
||||
|
||||
theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α → m (Option β)} :
|
||||
@[grind =] theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α → m (Option β)} :
|
||||
l.toArray.filterMapM f = toArray <$> l.filterMapM f := by
|
||||
simp [Array.filterMapM, filterMapM]
|
||||
conv => lhs; rw [← reverse_nil]
|
||||
@@ -314,7 +342,7 @@ theorem filterMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α → m
|
||||
subst w
|
||||
rw [filterMapM_toArray]
|
||||
|
||||
@[simp] theorem flatMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α → m (Array β)} :
|
||||
@[simp, grind =] theorem flatMapM_toArray [Monad m] [LawfulMonad m] {l : List α} {f : α → m (Array β)} :
|
||||
l.toArray.flatMapM f = toArray <$> l.flatMapM (fun a => Array.toList <$> f a) := by
|
||||
simp only [Array.flatMapM, bind_pure_comp, foldlM_toArray, flatMapM]
|
||||
conv => lhs; arg 2; change [].reverse.flatten.toArray
|
||||
|
||||
@@ -6,8 +6,11 @@ Authors: Kim Morrison
|
||||
module
|
||||
|
||||
prelude
|
||||
import all Init.Data.Array.Basic
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.Monadic
|
||||
import Init.Data.List.OfFn
|
||||
import Init.Data.List.FinRange
|
||||
|
||||
/-!
|
||||
# Theorems about `Array.ofFn`
|
||||
@@ -18,6 +21,8 @@ set_option linter.indexVariables true -- Enforce naming conventions for index va
|
||||
|
||||
namespace Array
|
||||
|
||||
/-! ### ofFn -/
|
||||
|
||||
@[simp] theorem ofFn_zero {f : Fin 0 → α} : ofFn f = #[] := by
|
||||
simp [ofFn, ofFn.go]
|
||||
|
||||
@@ -25,12 +30,17 @@ theorem ofFn_succ {f : Fin (n+1) → α} :
|
||||
ofFn f = (ofFn (fun (i : Fin n) => f i.castSucc)).push (f ⟨n, by omega⟩) := by
|
||||
ext i h₁ h₂
|
||||
· simp
|
||||
· simp [getElem_push]
|
||||
split <;> rename_i h₃
|
||||
· rfl
|
||||
· congr
|
||||
simp at h₁ h₂
|
||||
omega
|
||||
· simp only [getElem_ofFn, getElem_push, size_ofFn, Fin.castSucc_mk, left_eq_dite_iff,
|
||||
Nat.not_lt]
|
||||
simp only [size_ofFn] at h₁
|
||||
intro h₃
|
||||
simp only [show i = n by omega]
|
||||
|
||||
theorem ofFn_add {n m} {f : Fin (n + m) → α} :
|
||||
ofFn f = (ofFn (fun i => f (i.castLE (Nat.le_add_right n m)))) ++ (ofFn (fun i => f (i.natAdd n))) := by
|
||||
induction m with
|
||||
| zero => simp
|
||||
| succ m ih => simp [ofFn_succ, ih]
|
||||
|
||||
@[simp] theorem _root_.List.toArray_ofFn {f : Fin n → α} : (List.ofFn f).toArray = Array.ofFn f := by
|
||||
ext <;> simp
|
||||
@@ -38,6 +48,11 @@ theorem ofFn_succ {f : Fin (n+1) → α} :
|
||||
@[simp] theorem toList_ofFn {f : Fin n → α} : (Array.ofFn f).toList = List.ofFn f := by
|
||||
apply List.ext_getElem <;> simp
|
||||
|
||||
theorem ofFn_succ' {f : Fin (n+1) → α} :
|
||||
ofFn f = #[f 0] ++ ofFn (fun i => f i.succ) := by
|
||||
apply Array.toList_inj.mp
|
||||
simp [List.ofFn_succ]
|
||||
|
||||
@[simp]
|
||||
theorem ofFn_eq_empty_iff {f : Fin n → α} : ofFn f = #[] ↔ n = 0 := by
|
||||
rw [← Array.toList_inj]
|
||||
@@ -52,4 +67,70 @@ theorem mem_ofFn {n} {f : Fin n → α} {a : α} : a ∈ ofFn f ↔ ∃ i, f i =
|
||||
· rintro ⟨i, rfl⟩
|
||||
apply mem_of_getElem (i := i) <;> simp
|
||||
|
||||
/-! ### ofFnM -/
|
||||
|
||||
/-- Construct (in a monadic context) an array by applying a monadic function to each index. -/
|
||||
def ofFnM {n} [Monad m] (f : Fin n → m α) : m (Array α) :=
|
||||
Fin.foldlM n (fun xs i => xs.push <$> f i) (Array.emptyWithCapacity n)
|
||||
|
||||
@[simp]
|
||||
theorem ofFnM_zero [Monad m] {f : Fin 0 → m α} : ofFnM f = pure #[] := by
|
||||
simp [ofFnM]
|
||||
|
||||
theorem ofFnM_succ' {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) → m α} :
|
||||
ofFnM f = (do
|
||||
let a ← f 0
|
||||
let as ← ofFnM fun i => f i.succ
|
||||
pure (#[a] ++ as)) := by
|
||||
simp [ofFnM, Fin.foldlM_eq_foldlM_finRange, List.foldlM_push_eq_append, List.finRange_succ, Function.comp_def]
|
||||
|
||||
theorem ofFnM_succ {n} [Monad m] [LawfulMonad m] {f : Fin (n + 1) → m α} :
|
||||
ofFnM f = (do
|
||||
let as ← ofFnM fun i => f i.castSucc
|
||||
let a ← f (Fin.last n)
|
||||
pure (as.push a)) := by
|
||||
simp [ofFnM, Fin.foldlM_succ_last]
|
||||
|
||||
theorem ofFnM_add {n m} [Monad m] [LawfulMonad m] {f : Fin (n + k) → m α} :
|
||||
ofFnM f = (do
|
||||
let as ← ofFnM fun i : Fin n => f (i.castLE (Nat.le_add_right n k))
|
||||
let bs ← ofFnM fun i : Fin k => f (i.natAdd n)
|
||||
pure (as ++ bs)) := by
|
||||
induction k with
|
||||
| zero => simp
|
||||
| succ k ih =>
|
||||
simp only [ofFnM_succ, Nat.add_eq, ih, Fin.castSucc_castLE, Fin.castSucc_natAdd, bind_pure_comp,
|
||||
bind_assoc, bind_map_left, Fin.natAdd_last, map_bind, Functor.map_map]
|
||||
congr 1
|
||||
funext xs
|
||||
congr 1
|
||||
funext ys
|
||||
congr 1
|
||||
funext x
|
||||
simp
|
||||
|
||||
@[simp] theorem toList_ofFnM [Monad m] [LawfulMonad m] {f : Fin n → m α} :
|
||||
toList <$> ofFnM f = List.ofFnM f := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih => simp [ofFnM_succ, List.ofFnM_succ_last, ← ih]
|
||||
|
||||
@[simp]
|
||||
theorem ofFnM_pure_comp [Monad m] [LawfulMonad m] {n} {f : Fin n → α} :
|
||||
ofFnM (pure ∘ f) = (pure (ofFn f) : m (Array α)) := by
|
||||
apply Array.map_toList_inj.mp
|
||||
simp
|
||||
|
||||
-- Variant of `ofFnM_pure_comp` using a lambda.
|
||||
-- This is not marked a `@[simp]` as it would match on every occurrence of `ofFnM`.
|
||||
theorem ofFnM_pure [Monad m] [LawfulMonad m] {n} {f : Fin n → α} :
|
||||
ofFnM (fun i => pure (f i)) = (pure (ofFn f) : m (Array α)) :=
|
||||
ofFnM_pure_comp
|
||||
|
||||
@[simp, grind =] theorem idRun_ofFnM {f : Fin n → Id α} :
|
||||
Id.run (ofFnM f) = ofFn (fun i => Id.run (f i)) := by
|
||||
induction n with
|
||||
| zero => simp
|
||||
| succ n ih => simp [ofFnM_succ', ofFn_succ', ih]
|
||||
|
||||
end Array
|
||||
|
||||
@@ -7,6 +7,7 @@ module
|
||||
|
||||
prelude
|
||||
import Init.Data.List.Nat.Perm
|
||||
import all Init.Data.Array.Basic
|
||||
import Init.Data.Array.Lemmas
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
|
||||
@@ -27,23 +27,27 @@ Internal implementation of `Array.qsort`.
|
||||
|
||||
It does so by first swapping the elements at indices `lo`, `mid := (lo + hi) / 2`, and `hi`
|
||||
if necessary so that the middle (pivot) element is at index `hi`.
|
||||
We then iterate from `j = lo` to `j = hi`, with a pointer `i` starting at `lo`, and
|
||||
We then iterate from `k = lo` to `k = hi`, with a pointer `i` starting at `lo`, and
|
||||
swapping each element which is less than the pivot to position `i`, and then incrementing `i`.
|
||||
-/
|
||||
def qpartition {n} (as : Vector α n) (lt : α → α → Bool) (lo hi : Nat)
|
||||
(hlo : lo < n := by omega) (hhi : hi < n := by omega) : {m : Nat // lo ≤ m ∧ m < n} × Vector α n :=
|
||||
def qpartition {n} (as : Vector α n) (lt : α → α → Bool) (lo hi : Nat) (w : lo ≤ hi := by omega)
|
||||
(hlo : lo < n := by omega) (hhi : hi < n := by omega) : {m : Nat // lo ≤ m ∧ m ≤ hi} × Vector α n :=
|
||||
let mid := (lo + hi) / 2
|
||||
let as := if lt as[mid] as[lo] then as.swap lo mid else as
|
||||
let as := if lt as[hi] as[lo] then as.swap lo hi else as
|
||||
let as := if lt as[mid] as[hi] then as.swap mid hi else as
|
||||
let pivot := as[hi]
|
||||
let rec loop (as : Vector α n) (i j : Nat)
|
||||
(ilo : lo ≤ i := by omega) (jh : j < n := by omega) (w : i ≤ j := by omega) :=
|
||||
if h : j < hi then
|
||||
if lt as[j] pivot then
|
||||
loop (as.swap i j) (i+1) (j+1)
|
||||
-- During this loop, elements below in `[lo, i)` are less than `pivot`,
|
||||
-- elements in `[i, k)` are greater than or equal to `pivot`,
|
||||
-- elements in `[k, hi)` are unexamined,
|
||||
-- while `as[hi]` is (by definition) the pivot.
|
||||
let rec loop (as : Vector α n) (i k : Nat)
|
||||
(ilo : lo ≤ i := by omega) (ik : i ≤ k := by omega) (w : k ≤ hi := by omega) :=
|
||||
if h : k < hi then
|
||||
if lt as[k] pivot then
|
||||
loop (as.swap i k) (i+1) (k+1)
|
||||
else
|
||||
loop as i (j+1)
|
||||
loop as i (k+1)
|
||||
else
|
||||
(⟨i, ilo, by omega⟩, as.swap i hi)
|
||||
loop as lo lo
|
||||
@@ -51,25 +55,28 @@ def qpartition {n} (as : Vector α n) (lt : α → α → Bool) (lo hi : Nat)
|
||||
/--
|
||||
In-place quicksort.
|
||||
|
||||
`qsort as lt low high` sorts the subarray `as[low:high+1]` in-place using `lt` to compare elements.
|
||||
`qsort as lt lo hi` sorts the subarray `as[lo:hi+1]` in-place using `lt` to compare elements.
|
||||
-/
|
||||
@[inline] def qsort (as : Array α) (lt : α → α → Bool := by exact (· < ·))
|
||||
(low := 0) (high := as.size - 1) : Array α :=
|
||||
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat)
|
||||
(lo := 0) (hi := as.size - 1) : Array α :=
|
||||
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat) (w : lo ≤ hi := by omega)
|
||||
(hlo : lo < n := by omega) (hhi : hi < n := by omega) :=
|
||||
if h₁ : lo < hi then
|
||||
let ⟨⟨mid, hmid⟩, as⟩ := qpartition as lt lo hi
|
||||
if h₂ : mid ≥ hi then
|
||||
-- This only occurs when `hi ≤ lo`,
|
||||
-- and thus `as[lo:hi+1]` is trivially already sorted.
|
||||
as
|
||||
else
|
||||
-- Otherwise, we recursively sort the two subarrays.
|
||||
sort (sort as lo mid) (mid+1) hi
|
||||
else as
|
||||
if h : as.size = 0 then
|
||||
as
|
||||
else
|
||||
let low := min low (as.size - 1)
|
||||
let high := min high (as.size - 1)
|
||||
sort as.toVector low high |>.toArray
|
||||
let lo := min lo (as.size - 1)
|
||||
let hi := max lo (min hi (as.size - 1))
|
||||
sort as.toVector lo hi |>.toArray
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
/--
|
||||
|
||||
@@ -7,7 +7,8 @@ module
|
||||
|
||||
prelude
|
||||
import Init.Data.Array.Lemmas
|
||||
import Init.Data.Array.OfFn
|
||||
import all Init.Data.Array.Basic
|
||||
import all Init.Data.Array.OfFn
|
||||
import Init.Data.Array.MapIdx
|
||||
import Init.Data.Array.Zip
|
||||
import Init.Data.List.Nat.Range
|
||||
@@ -80,7 +81,7 @@ theorem range'_append {s m n step : Nat} :
|
||||
range' s m ++ range' (s + m) n = range' s (m + n) := by simpa using range'_append (step := 1)
|
||||
|
||||
theorem range'_concat {s n : Nat} : range' s (n + 1) step = range' s n step ++ #[s + step * n] := by
|
||||
simpa using range'_append.symm
|
||||
simp [← range'_append]
|
||||
|
||||
theorem range'_1_concat {s n : Nat} : range' s (n + 1) = range' s n ++ #[s + n] := by
|
||||
simp [range'_concat]
|
||||
|
||||
@@ -290,7 +290,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def foldl {α : Type u} {β : Type v} (f : β → α → β) (init : β) (as : Subarray α) : β :=
|
||||
Id.run <| as.foldlM f (init := init)
|
||||
Id.run <| as.foldlM (pure <| f · ·) (init := init)
|
||||
|
||||
/--
|
||||
Folds an operation from right to left over the elements in a subarray.
|
||||
@@ -304,7 +304,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def foldr {α : Type u} {β : Type v} (f : α → β → β) (init : β) (as : Subarray α) : β :=
|
||||
Id.run <| as.foldrM f (init := init)
|
||||
Id.run <| as.foldrM (pure <| f · ·) (init := init)
|
||||
|
||||
/--
|
||||
Checks whether any of the elements in a subarray satisfy a Boolean predicate.
|
||||
@@ -314,7 +314,7 @@ an element that satisfies the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
def any {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.anyM p
|
||||
Id.run <| as.anyM (pure <| p ·)
|
||||
|
||||
/--
|
||||
Checks whether all of the elements in a subarray satisfy a Boolean predicate.
|
||||
@@ -324,7 +324,7 @@ an element that does not satisfy the predicate is found.
|
||||
-/
|
||||
@[inline]
|
||||
def all {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
|
||||
Id.run <| as.allM p
|
||||
Id.run <| as.allM (pure <| p ·)
|
||||
|
||||
/--
|
||||
Applies a monadic function to each element in a subarray in reverse order, stopping at the first
|
||||
@@ -394,7 +394,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def findRev? {α : Type} (as : Subarray α) (p : α → Bool) : Option α :=
|
||||
Id.run <| as.findRevM? p
|
||||
Id.run <| as.findRevM? (pure <| p ·)
|
||||
|
||||
end Subarray
|
||||
|
||||
@@ -464,8 +464,12 @@ instance : Append (Subarray α) where
|
||||
let a := x.toArray ++ y.toArray
|
||||
a.toSubarray 0 a.size
|
||||
|
||||
/-- `Subarray` representation. -/
|
||||
protected def Subarray.repr [Repr α] (s : Subarray α) : Std.Format :=
|
||||
repr s.toArray ++ ".toSubarray"
|
||||
|
||||
instance [Repr α] : Repr (Subarray α) where
|
||||
reprPrec s _ := repr s.toArray ++ ".toSubarray"
|
||||
reprPrec s _ := Subarray.repr s
|
||||
|
||||
instance [ToString α] : ToString (Subarray α) where
|
||||
toString s := toString s.toArray
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user