Compare commits

..

2 Commits

Author SHA1 Message Date
Leonardo de Moura
4187edf5e7 chore: update stage0 2022-09-06 17:26:15 -07:00
Leonardo de Moura
7bc9c12462 chore: remove [inline] from parser combinators 2022-09-06 17:24:50 -07:00
2436 changed files with 15596 additions and 37521 deletions

1
.gitattributes vendored
View File

@@ -1,4 +1,3 @@
*.lean text eol=lf
*.expected.out -text
RELEASES.md merge=union
stage0/** binary linguist-generated

View File

@@ -1,33 +0,0 @@
name: add PR to changelog
on:
# needs read/write GH token, do *not* execute arbitrary code from PR
pull_request_target:
types: [closed]
jobs:
update-changelog:
if: |
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'changelog') &&
github.base_ref == 'master'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
# needs sufficiently elevated token to override branch protection rules
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- name: Update changelog
run: |
set -euxo pipefail
escaped_link=$(sed -e 's/[\/&]/\\&/g' <<'EOF'
[${{ github.event.pull_request.title}}](${{ github.event.pull_request.html_url }})
EOF
)
# insert link below first dashes line (https://stackoverflow.com/a/9453461/161659)
sed -i "0,/^---*/s/^---*/\0\n\n* $escaped_link./" RELEASES.md
# commit as github-actions bot (https://github.com/orgs/community/discussions/26560#discussioncomment-3252339)
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"
git commit -i RELEASES.md -m "doc: update changelog"
git push

View File

@@ -11,22 +11,17 @@ on:
schedule:
- cron: '0 7 * * *' # 8AM CET/11PM PT
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
set-nightly:
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
runs-on: ubuntu-latest
outputs:
nightly: ${{ steps.set.outputs.nightly }}
steps:
- name: Checkout
uses: actions/checkout@v3
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
uses: actions/checkout@v2
- name: Set Nightly
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
id: set
run: |
if [[ -n '${{ secrets.PUSH_NIGHTLY_TOKEN }}' ]]; then
@@ -35,13 +30,14 @@ jobs:
LEAN_VERSION_STRING="nightly-$(date -u +%F)"
# do nothing if commit already has a different tag
if [[ $(git name-rev --name-only --tags --no-undefined HEAD 2> /dev/null || echo $LEAN_VERSION_STRING) == $LEAN_VERSION_STRING ]]; then
echo "nightly=$LEAN_VERSION_STRING" >> $GITHUB_OUTPUT
echo "::set-output name=nightly::$LEAN_VERSION_STRING"
fi
fi
build:
needs: set-nightly
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
# `always` *must* be used to continue even after a dependency has been skipped
if: always() && (github.event_name != 'schedule' || github.repository == 'leanprover/lean4')
runs-on: ${{ matrix.os }}
defaults:
run:
@@ -54,7 +50,7 @@ jobs:
os: ubuntu-latest
release: true
shell: nix-shell --arg pkgsDist "import (fetchTarball \"channel:nixos-19.03\") {{}}" --run "bash -euxo pipefail {0}"
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/14.0.0/lean-llvm-x86_64-linux-gnu.tar.zst
prepare-llvm: ../script/prepare-llvm-linux.sh lean-llvm*
binary-check: ldd -v
# foreign code may be linked against more recent glibc
@@ -66,20 +62,18 @@ jobs:
- name: Linux Debug
os: ubuntu-latest
CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Debug
# exclude seriously slow tests
CTEST_OPTIONS: -E 'interactivetest|leanpkgtest|laketest|benchtest'
- name: Linux fsanitize
os: ubuntu-latest
# turn off custom allocator & symbolic functions to make LSAN do its magic
CMAKE_OPTIONS: -DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address,undefined -DLEANC_EXTRA_FLAGS='-fsanitize=address,undefined -fsanitize-link-c++-runtime' -DSMALL_ALLOCATOR=OFF -DBSYMBOLIC=OFF
# exclude seriously slow/problematic tests (laketests crash)
CTEST_OPTIONS: -E 'interactivetest|leanpkgtest|laketest|benchtest'
# exclude problematic tests
CTEST_OPTIONS: -E laketest
- name: macOS
os: macos-latest
release: true
shell: bash -euxo pipefail {0}
CMAKE_OPTIONS: -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/14.0.0/lean-llvm-x86_64-apple-darwin.tar.zst
prepare-llvm: ../script/prepare-llvm-macos.sh lean-llvm*
binary-check: otool -L
tar: gtar # https://github.com/actions/runner-images/issues/2619
@@ -89,7 +83,7 @@ jobs:
cross: true
shell: bash -euxo pipefail {0}
CMAKE_OPTIONS: -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-darwin_aarch64
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/14.0.0/lean-llvm-aarch64-apple-darwin.tar.zst https://github.com/leanprover/lean-llvm/releases/download/14.0.0/lean-llvm-x86_64-apple-darwin.tar.zst
prepare-llvm: EXTRA_FLAGS=--target=aarch64-apple-darwin ../script/prepare-llvm-macos.sh lean-llvm-aarch64-* lean-llvm-x86_64-*
binary-check: otool -L
tar: gtar # https://github.com/actions/runner-images/issues/2619
@@ -97,10 +91,10 @@ jobs:
os: windows-2022
release: true
shell: msys2 {0}
CMAKE_OPTIONS: -G "Unix Makefiles" -DUSE_GMP=OFF
CMAKE_OPTIONS: -G "Unix Makefiles"
# for reasons unknown, interactivetests are flaky on Windows
CTEST_OPTIONS: --repeat until-pass:2
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/14.0.0/lean-llvm-x86_64-w64-windows-gnu.tar.zst
prepare-llvm: ../script/prepare-llvm-mingw.sh lean-llvm*
binary-check: ldd
- name: Linux aarch64
@@ -109,7 +103,7 @@ jobs:
release: true
cross: true
shell: nix-shell --arg pkgsDist "import (fetchTarball \"channel:nixos-19.03\") {{ localSystem.config = \"aarch64-unknown-linux-gnu\"; }}" --run "bash -euxo pipefail {0}"
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst
llvm-url: https://github.com/leanprover/lean-llvm/releases/download/14.0.0/lean-llvm-x86_64-linux-gnu.tar.zst https://github.com/leanprover/lean-llvm/releases/download/14.0.0/lean-llvm-aarch64-linux-gnu.tar.zst
prepare-llvm: EXTRA_FLAGS=--target=aarch64-unknown-linux-gnu ../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*
# complete all jobs
fail-fast: false
@@ -127,13 +121,11 @@ jobs:
CXX: c++
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
submodules: true
- name: Install Nix
uses: cachix/install-nix-action@v18
with:
install_url: https://releases.nixos.org/nix/nix-2.12.0/install
uses: cachix/install-nix-action@v15
if: matrix.os == 'ubuntu-latest'
- name: Install MSYS2
uses: msys2/setup-msys2@v2
@@ -144,10 +136,10 @@ jobs:
if: matrix.os == 'windows-2022'
- name: Install Brew Packages
run: |
brew install ccache tree zstd coreutils gmp
brew install ccache tree zstd coreutils
if: matrix.os == 'macos-latest'
- name: Cache
uses: actions/cache@v3
uses: actions/cache@v2
with:
path: .ccache
key: ${{ matrix.name }}-build-v3-${{ github.sha }}
@@ -159,17 +151,10 @@ jobs:
# open nix-shell once for initial setup
true
if: matrix.os == 'ubuntu-latest'
- name: Set up core dumps
run: |
mkdir -p $PWD/coredumps
# store in current directory, for easy uploading together with binary
echo $PWD/coredumps/%e.%p.%t | sudo tee /proc/sys/kernel/core_pattern
if: matrix.os == 'ubuntu-latest'
- name: Build
run: |
mkdir build
cd build
ulimit -c unlimited # coredumps
OPTIONS=()
if [[ -n '${{ matrix.prepare-llvm }}' ]]; then
wget -q ${{ matrix.llvm-url }}
@@ -194,13 +179,13 @@ jobs:
dir=$(echo lean-*)
mkdir pack
# high-compression tar.zst + zip for release, fast tar.zst otherwise
if [[ '${{ startsWith(github.ref, 'refs/tags/') && matrix.release }}' == true || -n '${{ needs.set-nightly.outputs.nightly }}' ]]; then
if [[ '${{ startsWith(github.ref, 'refs/tags/v') && matrix.release }}' == true || -n '${{ needs.set-nightly.outputs.nightly }}' ]]; then
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -19 -o pack/$dir.tar.zst
zip -rq pack/$dir.zip $dir
else
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
fi
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v2
if: matrix.release
with:
name: build-${{ matrix.name }}
@@ -212,7 +197,6 @@ jobs:
- name: Test
run: |
cd build/stage1
ulimit -c unlimited # coredumps
# exclude nonreproducible test
ctest -j4 --output-on-failure -E leanlaketest_git ${{ matrix.CTEST_OPTIONS }} < /dev/null
if: ${{ !matrix.cross }}
@@ -222,13 +206,11 @@ jobs:
- name: Build Stage 2
run: |
cd build
ulimit -c unlimited # coredumps
make -j4 stage2
if: matrix.build-stage2 || matrix.check-stage3
- name: Check Stage 3
run: |
cd build
ulimit -c unlimited # coredumps
make -j4 check-stage3
if: matrix.check-stage3
- name: Test Speedcenter Benchmarks
@@ -241,38 +223,21 @@ jobs:
- name: Check rebootstrap
run: |
cd build
ulimit -c unlimited # coredumps
make update-stage0 && make -j4
if: matrix.name == 'Linux'
- name: CCache stats
run: ccache -s
- name: Show stacktrace for coredumps
if: ${{ failure() }} && matrix.os == 'ubuntu-latest'
run: |
for c in coredumps/*; do
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
echo bt | $GDB/bin/gdb -q $progbin $c || true
done
- name: Upload coredumps
uses: actions/upload-artifact@v3
if: ${{ failure() }} && matrix.os == 'ubuntu-latest'
with:
name: coredumps-${{ matrix.name }}
path: |
./coredumps
./build/stage0/bin/lean
./build/stage0/lib/lean/libleanshared.so
./build/stage1/bin/lean
./build/stage1/lib/lean/libleanshared.so
./build/stage2/bin/lean
./build/stage2/lib/lean/libleanshared.so
release:
if: startsWith(github.ref, 'refs/tags/')
# When GitHub says "If a job fails, all jobs that need it are skipped unless
# the jobs use a conditional expression that causes the job to continue.", don't believe
# their lies. It's actually the entire closure (i.e. including `set-nightly`) that
# must succeed for subsequent to be run without `always()`.
if: always() && needs.build.result == 'success' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v2
with:
path: artifacts
- name: Release
@@ -289,12 +254,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
# needed for tagging
fetch-depth: 0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v2
with:
path: artifacts
- name: Prepare Nightly Release

View File

@@ -9,10 +9,6 @@ on:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
Build:
runs-on: ${{ matrix.os }}
@@ -33,9 +29,9 @@ jobs:
NIX_BUILD_ARGS: -v --print-build-logs --fallback
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: Install Nix
uses: cachix/install-nix-action@v18
uses: cachix/install-nix-action@v15
with:
# https://github.com/NixOS/nix/issues/6572
install_url: https://releases.nixos.org/nix/nix-2.7.0/install
@@ -43,7 +39,7 @@ jobs:
extra-sandbox-paths = /nix/var/cache/ccache
substituters = file://${{ github.workspace }}/nix-store-cache-copy?priority=10&trusted=true https://cache.nixos.org
- name: Set Up Nix Cache
uses: actions/cache@v3
uses: actions/cache@v2
with:
path: nix-store-cache
key: ${{ matrix.name }}-nix-store-cache-${{ github.sha }}
@@ -61,7 +57,7 @@ jobs:
sudo mkdir -m0770 -p /nix/var/cache/ccache
sudo chown -R $USER /nix/var/cache/ccache
- name: Setup CCache Cache
uses: actions/cache@v3
uses: actions/cache@v2
with:
path: /nix/var/cache/ccache
key: ${{ matrix.name }}-nix-ccache-${{ github.sha }}
@@ -74,20 +70,21 @@ jobs:
sudo chown -R root:nixbld /nix/var/cache
sudo chmod -R 770 /nix/var/cache
- name: Install Cachix
uses: cachix/cachix-action@v12
uses: cachix/cachix-action@v10
with:
name: lean4
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
skipPush: true # we push specific outputs only
- name: Build
run: |
nix build $NIX_BUILD_ARGS .#cacheRoots -o push-build
# .o files are not a runtime dependency on macOS because of lack of thin archives
nix build $NIX_BUILD_ARGS .#stage0 .#stage1.lean-all .#Lean.oTree .#iTree .#modDepsFiles -o push-build
- name: Test
run: |
nix build $NIX_BUILD_ARGS .#test -o push-test
- name: Build manual
run: |
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,test,inked} -o push-doc
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,test} -o push-doc
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc
if: matrix.name == 'Nix Linux'
- name: Push to Cachix

View File

@@ -10,11 +10,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check Commit Message
uses: actions/github-script@v6
uses: actions/github-script@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: commits } = await github.rest.pulls.listCommits({
const { data: commits } = await github.pulls.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
@@ -22,7 +22,7 @@ jobs:
console.log(commits[0].commit.message);
// check first commit only (and only once) since later commits might be intended to be squashed away
if (!/^(feat|fix|doc|style|refactor|test|chore|perf): .*[^.]($|\n\n)/.test(commits[0].commit.message)) {
await github.rest.issues.createComment({
await github.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,

View File

@@ -15,10 +15,6 @@ community using the [lean4 Zulip channel](https://leanprover.zulipchat.com/#narr
Simple fixes for **typos and clear bugs** are welcome.
# **IMPORTANT**
We are currently overwhelmed. We respectfully request that you hold off on submitting Pull Requests and creating Request for Comments (RFCs) at this time. Our team is actively seeking funding to expand the Lean development team and improve our capacity to review and integrate contributions. We appreciate your understanding and look forward to being able to accept contributions in the near future. In the meantime, the process described in the following sections is temporarily suspended.
## Documentation
Tutorial-like examples are very welcome.

View File

@@ -1,5 +1,5 @@
This is the repository for **Lean 4**, which is being actively developed and published as nightly releases.
Stable point releases are planned for a later date after establishing a robust release process.
This is the repository for **Lean 4**, which is currently being released as milestone releases towards a first stable release.
[Lean 3](https://github.com/leanprover/lean) is still the latest stable release.
# About
@@ -8,7 +8,7 @@ Stable point releases are planned for a later date after establishing a robust r
- [Quick tour video](https://youtu.be/zyXtbb_eYbY)
- [Homepage](https://leanprover.github.io)
- [Theorem Proving Tutorial](https://leanprover.github.io/theorem_proving_in_lean4/)
- [Functional Programming in Lean](https://leanprover.github.io/functional_programming_in_lean/)
- [Functional Programming in Lean](https://leanprover.github.io/functional_programming_in_lean/) **first chapter is available!**
- [Manual](https://leanprover.github.io/lean4/doc/)
- [Release notes](RELEASES.md) starting at v4.0.0-m3
- [Examples](https://leanprover.github.io/lean4/doc/examples.html)

View File

@@ -1,97 +1,3 @@
Unreleased
---------
* [New `have this` implementation](https://github.com/leanprover/lean4/pull/2247).
`this` is now a regular identifier again that is implicitly introduced by anonymous `have :=` for the remainder of the tactic block. It used to be a keyword that was visible in all scopes and led to unexpected behavior when explicitly used as a binder name.
* [Show typeclass and tactic names in profile output](https://github.com/leanprover/lean4/pull/2170).
* [Make `calc` require the sequence of relation/proof-s to have the same indentation](https://github.com/leanprover/lean4/pull/1844),
and [add `calc` alternative syntax allowing underscores `_` in the first relation](https://github.com/leanprover/lean4/pull/1844).
The flexible indentation in `calc` was often used to align the relation symbols:
```lean
example (x y : Nat) : (x + y) * (x + y) = x * x + y * x + x * y + y * y :=
calc
(x + y) * (x + y) = (x + y) * x + (x + y) * y := by rw [Nat.mul_add]
-- improper indentation
_ = x * x + y * x + (x + y) * y := by rw [Nat.add_mul]
_ = x * x + y * x + (x * y + y * y) := by rw [Nat.add_mul]
_ = x * x + y * x + x * y + y * y := by rw [←Nat.add_assoc]
```
This is no longer legal. The new syntax puts the first term right after the `calc` and each step has the same indentation:
```lean
example (x y : Nat) : (x + y) * (x + y) = x * x + y * x + x * y + y * y :=
calc (x + y) * (x + y)
_ = (x + y) * x + (x + y) * y := by rw [Nat.mul_add]
_ = x * x + y * x + (x + y) * y := by rw [Nat.add_mul]
_ = x * x + y * x + (x * y + y * y) := by rw [Nat.add_mul]
_ = x * x + y * x + x * y + y * y := by rw [←Nat.add_assoc]
```
* Update Lake to latest prerelease.
* [Make go-to-definition on a typeclass projection application go to the instance(s)](https://github.com/leanprover/lean4/pull/1767).
* [Include timings in trace messages when `profiler` is true](https://github.com/leanprover/lean4/pull/1995).
* [Pretty-print signatures in hover and `#check <ident>`](https://github.com/leanprover/lean4/pull/1943).
* [Introduce parser memoization to avoid exponential behavior](https://github.com/leanprover/lean4/pull/1799).
* [feat: allow `doSeq` in `let x <- e | seq`](https://github.com/leanprover/lean4/pull/1809).
* [Add hover/go-to-def/refs for options](https://github.com/leanprover/lean4/pull/1783).
* [Add empty type ascription syntax `(e :)`](https://github.com/leanprover/lean4/pull/1797).
* [Make tokens in `<|>` relevant to syntax match](https://github.com/leanprover/lean4/pull/1744).
* [Add `linter.deprecated` option to silence deprecation warnings](https://github.com/leanprover/lean4/pull/1768).
* [Improve fuzzy-matching heuristics](https://github.com/leanprover/lean4/pull/1710).
* [Implementation-detail hypotheses](https://github.com/leanprover/lean4/pull/1692).
* [Hover information for `cases`/`induction` case names](https://github.com/leanprover/lean4/pull/1660).
* [Prefer longer parse even if unsuccessful](https://github.com/leanprover/lean4/pull/1658).
* [Show declaration module in hover](https://github.com/leanprover/lean4/pull/1638).
* [New `conv` mode structuring tactics](https://github.com/leanprover/lean4/pull/1636).
* `simp` can track information and can print an equivalent `simp only`. [PR #1626](https://github.com/leanprover/lean4/pull/1626).
* Enforce uniform indentation in tactic blocks / do blocks. See issue [#1606](https://github.com/leanprover/lean4/issues/1606).
* Moved `AssocList`, `HashMap`, `HashSet`, `RBMap`, `RBSet`, `PersistentArray`, `PersistentHashMap`, `PersistentHashSet` to the Lean package. The [standard library](https://github.com/leanprover/std4) contains versions that will evolve independently to simplify bootstrapping process.
* Standard library moved to the [std4 GitHub repository](https://github.com/leanprover/std4).
* `InteractiveGoals` now has information that a client infoview can use to show what parts of the goal have changed after applying a tactic. [PR #1610](https://github.com/leanprover/lean4/pull/1610).
* Add `[inheritDoc]` attribute. [PR #1480](https://github.com/leanprover/lean4/pull/1480).
* Expose that `panic = default`. [PR #1614](https://github.com/leanprover/lean4/pull/1614).
* New [code generator](https://github.com/leanprover/lean4/tree/master/src/Lean/Compiler/LCNF) project has started.
* Remove description argument frome `register_simp_attr`. [PR #1566](https://github.com/leanprover/lean4/pull/1566).
* [Additional concurrency primitives](https://github.com/leanprover/lean4/pull/1555).
* [Collapsible traces with messages](https://github.com/leanprover/lean4/pull/1448).
* [Hygienic resolution of namespaces](https://github.com/leanprover/lean4/pull/1442).
* [New `Float` functions](https://github.com/leanprover/lean4/pull/1460).
* Many new doc strings have been added to declarations at `Init`.
v4.0.0-m5 (07 August 2022)
---------
@@ -653,7 +559,7 @@ v4.0.0-m5 (07 August 2022)
...
```
* Remove support for `{}` annotation from inductive datatype constructors. This annotation was barely used, and we can control the binder information for parameter bindings using the new inductive family indices to parameter promotion. Example: the following declaration using `{}`
* Remove support for `{}` annotation from inductive datatype contructors. This annotation was barely used, and we can control the binder information for parameter bindings using the new inductive family indices to parameter promotion. Example: the following declaration using `{}`
```lean
inductive LE' (n : Nat) : Nat → Prop where
| refl {} : LE' n n -- Want `n` to be explicit
@@ -818,7 +724,7 @@ v4.0.0-m4 (23 March 2022)
initialize my_ext : SimpExtension ← registerSimpAttr `my_simp "my own simp attribute"
```
If you don't need to access `my_ext`, you can also use the macro
If you don't neet to acces `my_ext`, you can also use the macro
```lean
import Lean

View File

@@ -4,7 +4,6 @@
- [Tour of Lean](./tour.md)
- [Setting Up Lean](./quickstart.md)
- [Extended Setup Notes](./setup.md)
- [Nix Setup](./setup/nix.md)
- [Theorem Proving in Lean](./tpil.md)
- [Functional Programming in Lean](fplean.md)
- [Examples](./examples.md)
@@ -76,7 +75,6 @@
- [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

View File

@@ -43,5 +43,3 @@ set_option autoImplicit false
-- def compose (g : β → γ) (f : α → β) (x : α) : γ :=
-- g (f x)
```
The Lean language server provides [semantic highlighting](./semantic_highlighting.md) information to editors, and it provides
visual feedback whether an identifier has been interpreted as an auto bound implicit argument.

View File

@@ -7,27 +7,27 @@ 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
.. code-block:: lean
#print A.B.c -- opaque A.B.c : Nat
open A
#print B.c -- opaque A.B.c : Nat
```
namespace a
constant b.c : Nat
#print b.c -- constant a.b.c : Nat
end a
#print a.b.c -- constant a.b.c : Nat
open a
#print b.c -- constant 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
```
.. code-block:: lean
constant a : Nat
namespace a
constant a : Int
#print _root_.a -- constant a : Nat
#print a.a -- constant a.a : Int
end a
Contexts and Telescopes
=======================
@@ -53,14 +53,14 @@ 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
```
.. code-block:: text
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.
A *context* is sometimes called a *telescope*, but the latter is used more generally to include a sequence of declarations occuring 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.
@@ -76,7 +76,7 @@ Lean provides ways of adding new objects to the environment. The following provi
* ``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
using a proceture 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.
@@ -155,112 +155,112 @@ Under the propositions-as-type correspondence, when ``C x`` is an element of ``P
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)) ...
```
.. code-block :: text
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
.. code-block:: lean
-- BEGIN
inductive Empty : Type
namespace Hide
universe u v
inductive Unit : Type
| unit : Unit
-- BEGIN
inductive Empty : Type
inductive Bool : Type
| false : Bool
| true : Bool
inductive Unit : Type
| unit : Unit
inductive Prod (α : Type u) (β : Type v) : Type (max u v)
| mk : α → β → Prod α β
inductive Bool : Type
| false : Bool
| true : Bool
inductive Sum (α : Type u) (β : Type v)
| inl : αSum α β
| inr : β → Sum α β
inductive Prod (α : Type u) (β : Type v) : Type (max u v)
| mk : αβ → Prod α β
inductive Sigma (α : Type u) (β : α Type v)
| mk : (a : α) → β a → Sigma α β
inductive Sum (α : Type u) (β : Type v)
| inl : α → Sum α β
| inr : β → Sum α β
inductive false : Prop
inductive Sigma (α : Type u) (β : α → Type v)
| mk : (a : α) → β a → Sigma α β
inductive True : Prop
| trivial : True
inductive false : Prop
inductive And (p q : Prop) : Prop
| intro : p → q → And p q
inductive True : Prop
| trivial : True
inductive Or (p q : Prop) : Prop
| inl : p → Or p q
| inr : q → Or p q
inductive And (p q : Prop) : Prop
| intro : p → q → And p q
inductive Exists (α : Type u) (p : α Prop) : Prop
| intro : ∀ x : α, p x → Exists α p
inductive Or (p q : Prop) : Prop
| inl : p → Or p q
| inr : q → Or p q
inductive Subtype (α : Type u) (p : α → Prop) : Type u
| intro : ∀ x : α, p x → Subtype α p
inductive Exists (α : Type u) (p : α → Prop) : Prop
| intro : ∀ x : α, p x → Exists α p
inductive Nat : Type
| zero : Nat
| succ : Nat → Nat
inductive Subtype (α : Type u) (p : α → Prop) : Type u
| intro : ∀ x : α, p x → Subtype α p
inductive List (α : Type u)
| nil : List α
| cons : α → List α → List α
inductive Nat : Type
| zero : Nat
| succ : Nat → Nat
-- full binary tree with nodes and leaves labeled from α
inductive BinTree (α : Type u)
| leaf : α → BinTree α
| node : BinTree αα → BinTree α → BinTree α
inductive List (α : Type u)
| nil : List α
| cons : α → List α → List α
-- every internal node has subtrees indexed by Nat
inductive CBT (α : Type u)
| leaf : αCBT α
| node : (Nat → CBT α)CBT α
-- END
end Hide
```
-- 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).
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 ``[matchPattern]`` [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
.. code-block:: lean
-- BEGIN
inductive Weekday
| sunday | monday | tuesday | wednesday
| thursday | friday | saturday
namespace Hide
universe u
inductive Nat
| zero
| succ (n : Nat) : Nat
-- BEGIN
inductive Weekday
| sunday | monday | tuesday | wednesday
| thursday | friday | saturday
inductive List (α : Type u)
| nil : List α
| cons (a : α) (l : List α) : List α
inductive Nat
| zero
| succ (n : Nat) : Nat
@[match_pattern]
def List.nil' (α : Type u) : List α := List.nil
inductive List (α : Type u)
| nil : List α
| cons (a : α) (l : List α) : List α
def length {α : Type u} : List α → Nat
| (List.nil' _) => 0
| (List.cons a l) => 1 + length l
-- END
@[matchPattern]
def List.nil' (α : Type u) : List α := List.nil
end Hide
```
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:
@@ -272,33 +272,33 @@ The type former, constructors, and eliminator are all part of Lean's axiomatic f
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
.. code-block:: lean
-- BEGIN
inductive Nat
| zero
| succ (n : Nat) : Nat
namespace Hide
universe u
#check Nat
#check @Nat.rec
#check Nat.zero
#check Nat.succ
-- BEGIN
inductive Nat
| zero
| succ (n : Nat) : Nat
#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
#check Nat
#check @Nat.rec
#check Nat.zero
#check Nat.succ
-- END
#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 Hide
```
-- END
end Hide
.. _inductive_families:
@@ -307,13 +307,13 @@ 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ₙ
```
.. code-block:: text
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ᵢ``.
@@ -343,34 +343,34 @@ The declaration of the type ``Foo`` as above results in the addition of the foll
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)) ...
```
.. code-block :: text
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
.. code-block:: lean
-- BEGIN
inductive Vector (α : Type u) : Nat → Type u
| nil : Vector 0
| succ : Π n, Vector n → Vector (n + 1)
namespace Hide
universe u
-- '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)
-- BEGIN
inductive Vector (α : Type u) : Nat → Type u
| nil : Vector 0
| succ : Π n, Vector n → Vector (n + 1)
inductive Eq {α : Sort u} (a : α) : α → Prop
| refl : Eq a
-- END
-- '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)
end Hide
```
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.
@@ -385,23 +385,23 @@ Lean supports two generalizations of the inductive families described above, nam
The first generalization allows for multiple inductive types to be defined simultaneously.
```
mutual
.. code-block:: text
inductive Foo (a : α) : Π (c : γ₁), Sort u
| constructor₁₁ : Π (b : β₁₁), Foo a t₁₁
| constructor₁₂ : Π (b : β₁₂), Foo a t₁₂
...
| constructor₁ₙ : Π (b : β₁ₙ), Foo a t₁ₙ
mutual
inductive Bar (a : α) : Π (c : γ), Sort u
| constructor₁ : Π (b : β₁), Bar a t
| constructor₂ : Π (b : β₂), Bar a t
...
| constructor₂ₘ : Π (b : β₂ₘ), Bar a t₂ₘ
inductive Foo (a : α) : Π (c : γ), Sort u
| constructor₁ : Π (b : β₁), Foo a t
| constructor₂ : Π (b : β₂), Foo a t
...
| constructor₁ₙ : Π (b : β₁ₙ), Foo a t₁ₙ
end
```
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.
@@ -411,24 +411,24 @@ The second generalization relaxes the restriction that in the recursive definiti
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
.. code-block:: lean
inductive Tree (α : Type u)
| mk : α → List (Tree α) → Tree α
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 DoubleTree (α : Type u)
| mk : α → List (DoubleTree α) × List (DoubleTree α) → DoubleTree α
-- END
```
inductive Tree (α : Type u)
| mk : α → List (Tree α) → Tree α
inductive DoubleTree (α : Type u)
| mk : α → List (DoubleTree α) × List (DoubleTree α) → DoubleTree α
-- END
.. _the_equation_compiler:
@@ -437,12 +437,12 @@ 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ₙ
```
.. code-block:: text
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``.
@@ -457,160 +457,160 @@ 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.
When identifiers are marked with the ``[matchPattern]`` 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
.. code-block:: lean
def sub2 : Nat → Nat
| zero => 0
| succ zero => 0
| succ (succ a) => a
open Nat
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 sub2 : Nat → Nat
| zero => 0
| succ zero => 0
| succ (succ a) => a
def baz : Char → Nat
| 'A' => 1
| 'B' => 2
| _ => 3
```
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
If any of the terms ``tᵢ`` in the template above contain a recursive call to ``foo``, the equation compiler 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
.. code-block:: lean
-- BEGIN
def fib : Nat → Nat
| 0 => 1
| 1 => 1
| (n+2) => fib (n+1) + fib n
namespace Hide
def append {α : Type} : List α → List α → List α
| [], l => l
| h::t, l => h :: append t l
-- BEGIN
def fib : Nat → Nat
| 0 => 1
| 1 => 1
| (n+2) => fib (n+1) + fib n
example : append [(1 : Nat), 2, 3] [4, 5] = [1, 2, 3, 4, 5] => rfl
-- END
def append {α : Type} : List α → List α → List α
| [], l => l
| h::t, l => h :: append t l
end Hide
```
example : append [(1 : Nat), 2, 3] [4, 5] = [1, 2, 3, 4, 5] => rfl
-- END
end Hide
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 show that each recursive call is decreasing under the lexicographic order of the arguments with respect to ``sizeOf`` measure. If it fails, the error message provides information as to the goal that Lean tried to prove. 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 this case of well-founded recursion, the defining equations hold only propositionally, and can be accessed using ``simp`` and ``rewrite`` with the name ``foo``.
```lean
namespace Hide
open Nat
.. code-block:: lean
-- 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
namespace Hide
open Nat
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
-- 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
end Hide
```
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
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.
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). They are 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
.. code-block:: lean
example (a : Nat) : even (a + 1) = odd a :=
by simp [even]
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) : odd (a + 1) = even a :=
by simp [odd]
```
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
.. code-block:: lean
open Even Odd
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
theorem not_odd_zero : ¬ Odd 0 := fun x => nomatch x
open Even Odd
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
theorem not_odd_zero : ¬ Odd 0 := fun x => nomatch x
inductive Term
| const : String → Term
| app : String → List Term → Term
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
open Term
inductive Term
| const : String → Term
| app : String → List Term → 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
```
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
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
.. code-block:: lean
inductive Vector (α : Type u) : Nat → Type u
| nil : Vector α 0
| cons : α → Vector α n → Vector α (n+1)
universe u
namespace Vector
inductive Vector (α : Type u) : Nat → Type u
| nil : Vector α 0
| cons : α → Vector α n → Vector α (n+1)
def head {α : Type} : Vector α (n+1) → α
| cons h t => h
namespace Vector
def tail {α : Type} : Vector α (n+1) → Vector α n
| cons h t => t
def head {α : Type} : Vector α (n+1) → α
| cons h t => h
def map {α β γ : Type} (f : α → β → γ) :
∀ {n}, Vector α n → Vector β n → Vector γ n
| 0, nil, nil => nil
| n+1, cons a va, cons b vb => cons (f a b) (map f va vb)
def tail {α : Type} : Vector α (n+1) → Vector α n
| cons h t => t
end Vector
```
def map {α β γ : Type} (f : α → β → γ) :
∀ {n}, Vector α n → Vector β n → Vector γ n
| 0, nil, nil => nil
| n+1, cons a va, cons b vb => cons (f a b) (map f va vb)
end Vector
.. _match_expressions:
@@ -619,41 +619,41 @@ 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ₘ
```
.. code-block:: text
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
```
.. code-block:: 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
.. code-block:: lean
def bar₂ (p : Nat × Nat) : Nat :=
match p with | (m, n) => m + n
def bar : Nat × Nat Nat
| (m, n) => m + n
def bar : Nat × Nat Nat :=
fun ⟨m, n => m + n
def bar₂ (p : Nat × Nat) : Nat :=
match p with | (m, n) => m + n
def bar₄ (p : Nat × Nat) : Nat :=
let ⟨m, n⟩ := p; 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:
@@ -676,10 +676,10 @@ 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 : α) extends Bar, Baz : Sort u :=
constructor :: (field₁ : β₁) ... (fieldₙ : βₙ)
```
.. code-block:: text
structure Foo (a : α) extends Bar, Baz : Sort u :=
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. Finally, ``(field₁ : β₁) ... (fieldₙ : βₙ)`` is a telescope relative to ``(a : α)`` and the fields in ``bar`` and ``baz``.
@@ -688,10 +688,10 @@ The declaration above is syntactic sugar for an inductive type declaration, and
- the type former : ``Foo : Π (a : α), Sort u``
- the single constructor :
```
Foo.constructor : Π (a : α) (toBar : Bar) (toBaz : Baz)
(field₁ : β₁) ... (fieldₙ : βₙ), Foo a
```
.. code-block:: text
Foo.constructor : Π (a : α) (toBar : Bar) (toBaz : Baz)
(field₁ : β₁) ... (fieldₙ : βₙ), Foo a
- the eliminator ``Foo.rec`` for the inductive type with that constructor
@@ -713,10 +713,10 @@ Similarly, Lean offers the following convenient syntax for constructing elements
- *anonymous constructor*: ``⟨ b₁, b₂, f₁, ..., fₙ ⟩``
- *record notation*:
```
{ toBar := b₁, toBaz := b₂, field₁ := f₁, ...,
field := fₙ : Foo a }
```
.. code-block:: text
{ 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.
@@ -728,63 +728,63 @@ Here ``t`` is a term of type ``Foo a`` for some ``a``. The notation instructs Le
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
.. code-block:: lean
structure Vec (α : Type u) (n : Nat) :=
(l : List α) (h : l.length = n)
universe u v
structure Foo (α : Type u) (β : Nat → Type v) : Type (max u v) :=
(a : α) (n : Nat) (b : β n)
structure Vec (α : Type u) (n : Nat) :=
(l : List α) (h : l.length = n)
structure Bar :=
(c : Nat := 8) (d : Nat)
structure Foo (α : Type u) (β : Nat → Type v) : Type (max u v) :=
(a : α) (n : Nat) (b : β n)
structure Baz extends Foo Nat (Vec Nat), Bar :=
(v : Vec Nat n)
structure Bar :=
(c : Nat := 8) (d : Nat)
#check Foo
#check @Foo.mk
#check @Foo.rec
structure Baz extends Foo Nat (Vec Nat), Bar :=
(v : Vec Nat n)
#check Foo.a
#check Foo.n
#check Foo.b
#check Foo
#check @Foo.mk
#check @Foo.rec
#check Baz
#check @Baz.mk
#check @Baz.rec
#check Foo.a
#check Foo.n
#check Foo.b
#check Baz.toFoo
#check Baz.toBar
#check Baz.v
#check Baz
#check @Baz.mk
#check @Baz.rec
def bzz := Vec.mk [1, 2, 3] rfl
#check Baz.toFoo
#check Baz.toBar
#check Baz.v
#check Vec.l bzz
#check Vec.h bzz
#check bzz.l
#check bzz.h
#check bzz.1
#check bzz.2
def bzz := Vec.mk [1, 2, 3] rfl
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 }
#check Vec.l bzz
#check Vec.h bzz
#check bzz.l
#check bzz.h
#check bzz.1
#check bzz.2
example : Foo Nat (Vec Nat) := ⟨1, 3, bzz⟩
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 : 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) := ⟨1, 3, bzz⟩
example : Foo Nat (Vec Nat) := { fzz with a := 7 }
example : Baz := { fzz with c := 5, d := 7, v := 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 : Bar := { c := 8, d := 9 }
example : Bar := { d := 9 } -- uses the default value for c
```
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:

View File

@@ -21,7 +21,7 @@ If `n` is 0, the corresponding C declaration is
extern s sym;
```
where `s` is the C translation of `β` as specified in the next section.
In the case of an `@[extern]` definition, the symbol's value is guaranteed to be initialized only after calling the Lean module's initializer or that of an importing module; see [Initialization](#initialization).
In the case of an `@[extern]` definition, the symbol's value is guaranteed to be initialized only after calling the Lean module's initializer or that of an importing module; see [Initialization](.#init).
If `n` is greater than 0, the corresponding C declaration is
```c
@@ -32,7 +32,7 @@ In the case of `@[extern]` all *irrelevant* types are removed first; see next se
### Translating Types from Lean to C
* The integer types `UInt8`, ..., `UInt64`, `USize` are represented by the C types `uint8_t`, ..., `uint64_t`, `size_t`, respectively
* The integer types `UInt8`, ..., `UInt64`, `USize` are represented by the C types `uint8_t`, ..., `uint64_t`, `usize_t`, respectively
* `Char` is represented by `uint32_t`
* `Float` is represented by `double`
* An *enum* inductive type of at least 2 and at most 2^32 constructors, each of which with no parameters, is represented by the first type of `uint8_t`, `uint16_t`, `uint32_t` that is sufficient to represent all constructor indices.
@@ -45,7 +45,7 @@ In the case of `@[extern]` all *irrelevant* types are removed first; see next se
* it has a single constructor with a single parameter of *relevant* type
is represented by the representation of that parameter's type.
For example, `{ x : α // p }`, the `Subtype` structure of a value of type `α` and an irrelevant proof, is represented by the representation of `α`.
* `Nat` is represented by `lean_object *`.
Its runtime value is either a pointer to an opaque bignum object or, if the lowest bit of the "pointer" is 1 (`lean_is_scalar`), an encoded unboxed natural number (`lean_box`/`lean_unbox`).
@@ -70,13 +70,13 @@ When including Lean code as part of a larger program, modules must be *initializ
Module initialization entails
* initialization of all "constants" (nullary functions), including closed terms lifted out of other functions
* execution of all `[init]` functions
* execution of all `[builtin_init]` functions, if the `builtin` parameter of the module initializer has been set
* execution of all `[builtinInit]` functions, if the `builtin` parameter of the module initializer has been set
The module initializer is automatically run with the `builtin` flag for executables compiled from Lean code and for "plugins" loaded with `lean --plugin`.
For all other modules imported by `lean`, the initializer is run without `builtin`.
Thus `[init]` functions are run iff their module is imported, regardless of whether they have native code available or not, while `[builtin_init]` functions are only run for native executable or plugins, regardless of whether their module is imported or not.
Thus `[init]` functions are run iff their module is imported, regardless of whether they have native code available or not, while `[builtinInit]` functions are only run for native executable or plugins, regardless of whether their module is imported or not.
`lean` uses built-in initializers for e.g. registering basic parsers that should be available even without importing their module (which is necessary for bootstrapping).
The initializer for module `A.B` is called `initialize_A_B` and will automatically initialize any imported modules.
Module initializers are idempotent (when run with the same `builtin` flag), but not thread-safe.
Together with initialization of the Lean runtime, you should execute code like the following exactly once before accessing any Lean declarations:

View File

@@ -1,7 +1,7 @@
# Documentation
The Lean `doc` folder contains the [Lean Manual](https://leanprover.github.io/lean4/doc/) and is
authored in a combination of markdown (`*.md`) files and literate Lean files. The .lean files are
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.
@@ -53,28 +53,25 @@ Then run the following:
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. Clone https://github.com/leanprover/LeanInk.git and run `lake build` then copy the resulting
executable to your `$HOME/.elan/bin` folder or `%USERPROFILE%\.elan\bin` so Alectryon can find it
there.
1. Create a Python 3.10 environment.
1. Install Alectryon:
1. Install the following packages:
```
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:
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
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.
And 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:
```

View File

@@ -1,22 +0,0 @@
/- "Hello world" -/
#eval "hello" ++ " " ++ "world"
-- "hello world"
#check true
-- Bool
def x := 10
#eval x + 2
-- 12
def double (x : Int) := 2*x
#eval double 3
-- 6
#check double
-- Int → Int
example : double 4 = 8 := rfl

View File

@@ -1,19 +0,0 @@
/- Dependent pattern matching -/
inductive Vector (α : Type u) : Nat Type u
| nil : Vector α 0
| cons : α Vector α n Vector α (n+1)
infix:67 "::" => Vector.cons
def Vector.zip : Vector α n Vector β n Vector (α × β) n
| nil, nil => nil
| a::as, b::bs => (a, b) :: zip as bs
#print Vector.zip
/-
def Vector.zip.{u_1, u_2} : {α : Type u_1} → {n : Nat} → {β : Type u_2} → Vector α n → Vector β n → Vector (α × β) n :=
fun {α} {n} {β} x x_1 =>
Vector.brecOn (motive := fun {n} x => {β : Type u_2} → Vector β n → Vector (α × β) n) x
...
-/

View File

@@ -1,22 +0,0 @@
/- Structures -/
structure Point where
x : Int := 0
y : Int := 0
deriving Repr
#eval Point.x (Point.mk 10 20)
-- 10
#eval { x := 10, y := 20 : Point }
def p : Point := { y := 20 }
#eval p.x
#eval p.y
#eval { p with x := 5 }
-- { x := 5, y := 20 }
structure Point3D extends Point where
z : Int

View File

@@ -1,28 +0,0 @@
/- Type classes -/
namespace Example
class ToString (α : Type u) where
toString : α String
#check @ToString.toString
-- {α : Type u_1} → [self : ToString α] → α → String
instance : ToString String where
toString s := s
instance : ToString Bool where
toString b := if b then "true" else "false"
#eval ToString.toString "hello"
export ToString (toString)
#eval toString true
-- "true"
-- #eval toString (true, "hello") -- Error
instance [ToString α] [ToString β] : ToString (α × β) where
toString p := "(" ++ toString p.1 ++ ", " ++ toString p.2 ++ ")"
#eval toString (true, "hello")
-- "(true, hello)"
end Example

View File

@@ -1,44 +0,0 @@
/- Type classes are heavily used in Lean -/
namespace Example
class Mul (α : Type u) where
mul : α α α
infixl:70 " * " => Mul.mul
def double [Mul α] (a : α) := a * a
class Semigroup (α : Type u) extends Mul α where
mul_assoc : a b c : α, (a * b) * c = a * (b * c)
instance : Semigroup Nat where
mul := Nat.mul
mul_assoc := Nat.mul_assoc
#eval double 5
class Functor (f : Type u Type v) : Type (max (u+1) v) where
map : (α β) f α f β
infixr:100 " <$> " => Functor.map
class LawfulFunctor (f : Type u Type v) [Functor f] : Prop where
id_map (x : f α) : id <$> x = x
comp_map (g : α β) (h : β γ) (x : f α) :(h g) <$> x = h <$> g <$> x
end Example
/-
`Deriving instances automatically`
We have seen `deriving Repr` in a few examples.
It is an instance generator.
Lean comes equipped with generators for the following classes.
`Repr`, `Inhabited`, `BEq`, `DecidableEq`,
`Hashable`, `Ord`, `FromToJson`, `SizeOf`
-/
inductive Tree (α : Type u) where
| leaf (val : α)
| node (left right : Tree α)
deriving DecidableEq, Ord, Inhabited, Repr

View File

@@ -1,31 +0,0 @@
/- Tactics -/
example : p q p q p := by
intro hp hq
apply And.intro
exact hp
apply And.intro
exact hq
exact hp
example : p q p q p := by
intro hp hq; apply And.intro hp; exact And.intro hq hp
/- Structuring proofs -/
example : p q p q p := by
intro hp hq
apply And.intro
case left => exact hp
case right =>
apply And.intro
case left => exact hq
case right => exact hp
example : p q p q p := by
intro hp hq
apply And.intro
. exact hp
. apply And.intro
. exact hq
. exact hp

View File

@@ -1,19 +0,0 @@
/- intro tactic variants -/
example (p q : α Prop) : ( x, p x q x) x, q x p x := by
intro h
match h with
| Exists.intro w (And.intro hp hq) => exact Exists.intro w (And.intro hq hp)
example (p q : α Prop) : ( x, p x q x) x, q x p x := by
intro (Exists.intro _ (And.intro hp hq))
exact Exists.intro _ (And.intro hq hp)
example (p q : α Prop) : ( x, p x q x) x, q x p x := by
intro _, hp, hq
exact _, hq, hp
example (α : Type) (p q : α Prop) : ( x, p x q x) x, q x p x := by
intro
| _, .inl h => exact _, .inr h
| _, .inr h => exact _, .inl h

View File

@@ -1,12 +0,0 @@
/- Inaccessible names -/
example : x y : Nat, x = y y = x := by
intros
apply Eq.symm
assumption
example : x y : Nat, x = y y = x := by
intros
apply Eq.symm
rename_i a b hab
exact hab

View File

@@ -1,19 +0,0 @@
/- More tactics -/
example (p q : Nat Prop) : ( x, p x q x) x, q x p x := by
intro h
cases h with
| intro x hpq =>
cases hpq with
| intro hp hq =>
exists x
example : p q q p := by
intro p
cases p
constructor <;> assumption
example : p ¬ p q := by
intro h
cases h
contradiction

View File

@@ -1,20 +0,0 @@
/- Structuring proofs (cont.) -/
example : p (q r) (p q) (p r) := by
intro h
have hp : p := h.left
have hqr : q r := h.right
show (p q) (p r)
cases hqr with
| inl hq => exact Or.inl hp, hq
| inr hr => exact Or.inr hp, hr
example : p (q r) (p q) (p r) := by
intro hp, hqr
cases hqr with
| inl hq =>
have := And.intro hp hq
apply Or.inl; exact this
| inr hr =>
have := And.intro hp hr
apply Or.inr; exact this

View File

@@ -1,10 +0,0 @@
/- Tactic combinators -/
example : p q r p ((p q) r) (q r p) := by
intros
repeat (any_goals constructor)
all_goals assumption
example : p q r p ((p q) r) (q r p) := by
intros
repeat (any_goals (first | assumption | constructor))

View File

@@ -1,14 +0,0 @@
/- First-class functions -/
def twice (f : Nat Nat) (a : Nat) :=
f (f a)
#check twice
-- (Nat → Nat) → Nat → Nat
#eval twice (fun x => x + 2) 10
theorem twice_add_2 (a : Nat) : twice (fun x => x + 2) a = a + 4 := rfl
-- `(· + 2)` is syntax sugar for `(fun x => x + 2)`.
#eval twice (· + 2) 10

View File

@@ -1,22 +0,0 @@
/- Rewriting -/
example (f : Nat Nat) (k : Nat) (h₁ : f 0 = 0) (h₂ : k = 0) : f k = 0 := by
rw [h₂] -- replace k with 0
rw [h₁] -- replace f 0 with 0
example (f : Nat Nat) (k : Nat) (h₁ : f 0 = 0) (h₂ : k = 0) : f k = 0 := by
rw [h₂, h₁]
example (f : Nat Nat) (a b : Nat) (h₁ : a = b) (h₂ : f a = 0) : f b = 0 := by
rw [ h₁, h₂]
example (f : Nat Nat) (a : Nat) (h : 0 + a = 0) : f a = f 0 := by
rw [Nat.zero_add] at h
rw [h]
def Tuple (α : Type) (n : Nat) :=
{ as : List α // as.length = n }
example (n : Nat) (h : n = 0) (t : Tuple α n) : Tuple α 0 := by
rw [h] at t
exact t

View File

@@ -1,21 +0,0 @@
/- Simplifier -/
example (p : Nat Prop) : (x + 0) * (0 + y * 1 + z * 0) = x * y := by
simp
example (p : Nat Prop) (h : p (x * y)) : p ((x + 0) * (0 + y * 1 + z * 0)) := by
simp; assumption
example (p : Nat Prop) (h : p ((x + 0) * (0 + y * 1 + z * 0))) : p (x * y) := by
simp at h; assumption
def f (m n : Nat) : Nat :=
m + n + m
example (h : n = 1) (h' : 0 = m) : (f m n) = n := by
simp [h, h', f]
example (p : Nat Prop) (h₁ : x + 0 = x') (h₂ : y + 0 = y')
: x + y + 0 = x' + y' := by
simp at *
simp [*]

View File

@@ -1,13 +0,0 @@
/- Simplifier -/
def mk_symm (xs : List α) :=
xs ++ xs.reverse
@[simp] theorem reverse_mk_symm : (mk_symm xs).reverse = mk_symm xs := by
simp [mk_symm]
theorem tst : (xs ++ mk_symm ys).reverse = mk_symm ys ++ xs.reverse := by
simp
#print tst
-- Lean reverse_mk_symm, and List.reverse_append

View File

@@ -1,26 +0,0 @@
/- split tactic -/
def f (x y z : Nat) : Nat :=
match x, y, z with
| 5, _, _ => y
| _, 5, _ => y
| _, _, 5 => y
| _, _, _ => 1
example : x 5 y 5 z 5 z = w f x y w = 1 := by
intros
simp [f]
split
. contradiction
. contradiction
. contradiction
. rfl
def g (xs ys : List Nat) : Nat :=
match xs, ys with
| [a, b], _ => a+b+1
| _, [b, c] => b+1
| _, _ => 1
example (xs ys : List Nat) (h : g xs ys = 0) : False := by
unfold g at h; split at h <;> simp_arith at h

View File

@@ -1,9 +0,0 @@
/- induction tactic -/
example (as : List α) (a : α) : (as.concat a).length = as.length + 1 := by
induction as with
| nil => rfl
| cons _ xs ih => simp [List.concat, ih]
example (as : List α) (a : α) : (as.concat a).length = as.length + 1 := by
induction as <;> simp! [*]

View File

@@ -1,59 +0,0 @@
/- Enumerated types -/
inductive Weekday where
| sunday | monday | tuesday | wednesday
| thursday | friday | saturday
#check Weekday.sunday
-- Weekday
open Weekday
#check sunday
def natOfWeekday (d : Weekday) : Nat :=
match d with
| sunday => 1
| monday => 2
| tuesday => 3
| wednesday => 4
| thursday => 5
| friday => 6
| saturday => 7
def Weekday.next (d : Weekday) : Weekday :=
match d with
| sunday => monday
| monday => tuesday
| tuesday => wednesday
| wednesday => thursday
| thursday => friday
| friday => saturday
| saturday => sunday
def Weekday.previous : Weekday Weekday
| sunday => saturday
| monday => sunday
| tuesday => monday
| wednesday => tuesday
| thursday => wednesday
| friday => thursday
| saturday => friday
/- Proving theorems using tactics -/
theorem Weekday.next_previous (d : Weekday) : d.next.previous = d :=
match d with
| sunday => rfl
| monday => rfl
| tuesday => rfl
| wednesday => rfl
| thursday => rfl
| friday => rfl
| saturday => rfl
theorem Weekday.next_previous' (d : Weekday) : d.next.previous = d := by -- switch to tactic mode
cases d -- Creates 7 goals
rfl; rfl; rfl; rfl; rfl; rfl; rfl
theorem Weekday.next_previous'' (d : Weekday) : d.next.previous = d := by
cases d <;> rfl

View File

@@ -1,20 +0,0 @@
/- What is the type of Nat? -/
#check 0
-- Nat
#check Nat
-- Type
#check Type
-- Type 1
#check Type 1
-- Type 2
#check Eq.refl 2
-- 2 = 2
#check 2 = 2
-- Prop
#check Prop
-- Type
example : Prop = Sort 0 := rfl
example : Type = Sort 1 := rfl
example : Type 1 = Sort 2 := rfl

View File

@@ -1,21 +0,0 @@
/- Implicit arguments and universe polymorphism -/
def f (α β : Sort u) (a : α) (b : β) : α := a
#eval f Nat String 1 "hello"
-- 1
def g {α β : Sort u} (a : α) (b : β) : α := a
#eval g 1 "hello"
def h (a : α) (b : β) : α := a
#check g
-- ?m.1 → ?m.2 → ?m.1
#check @g
-- {α β : Sort u} → α → β → α
#check @h
-- {α : Sort u_1} → {β : Sort u_2} → α → β → α
#check g (α := Nat) (β := String)
-- Nat → String → Nat

View File

@@ -1,14 +0,0 @@
/- Inductive Types -/
inductive Tree (β : Type v) where
| leaf
| node (left : Tree β) (key : Nat) (value : β) (right : Tree β)
deriving Repr
#eval Tree.node .leaf 10 true .leaf
-- Tree.node Tree.leaf 10 true Tree.leaf
inductive Vector (α : Type u) : Nat Type u
| nil : Vector α 0
| cons : α Vector α n Vector α (n+1)

View File

@@ -1,25 +0,0 @@
/- Recursive functions -/
#print Nat -- Nat is an inductive datatype
def fib (n : Nat) : Nat :=
match n with
| 0 => 1
| 1 => 1
| n+2 => fib (n+1) + fib n
example : fib 5 = 8 := rfl
example : fib (n+2) = fib (n+1) + fib n := rfl
#print fib
/-
def fib : Nat → Nat :=
fun n =>
Nat.brecOn n fun n f =>
(match (motive := (n : Nat) → Nat.below n → Nat) n with
| 0 => fun x => 1
| 1 => fun x => 1
| Nat.succ (Nat.succ n) => fun x => x.fst.fst + x.fst.snd.fst.fst)
f
-/

View File

@@ -1,25 +0,0 @@
/- Well-founded recursion -/
def ack : Nat Nat Nat
| 0, y => y+1
| x+1, 0 => ack x 1
| x+1, y+1 => ack x (ack (x+1) y)
termination_by ack x y => (x, y)
def sum (a : Array Int) : Int :=
let rec go (i : Nat) :=
if i < a.size then
a[i] + go (i+1)
else
0
go 0
termination_by go i => a.size - i
set_option pp.proofs true
#print sum.go
/-
def sum.go : Array Int → Nat → Int :=
fun a =>
WellFounded.fix (sum.go.proof_1 a) fun i a_1 =>
if h : i < Array.size a then Array.getOp a i + a_1 (i + 1) (sum.go.proof_2 a i h) else 0
-/

View File

@@ -1,44 +0,0 @@
/- Mutual recursion -/
inductive Term where
| const : String Term
| app : String List Term Term
namespace Term
mutual
def numConsts : Term Nat
| const _ => 1
| app _ cs => numConstsLst cs
def numConstsLst : List Term Nat
| [] => 0
| c :: cs => numConsts c + numConstsLst cs
end
mutual
def replaceConst (a b : String) : Term Term
| const c => if a = c then const b else const c
| app f cs => app f (replaceConstLst a b cs)
def replaceConstLst (a b : String) : List Term List Term
| [] => []
| c :: cs => replaceConst a b c :: replaceConstLst a b cs
end
/- Mutual recursion in theorems -/
mutual
theorem numConsts_replaceConst (a b : String) (e : Term)
: numConsts (replaceConst a b e) = numConsts e := by
match e with
| const c => simp [replaceConst]; split <;> simp [numConsts]
| app f cs => simp [replaceConst, numConsts, numConsts_replaceConstLst a b cs]
theorem numConsts_replaceConstLst (a b : String) (es : List Term)
: numConstsLst (replaceConstLst a b es) = numConstsLst es := by
match es with
| [] => simp [replaceConstLst, numConstsLst]
| c :: cs =>
simp [replaceConstLst, numConstsLst, numConsts_replaceConst a b c,
numConsts_replaceConstLst a b cs]
end

View File

@@ -176,8 +176,7 @@ The modifier `local` specifies the scope of the macro.
/-- The `have_eq lhs rhs` tactic (tries to) prove that `lhs = rhs`,
and then replaces `lhs` with `rhs`. -/
local macro "have_eq " lhs:term:max rhs:term:max : tactic =>
`(tactic|
(have h : $lhs = $rhs :=
`((have h : $lhs = $rhs :=
-- TODO: replace with linarith
by simp_arith at *; apply Nat.le_antisymm <;> assumption
try subst $lhs))
@@ -192,7 +191,7 @@ useful if `e` is the condition of an `if`-statement.
-/
/-- `by_cases' e` is a shorthand form `by_cases e <;> simp[*]` -/
local macro "by_cases' " e:term : tactic =>
`(tactic| by_cases $e <;> simp [*])
`(by_cases $e <;> simp [*])
/-!

View File

@@ -106,7 +106,7 @@ more information for us, in the form of a `snap : Snapshot`. With this in hand,
-/
open Server RequestM in
@[server_rpc_method]
@[serverRpcMethod]
def getType (params : GetTypeParams) : RequestM (RequestTask CodeWithInfos) :=
withWaitFindSnapAtPos params.pos fun snap => do
runTermElabM snap do
@@ -126,10 +126,9 @@ as seen in the goal view. We will use it to implement our custom `#check` displa
⚠️ WARNING: Like the other widget APIs, the infoview JS API is **unstable** and subject to breaking changes.
The code below demonstrates useful parts of the API. To make RPC method calls, we use the `RpcContext`.
The `useAsync` helper packs the results of a call into an `AsyncState` structure which indicates
whether the call has resolved successfully, has returned an error, or is still in-flight. Based
on this we either display an `InteractiveCode` with the type, `mapRpcError` the error in order
to turn it into a readable message, or show a `Loading..` message, respectively.
The `useAsync` helper packs the results of a call into a `status` enum, the returned `val`ue in case
the call was successful, and otherwise an `err`or. Based on the `status` we either display
an `InteractiveCode`, or `mapRpcError` the error in order to turn it into a readable message.
-/
@[widget]
@@ -144,15 +143,18 @@ export default function(props) {
const rs = React.useContext(RpcContext)
const [name, setName] = React.useState('getType')
const st = useAsync(() =>
const [status, val, err] = useAsync(() =>
rs.call('getType', { name, pos: props.pos }), [name, rs, props.pos])
const type = st.state === 'resolved' ? st.value && e(InteractiveCode, {fmt: st.value})
: st.state === 'rejected' ? e('p', null, mapRpcError(st.error).message)
: e('p', null, 'Loading..')
const type = status === 'fulfilled' ? val && e(InteractiveCode, {fmt: val})
: status === 'rejected' ? e('p', null, mapRpcError(err).message)
: e('p', null, 'Loading..')
const onChange = (event) => { setName(event.target.value) }
return e('div', null,
e('input', { value: name, onChange }), ' : ', type)
e('input', { value: name, onChange }),
' : ',
type)
}
"
@@ -181,36 +183,4 @@ the infoview we need to:
In the RubiksCube sample, we provide a working `rollup.js` build configuration in
[rollup.config.js](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/widget/rollup.config.js).
## Inserting text
We can also instruct the editor to insert text, copy text to the clipboard, or
reveal a certain location in the document.
To do this, use the `React.useContext(EditorContext)` React context.
This will return an `EditorConnection` whose `api` field contains a number of methods to
interact with the text editor.
You can see the full API for this [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52)
-/
@[widget]
def insertTextWidget : UserWidgetDefinition where
name := "textInserter"
javascript := "
import * as React from 'react';
const e = React.createElement;
import { EditorContext } from '@leanprover/infoview';
export default function(props) {
const editorConnection = React.useContext(EditorContext)
function onClick() {
editorConnection.api.insertText('-- hello!!!', 'above')
}
return e('div', null, e('button', { value: name, onClick }, 'insert'))
}
"
/-! Finally, we can try this out: -/
#widget insertTextWidget .null

View File

@@ -406,7 +406,7 @@ The reduction relation is transitive, which is to say, is ``s`` reduces to ``s'`
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.
Definitional equality is a strong notion of equalty 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.

68
doc/flake.lock generated
View File

@@ -19,11 +19,11 @@
},
"flake-utils": {
"locked": {
"lastModified": 1656928814,
"narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
"lastModified": 1644229661,
"narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
"rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
"type": "github"
},
"original": {
@@ -35,13 +35,14 @@
"lean": {
"inputs": {
"flake-utils": "flake-utils",
"lean-stage0": "lean-stage0",
"lean4-mode": "lean4-mode",
"nix": "nix",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 0,
"narHash": "sha256-YnYbmG0oou1Q/GE4JbMNb8/yqUVXBPIvcdQQJHBqtPk=",
"narHash": "sha256-AfBkKX6Ahb9YbZke+eWLmsUk1Z9BwdJ1CpIoPY8Msx8=",
"path": "../.",
"type": "path"
},
@@ -50,14 +51,29 @@
"type": "path"
}
},
"lean-stage0": {
"locked": {
"lastModified": 0,
"narHash": "sha256-3K/43lSW4WIHNG+HHVKCD1odS63mHuaQ4ueHyTIkcls=",
"owner": "leanprover",
"repo": "lean4",
"rev": "0000000000000000000000000000000000000000",
"type": "github"
},
"original": {
"owner": "leanprover",
"repo": "lean4",
"type": "github"
}
},
"lean4-mode": {
"flake": false,
"locked": {
"lastModified": 1659020985,
"narHash": "sha256-+dRaXB7uvN/weSZiKcfSKWhcdJVNg9Vg8k0pJkDNjpc=",
"lastModified": 1647694750,
"narHash": "sha256-0rV61KhevG9IAjZDN2Ts2VS65fiUAPAezbf282u7yy8=",
"owner": "leanprover",
"repo": "lean4-mode",
"rev": "37d5c99b7b29c80ab78321edd6773200deb0bca6",
"rev": "c016c7aeee92564836355083664c49ed57024427",
"type": "github"
},
"original": {
@@ -69,11 +85,11 @@
"leanInk": {
"flake": false,
"locked": {
"lastModified": 1666154782,
"narHash": "sha256-0ELqEca6jZT4BW/mqkDD+uYuxW5QlZUFlNwZkvugsg8=",
"owner": "digama0",
"lastModified": 1656863690,
"narHash": "sha256-9tmynTTeJGhYZaltS4xhSJgLTpe7Ta1ofV6U1SA/5V4=",
"owner": "leanprover",
"repo": "LeanInk",
"rev": "12a2aec9b5f4aa84e84fb01a9af1da00d8aaff4e",
"rev": "4b5e606ea8cc54c2447ce48706f8ec1d133d19e9",
"type": "github"
},
"original": {
@@ -121,11 +137,11 @@
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1657097207,
"narHash": "sha256-SmeGmjWM3fEed3kQjqIAO8VpGmkC2sL1aPE7kKpK650=",
"lastModified": 1648022028,
"narHash": "sha256-HtwmifW6STPcym+3uJ4YavgTKTYVIoiQHg3f0wXOm+Q=",
"owner": "NixOS",
"repo": "nix",
"rev": "f6316b49a0c37172bca87ede6ea8144d7d89832f",
"rev": "98ce1a21b7d959c5575fac566c8699e91703a9f7",
"type": "github"
},
"original": {
@@ -136,18 +152,17 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1653988320,
"narHash": "sha256-ZaqFFsSDipZ6KVqriwM34T739+KLYJvNmCWzErjAg7c=",
"lastModified": 1632864508,
"narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2fa57ed190fd6c7c746319444f34b5917666e5c1",
"rev": "82891b5e2c2359d7e58d08849e4c89511ab94234",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05-small",
"repo": "nixpkgs",
"type": "github"
"id": "nixpkgs",
"ref": "nixos-21.05-small",
"type": "indirect"
}
},
"nixpkgs-regression": {
@@ -160,19 +175,18 @@
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"id": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1657208011,
"narHash": "sha256-BlIFwopAykvdy1DYayEkj6ZZdkn+cVgPNX98QVLc0jM=",
"lastModified": 1648219316,
"narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2770cc0b1e8faa0e20eb2c6aea64c256a706d4f2",
"rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634",
"type": "github"
},
"original": {

View File

@@ -4,15 +4,15 @@
inputs.lean.url = path:../.;
inputs.flake-utils.follows = "lean/flake-utils";
inputs.mdBook = {
url = "github:leanprover/mdBook";
url = github:leanprover/mdBook;
flake = false;
};
inputs.alectryon = {
url = "github:Kha/alectryon/typeid";
url = github:Kha/alectryon/typeid;
flake = false;
};
inputs.leanInk = {
url = "github:leanprover/LeanInk";
url = github:leanprover/LeanInk;
flake = false;
};
@@ -78,30 +78,22 @@
(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"; }
{ mod = "monads"; glob = "submodules"; }
];
};
inked = renderPackage literate;
renderLean = name: file: runCommandNoCC "${name}.md" { buildInputs = [ alectryon ]; } ''
mkdir -p $(basename $out/${name})
alectryon --frontend lean4+markup ${file} --backend webpage -o $out/${name}.md
'';
listFilesRecursiveRel = root: dir: lib.flatten (lib.mapAttrsToList (name: type:
if type == "directory" then
listFilesRecursiveRel root ("${dir}/${name}")
else
dir + "/${name}"
) (builtins.readDir "${root}/${dir}"));
renderDir = dir: let
inputs = builtins.filter (n: builtins.match ".*\.lean" n != null) (listFilesRecursiveRel dir ".");
outputs = lib.genAttrs inputs (n: renderLean n "${dir}/${n}");
in
outputs // symlinkJoin { inherit name; paths = lib.attrValues outputs; };
inked = renderDir ./.;
doc = book;
};
defaultPackage = self.packages.${system}.doc;

View File

@@ -1143,7 +1143,7 @@ hljs.registerLanguage("lean", function(hljs) {
'sorry admit',
};
var LEAN_IDENT_RE = /[A-Za-z_][\\w\u207F-\u209C\u1D62-\u1D6A\u2079\'0-9?]*/;
var LEAN_IDENT_RE = /[A-Za-z_][\\w\u207F-\u209C\u1D62-\u1D6A\u2079\'0-9]*/;
var DASH_COMMENT = hljs.COMMENT('--', '$');
var MULTI_LINE_COMMENT = hljs.COMMENT('/-[^-]', '-/');
@@ -1167,7 +1167,7 @@ hljs.registerLanguage("lean", function(hljs) {
var LEAN_DEFINITION = {
className: 'theorem',
begin: '\\b(def|theorem|lemma|class|structure|(?<!deriving\\s+)instance)\\b',
beginKeywords: 'def theorem lemma class instance structure',
end: ':= | where',
excludeEnd: true,
contains: [

View File

@@ -201,7 +201,6 @@ literate=
{}{{\ensuremath{_n}}}1
{}{{\ensuremath{_m}}}1
{}{{\ensuremath{_p}}}1
{}{{\ensuremath{\uparrow}}}1
{}{{\ensuremath{\downarrow}}}1

View File

@@ -42,7 +42,7 @@ def Sum.str : Option Nat → String :=
## 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,
It is often used when we 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.
@@ -85,7 +85,7 @@ def id5 : {α : Type} → αα :=
## 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:
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
@@ -196,8 +196,6 @@ 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,
@@ -289,11 +287,11 @@ Lean execution runtime. For example, we cannot prove in Lean that arrays have a
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
unsafe code. Thus, Lean 4 provides the attribute `@[implementedBy 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,
at the attribute `@[implementedBy 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
cannot be compromised by using the attribute `implementedBy`, 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`
@@ -304,7 +302,7 @@ unsafe
def withPtrUnsafe {α β : Type} (a : α) (k : USize β) (h : u, k u = k 0) : β :=
k (ptrAddrUnsafe a)
@[implemented_by withPtrUnsafe]
@[implementedBy withPtrUnsafe]
def withPtr {α β : Type} (a : α) (k : USize β) (h : u, k u = k 0) : β :=
k 0
```
@@ -342,7 +340,8 @@ partial def f (x : Nat) : IO Unit := do
These are changes to the library which may trip up Lean 3 users:
- `List` is no longer a monad.
- `Option` and `List` are no longer monads. Instead there is `OptionM`. This was done to avoid some performance traps. For example `o₁ <|> o₂` where `o₁ o₂ : Option α` will evaluate both `o₁` and `o₂` even if `o₁` evaluates to `some x`. This can be a problem if `o₂` requires a lot of compute to evaluate. A zulip discussion on this design choice is [here](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Option.20do.20notation.20regression.3F).
## Style changes

View File

@@ -1,7 +1,7 @@
# Macro Overview
The official paper describing the mechanics behind Lean 4's macro system can be
The offical paper describing the mechanics behind Lean 4's macro system can be
found in [Beyond Notations: Hygienic Macro Expansion for Theorem Proving
Languages](https://arxiv.org/abs/2001.10490) by Sebastian Ullrich and Leonardo
de Moura, and the accompanying repo with example code can be found in the
@@ -68,7 +68,7 @@ carries state needed for macro expansion to work nicely, including the info
needed to implement hygiene.
As an example, we again refer to Mathlib's set builder notation:
```lean
```
/- Declares a parser -/
syntax (priority := high) "{" term,+ "}" : term
@@ -95,8 +95,7 @@ simplified representation which omits details in the `atom` and `ident`
constructors; users can create atoms and idents which comport with this
simplified representation using the `mkAtom` and `mkIdent` methods provided in
the `Lean` namespace.
```lean
# open Lean
```
inductive Syntax where
| missing : Syntax
| node (kind : SyntaxNodeKind) (args : Array Syntax) : Syntax
@@ -107,14 +106,12 @@ inductive Syntax where
For those interested, `MacroM` is a `ReaderT`:
```lean
# open Lean
```
abbrev MacroM := ReaderT Macro.Context (EStateM Macro.Exception Macro.State)
```
The other relevant components are defined as follows:
```lean
# open Lean
```
structure Context where
methods : MethodsRef
mainModule : Name
@@ -151,7 +148,7 @@ or mathlib4's `binderterm`. These are the different categories of things that
can be referred to in a quote/antiquote. `declare_syntax_cat` results in a call
to `registerParserCategory` and produces a new parser descriptor:
```lean
```
set_option trace.Elab.definition true in
declare_syntax_cat binderterm
@@ -181,8 +178,7 @@ macro/pattern language by way of the `syntax` keyword. This is the recommended
means of writing parsers. As an example, the parser for the `rwa` (rewrite, then
use assumption) tactic is:
```lean
# open Lean.Parser.Tactic
```
set_option trace.Elab.definition true in
syntax "rwa " rwRuleSeq (location)? : tactic
@@ -211,17 +207,15 @@ mark, which is not what we want.
The name `tacticRwa__` is automatically generated. You can name parser
descriptors declared with the `syntax` keyword like so:
```lean
```
set_option trace.Elab.definition true in
syntax (name := introv) "introv " (colGt ident)* : tactic
/-
[Elab.definition.body] introv : Lean.ParserDescr :=
Lean.ParserDescr.node `introv 1022
(Lean.ParserDescr.binary `andthen (Lean.ParserDescr.nonReservedSymbol "introv " false)
(Lean.ParserDescr.unary `many
(Lean.ParserDescr.binary `andthen (Lean.ParserDescr.const `colGt) (Lean.ParserDescr.const `ident))))
-/
```
## The pattern language
@@ -257,7 +251,7 @@ pretty printed output.
## Syntax expansions with `macro_rules`, and how it desugars.
`macro_rules` lets you declare expansions for a given `Syntax` element using a
syntax similar to a `match` statement. The left-hand side of a match arm is a
syntax simlar to a `match` statement. The left-hand side of a match arm is a
quotation (with a leading `<cat>|` for categories other than `term` and
`command`) in which users can specify the pattern they'd like to write an
expansion for. The right-hand side returns a syntax quotation which is the
@@ -274,7 +268,7 @@ declared with `macro_rules`. This `transitivity` tactic is implemented such that
it will work for either Nat.le or Nat.lt. The Nat.lt version was declared "most
recently", so it will be tried first, but if it fails (for example, if the
actual term in question is Nat.le) the next potential expansion will be tried:
```lean
```
macro "transitivity" e:(colGt term) : tactic => `(tactic| apply Nat.le_trans (m := $e))
macro_rules
| `(tactic| transitivity $e) => `(tactic| apply Nat.lt_trans (m := $e))
@@ -289,20 +283,18 @@ example (a b c : Nat) (h0 : a <= b) (h1 : b <= c) : a <= c := by
/- This will fail, but is interesting in that it exposes the "most-recent first" behavior, since the
error message complains about being unable to unify mvar1 <= mvar2, rather than mvar1 < mvar2. -/
/-
example (a b c : Nat) (h0 : a <= b) (h1 : b <= c) : False := by
transitivity b <;>
assumption
-/
```
To see the desugared definition of the actual expansion, we can again use
`set_option trace.Elab.definition true in` and observe the output of the humble
`exfalso` tactic defined in Mathlib4:
```lean
```
set_option trace.Elab.definition true in
macro "exfalso" : tactic => `(tactic| apply False.elim)
macro "exfalso" : tactic => `(apply False.elim)
/-
Results in the expansion:
@@ -336,8 +328,7 @@ fun x =>
We can also create the syntax transformer declaration ourselves instead of using
`macro_rules`. We'll need to name our parser and use the attribute `@[macro
myExFalsoParser]` to associate our declaration with the parser:
```lean
# open Lean
```
syntax (name := myExfalsoParser) "myExfalso" : tactic
-- remember that `Macro` is a synonym for `Syntax -> TacticM Unit`
@@ -352,12 +343,12 @@ example (p : Prop) (h : p) (f : p -> False) : 3 = 2 := by
In the above example, we're still using the sugar Lean provides for creating
quotations, as it feels more intuitive and saves us some work. It is possible to
forego the sugar altogether:
```lean
```
syntax (name := myExfalsoParser) "myExfalso" : tactic
@[macro myExfalsoParser] def implMyExfalso : Lean.Macro :=
fun stx => pure (Lean.mkNode `Lean.Parser.Tactic.apply
#[Lean.mkAtomFrom stx "apply", Lean.mkCIdentFrom stx ``False.elim])
fun stx => Lean.mkNode `Lean.Parser.Tactic.apply
#[Lean.mkAtomFrom stx "apply", Lean.mkCIdentFrom stx ``False.elim]
example (p : Prop) (h : p) (f : p -> False) : 3 = 2 := by
myExfalso

View File

@@ -83,7 +83,7 @@ Work on two adjacent stages at the same time without the need for repeatedly upd
```bash
# open an editor that will use only committed changes (so first commit them when changing files)
nix run .#HEAD-as-stage1.emacs-dev&
# open a second editor that will use those committed changes as stage 0
# open a second editor that will use those commited changes as stage 0
# (so don't commit changes done here until you are done and ran a final `update-stage0-commit`)
nix run .#HEAD-as-stage0.emacs-dev&
```

View File

@@ -8,7 +8,7 @@ Let's see how they work!
## What is an Applicative Functor?
An applicative functor defines a default or "base" construction for an object and allows
An applicative functor is an defines a default or "base" construction for an object and allows
function application to be chained across multiple instances of the structure. All applicative
functors are functors, meaning they must also support the "map" operation.
@@ -29,30 +29,24 @@ simply from outside the structure, as was the case with `Functor.map`.
Applicative in Lean is built on some helper type classes, `Functor`, `Pure` and `Seq`:
-/
namespace hidden -- hidden
```lean,ignore
class Applicative (f : Type u → Type v) extends Functor f, Pure f, Seq f, SeqLeft f, SeqRight f where
map := fun x y => Seq.seq (pure x) fun _ => y
seqLeft := fun a b => Seq.seq (Functor.map (Function.const _) a) b
seqRight := fun a b => Seq.seq (Functor.map (Function.const _ id) a) b
end hidden -- hidden
/-!
```
Notice that as with `Functor` it is also a type transformer `(f : Type u → Type v)` and notice the
`extends Functor f` is ensuring the base `Functor` also performs that same type transformation.
`extends Functor f` is ensuring the base Functor also performs that same type transformation.
As stated above, all applicatives are then functors. This means you can assume that `map` already
exists for all these types.
The `Pure` base type class is a very simple type class that supplies the `pure` function.
-/
namespace hidden -- hidden
```lean,ignore
class Pure (f : Type u → Type v) where
pure {α : Type u} : α → f α
end hidden -- hidden
/-!
```
You can think of it as lifting the result of a pure value to some monadic type. The simplest example
You can think of it as lifing the result of a pure value to some monadic type. The simplest example
of `pure` is the `Option` type:
-/
@@ -71,12 +65,10 @@ instance : Monad Option where
The `Seq` type class is also a simple type class that provides the `seq` operator which can
also be written using the special syntax `<*>`.
-/
namespace hidden -- hidden
```lean,ignore
class Seq (f : Type u → Type v) : Type (max (u+1) v) where
seq : {α β : Type u} → f (α → β) → (Unit → f α) → f β
end hidden -- hidden
/-!
```
## Basic Applicative Examples
@@ -212,7 +204,7 @@ so you get a nice zipped list like this:
-- [(1, 4), (2, 5), (3, 6)]
/-!
And of course, as you would expect, there is an `unzip` also:
And of couse, as you would expect, there is an `unzip` also:
-/
#eval List.unzip (List.zip [1, 2, 3] [4, 5, 6])
@@ -256,7 +248,7 @@ Applicative functor.
You may remember seeing the `SeqLeft` and `SeqRight` base types on `class Applicative` earlier.
These provide the `seqLeft` and `seqRight` operations which also have some handy notation
shorthands `<*` and `*>` respectively. Where: `x <* y` evaluates `x`, then `y`, and returns the
shorthands `<*` and `*>` repsectively. Where: `x <* y` evaluates `x`, then `y`, and returns the
result of `x` and `x *> y` evaluates `x`, then `y`, and returns the result of `y`.
To make it easier to remember, notice that it returns that value that the `<*` or `*>` notation is
@@ -286,13 +278,13 @@ But you will need to understand full Monads before this will make sense.
Diving a bit deeper, (you can skip this and jump to the [Applicative
Laws](laws.lean.md#what-are-the-applicative-laws) if don't want to dive into this implementation detail right
now). But, if you write a simple `Option` example `(.*.) <$> some 4 <*> some 5` that produces `some 20`
using `Seq.seq` you will see something interesting:
using `Seq.seq` you will see somthing interesting:
-/
#eval Seq.seq ((.*.) <$> some 4) (fun (_ : Unit) => some 5) -- some 20
/-!
This may look a bit cumbersome, specifically, why did we need to invent this funny looking function
This may look a bit combersome, specifically, why did we need to invent this funny looking function
`fun (_ : Unit) => (some 5)`?
Well if you take a close look at the type class definition:

View File

@@ -48,7 +48,6 @@ And now you can run this test and get the expected exception:
#eval test -- Except.error "can't divide by zero"
/-!
## Chaining
Now as before you can build a chain of monadic actions that can be composed together using `bind (>>=)`:
@@ -71,17 +70,7 @@ def chainUsingDoNotation := do
/-!
Notice in the second `divide 6 0` the exception from that division was nicely propagated along
to the final result and the square function was ignored in that case. You can see why the
`square` function was ignored if you look at the implementation of `Except.bind`:
-/
def bind (ma : Except ε α) (f : α Except ε β) : Except ε β :=
match ma with
| Except.error err => Except.error err
| Except.ok v => f v
/-!
Specifically notice that it only calls the next function `f v` in the `Except.ok`, and
in the error case it simply passes the same error along.
to the final result and the square function was pretty much ignored in that case.
Remember also that you can chain the actions with implicit binding by using the `do` notation
as you see in the `chainUsingDoNotation` function above.
@@ -89,7 +78,7 @@ as you see in the `chainUsingDoNotation` function above.
## Try/Catch
Now with all good exception handling you also want to be able to catch exceptions so your program
can continue on or do some error recovery task, which you can do like this:
can try continue on or do some error recovery task, which you can do like this:
-/
def testCatch :=
try
@@ -139,8 +128,7 @@ def testUnwrap : String := Id.run do
The `Id.run` function is a helper function that executes the `do` block and returns the result where
`Id` is the _identity monad_. So `Id.run do` is a pattern you can use to execute monads in a
function that is not itself monadic. This works for all monads except `IO` which, as stated earlier,
you cannot invent out of thin air, you must use the `IO` monad given to your `main` function.
function that is not itself monadic.
## Monadic functions
@@ -173,6 +161,6 @@ def forM [Monad m] (as : List α) (f : α → m PUnit) : m PUnit :=
Now that you know all these different monad constructs, you might be wondering how you can combine
them. What if there was some part of your state that you wanted to be able to modify (using the
State monad), but you also needed exception handling. How can you get multiple monadic capabilities
in the same function. To learn the answer, head to [Monad Transformers](transformers.lean.md).
in the same fuunction. To learn the answer, head to [Monad Transformers](transformers.lean.md).
-/

View File

@@ -172,7 +172,7 @@ and now bringing it all together you can use the simple function `squareFeetToMe
Lean also defines custom infix operator `<$>` for `Functor.map` which allows you to write this:
-/
#eval (fun s => s.length) <$> ["elephant", "tiger", "giraffe"] -- [8, 5, 7]
#eval (fun s => s.length) <$> ["elephant", "tiger", "giraffe"]
#eval (fun x => x + 1) <$> (some 5) -- some 6
/-!
@@ -213,7 +213,7 @@ class Functor (f : Type u → Type v) : Type (max (u+1) v) where
Note that `mapConst` has a default implementation, namely:
`mapConst : {α β : Type u} → α → f β → f α := Function.comp map (Function.const _)` in the `Functor`
type class. So you can use this default implementation and you only need to replace it if
your functor has a more specialized variant than this (usually the custom version is more performant).
your Functors has a more specialized variant than this which is more performant.
In general then, a functor is a function on types `F : Type u → Type v` equipped with an operator
called `map` such that if you have a function `f` of type `α → β` then `map f` will convert your

View File

@@ -5,7 +5,7 @@ of [Category Theory](https://en.wikipedia.org/wiki/Monad_%28category_theory%29).
Monads in Lean are so similar to Haskell that this introduction to monads is heavily based on the
similar chapter of the [Monday Morning Haskell](https://mmhaskell.com/monads/). Many thanks to
the authors of that material for allowing us to reuse it here.
the authors of that material for allowing it to reused it here.
Monads build on the following fundamental type classes which you will need to understand
first before fully understanding monads. Shown in light blue are some concrete functors
@@ -29,10 +29,10 @@ still be summed up in a couple simple functions. Here you will learn how to cre
## [Monads Tutorial](monads.lean.md)
Now that you have an intuition for how abstract structures work, you'll examine some of the problems
that functors and applicative functors don't help you solve. Then you'll learn the specifics of how
that functors and applicative functors don't help you solve. Then you'll lean the specifics of how
to actually use monads with some examples using the `Option` monad and the all important `IO` monad.
## [Reader Monad](readers.lean.md)
## [Reader Monads](readers.lean.md)
Now that you understand the details of what makes a monadic structure work, in this section, you'll
learn about one of the most useful built in monads `ReaderM`, which gives your programs a
global read-only context.
@@ -45,19 +45,19 @@ of the things a function programming language supposedly "can't" do.
## [Except Monad](except.lean.md)
Similar to the `Option` monad the `Except` monad allows you to change the signature of a function so
that it can return an `ok` value or an `error` and it provides the classic exception handling
that it can return an `ok` value or an `error` and it makes available the classic exception handling
operations `throw/try/catch` so that your programs can do monad-based exception handling.
## [Monad Transformers](transformers.lean.md)
Now that you are familiar with all the above monads it is time to answer the question - how you can
make them work together? After all, there are definitely times when you need multiple kinds of
Now that you are familiar with all the above monads it is time to answer the question of how you can
make them work together. After all, there are definitely times when you need multiple kinds of
monadic behavior. This section introduces the concept of monad transformers, which allow you to
combine multiple monads into one.
## [Monad Laws](laws.lean.md)
This section examines what makes a monad a legal monad. You could just implement your monadic type
classes any way you want and write "monad" instances, but starting back with functors and
applicative functors, you'll learn that all these structures have "laws" that they are expected to
obey with respect to their behavior. You can make instances that don't follow these laws. But you do
so at your peril, as other programmers will be very confused when they try to use them.
This section examines what makes a monad a monad. After all, can't you just implement these type
classes any way you want and write a "monad" instance? Starting back with functors and applicative
functors, you'll learn that all these structures have "laws" that they are expected to obey with
respect to their behavior. You can make instances that don't follow these laws. But you do so at
your peril, as other programmers will be very confused when they try to use them.

View File

@@ -46,7 +46,7 @@ as you will see below.
Functors have two laws: the _identity_ law, and the _composition_ law. These laws express behaviors that
your functor instances should follow. If they don't, other programmers will be very confused at the
effect your instances have on their program.
effect your instances have on their program. Many structures have similar laws, including monads.
The identity law says that if you "map" the identity function (`id`) over your functor, the
resulting functor should be the same. A succinct way of showing this on a `List` functor is:
@@ -65,7 +65,7 @@ def p1 : Point Nat := (Point.mk 1 2)
#eval id <$> p1 == p1 -- false
/-!
Oh, and look while the `List` is behaving well, the `Point` functor fails this identity test.
Oh, and look while the List is behaving well, the `Point` functor fails this identity test.
The _composition_ law says that if you "map" two functions in succession over a functor, this
should be the same as "composing" the functions and simply mapping that one super-function over the
@@ -149,9 +149,9 @@ example [Applicative m] [LawfulApplicative m] (v : m α) :
`pure f <*> pure x = pure (f x)`
Suppose you wrap a function and an object in `pure`. You can then apply the wrapped function over the
Suppose you wrap a function and an object in pure. You can then apply the wrapped function over the
wrapped object. Of course, you could also apply the normal function over the normal object, and then
wrap it in `pure`. The homomorphism law states these results should be the same.
wrap it in pure. The homomorphism law states these results should be the same.
For example:
@@ -238,12 +238,8 @@ instance : Monad List where
pure := List.pure
bind := List.bind
def a := ["apple", "orange"]
#eval a >>= pure -- ["apple", "orange"]
#eval a >>= pure = a -- true
#eval ["apple", "orange"] >>= pure -- ["apple", "orange"]
#eval [1,2,3] >>= pure -- [1,2,3]
/-!
### Right Identity
@@ -256,7 +252,7 @@ def z := 5
#eval pure z >>= h -- some 6
#eval h z -- some 6
#eval pure z >>= h = h z -- true
#eval pure z >>= h = h x -- true
/-!
So in this example, with this specific `z` and `h`, you see that the rule holds true.
@@ -316,7 +312,7 @@ There are two main ideas from all the laws:
1. It should not matter what order you group operations in. Another way to state this is function
composition should hold across your structures.
Following these laws will ensure other programmers are not confused by the behavior of your
Following these laws will ensure other programmers are not confused by the bahavior of your
new functors, applicatives and monads.
-/

View File

@@ -62,8 +62,8 @@ some kind. Let's examine those function types:
So `map` is a pure function, `seq` is a pure function wrapped in the structure, and `bind` takes a
pure input but produces an output wrapped in the structure.
Note: we are ignoring the `(Unit → f α)` function used by `seq` here since that has a special
purpose explained in [Applicatives Lazy Evaluation](applicatives.lean.md#lazy-evaluation).
Note: we are ignoring the `(Unit → f α)` function also used by `seq` in this comparison, since that
was explained in [Applicatives Lazy Evaluation](applicatives.lean.md#lazy-evaluation).
## Basic Monad Example

View File

@@ -3,13 +3,13 @@
In the [previous section](monads.lean.md) you learned about the conceptual idea of monads. You learned
what they are, and saw how some common types like `IO` and `Option` work as monads. Now in this
section, you will be looking at some other useful monads. In particular, the `ReaderM` monad.
part, you will be looking at some other useful monads. In particular, the `ReaderM` monad.
## How to do Global Variables in Lean?
In Lean, your code is generally "pure", meaning functions can only interact with the arguments
passed to them. This effectively means you cannot have global variables. You can have global
definitions, but these are fixed at compile time. If some user behavior might change them, you would have
definitions, but these are fixed at compile time. If some user behavior might change them, you have
to wrap them in the `IO` monad, which means they can't be used from pure code.
Consider this example. Here, you want to have an `Environment` containing different parameters as a
@@ -93,7 +93,7 @@ def main2 : IO Unit := do
#eval main2 -- Result: 7538
/-!
The `ReaderM` monad provides a `run` method and it is the `ReaderM` run method that takes the initial
`Environment` context. So here you see `main2` loads the environment as before, and establishes
`Environment` context. So here you see `main2` loads the environment as before, and estabilishes
the `ReaderM` context by passing `env` to the `run` method.
> **Side note 1**: The `return` statement used above also needs some explanation. The `return`
@@ -111,7 +111,7 @@ the monadic container type.
> **Side note 2**: If the function `readerFunc3` also took some explicit arguments then you would have
to write `(readerFunc3 args).run env` and this is a bit ugly, so Lean provides an infix operator
`|>` that eliminates those parentheses so you can write `readerFunc3 args |>.run env` and then you can
`|>` that eliminiates those parens so you can write `readerFunc3 args |>.run env` and then you can
chain multiple monadic actions like this `m1 args1 |>.run args2 |>.run args3` and this is the
recommended style. You will see this patten used heavily in Lean code.
@@ -122,7 +122,8 @@ become available only when you are in the context of that monad.
Here the `readerFunc2` function uses the `bind` operator `>>=` just to show you that there are bind
operations happening here. The `readerFunc3` function uses the `do` notation you learned about in
[Monads](monads.lean.md) which hides that bind operation and can make the code look cleaner.
So the expression `let x ← readerFunc2` is also calling the `bind` function under the covers,
The `do` notation with `let x ← readerFunc2` is also calling the `bind` function under the covers,
so that you can access the unwrapped value `x` needed for the `toString x` conversion.
The important difference here to the earlier code is that `readerFunc3` and `readerFunc2` no longer
@@ -148,7 +149,7 @@ Now, remember in Lean that a function that takes an argument of type `Nat` and r
like `def f (a : Nat) : String` is the same as this function `def f : Nat → String`. These are
exactly equal as types. Well this is being used by the `ReaderM` Monad to add an input argument to
all the functions that use the `ReaderM` monad and this is why `main` is able to start things off by
simply passing that new input argument in `readerFunc3.run env`. So now that you know the implementation
simply passing that new input argument in `readerFunc3 env`. So now that you know the implementation
details of the `ReaderM` monad you can see that what it is doing looks very much like the original
code we wrote at the beginning of this section, only it's taking a lot of the tedious work off your
plate and it is creating a nice clean separation between what your pure functions are doing, and the
@@ -157,7 +158,7 @@ global context idea that the `ReaderM` adds.
## withReader
One `ReaderM` function can call another with a modified version of the `ReaderM` context. You can
use the `withReader` function from the `MonadWithReader` type class to do this:
use the `withReader` function from the `MonadWithReader` typeclass to do this:
-/
def readerFunc3WithReader : ReaderM Environment String := do
@@ -165,8 +166,8 @@ def readerFunc3WithReader : ReaderM Environment String := do
return "Result: " ++ toString x
/-!
Here we changed the `user` in the `Environment` context to "new user" and then we passed that
modified context to `readerFunc2`.
Here we changed the `user` in the `Environment` context to "new user" and then we
passed that modified context to `readerFunc2`.
So `withReader f m` executes monad `m` in the `ReaderM` context modified by `f`.
@@ -188,12 +189,6 @@ find that in larger code bases, with many different types of monads all composed
greatly cleans up the code. Monads provide a beautiful functional way of managing cross-cutting
concerns that would otherwise make your code very messy.
Having this control over the inherited `ReaderM` context via `withReader` is actually very useful
and something that is quite messy if you try and do this sort of thing with global variables, saving
the old value, setting the new one, calling the function, then restoring the old value, making sure
you do that in a try/finally block and so on. The `ReaderM` design pattern avoids that mess
entirely.
Now it's time to move on to [StateM Monad](states.lean.md) which is like a `ReaderM` that is
also updatable.
-/

View File

@@ -9,7 +9,7 @@ part, you will explore the `StateM` monad, which is like a `ReaderM` only the st
## Motivating example: Tic Tac Toe
For this section, let's build a simple model for a Tic Tace Toe game. The main object is the `GameState`
For this part, let's build a simple model for a Tic Tace Toe game. The main object is the `GameState`
data type containing several important pieces of information. First and foremost, it has the
"board", a map from 2D tile indices to the "Tile State" (X, O or empty). Then it also knows the
current player, and it has a random generator.
@@ -59,7 +59,7 @@ provide an initial state, in addition to the computation to run. `StateM` then p
the result of the computation combined with the final updated state.
If you wish to discard the final state and just get the computation's result, you can use
`run'` method instead. Yes in Lean, the apostrophe can be part of a name, you read this "run
`run'` method instead. Yes in Lean, the apostraphe can be part of a name, you read this "run
prime", and the general naming convention is that the prime method discards something.
So for your Tic Tac Toe game, many of your functions will have a signature like `State GameState a`.
@@ -115,16 +115,12 @@ So finally, you can combine these functions together with `do` notation, and it
clean! You don't need to worry about the side effects. The different monadic functions handle them.
Here's a sample of what your function might look like to play one turn of the game. At the end, it
returns a boolean determining if all the spaces have been filled.
Notice in `isGameDone` and `nextTurn` we have stopped providing the full return type
`StateM GameState Unit`. This is because Lean is able to infer the correct monadic return type
from the context and as a result the code is now looking really clean.
-/
def isGameDone := do
def isGameDone : StateM GameState Bool := do
return ( findOpen).isEmpty
def nextTurn := do
def nextTurn : StateM GameState Bool := do
let i chooseRandomMove
applyMove i
isGameDone
@@ -153,7 +149,7 @@ def printBoard (board : Board) : IO Unit := do
IO.println row
row := []
def playGame := do
def playGame : StateM GameState Unit := do
while true do
let finished nextTurn
if finished then return
@@ -190,8 +186,10 @@ at the reduced Type for `nextTurn`:
So a function like `nextTurn` that might have just returned a `Bool` has been modified by the
`StateM` monad such that the initial `GameState` is passed in as a new input argument, and the output
value has been changed to the pair `Bool × GameState` so that it can return the pure `Bool` and the
updated `GameState`. So `playGame` then is automatically saving that updated game state so that each
time around the `while` loop it is acting on the new state, otherwise that would be an infinite loop!
updated `GameState`. This is why the call to `nextTurn` looks like this: `let (_, g) := nextTurn gs`.
This expression `(_, g)` conveniently breaks the pair up into 2 values, it doesn't care what the first
value is (hence the underscore `_`), but it does need the updated state `g` which you can then assign
back to the mutable `gs` variable to use next time around this loop.
It is also interesting to see how much work the `do` and `←` notation are doing for you. To
implement the `nextTurn` function without these you would have to write this, manually plumbing
@@ -206,12 +204,6 @@ def nextTurnManually : StateM GameState Bool
/-!
This expression `let (i, gs)` conveniently breaks a returned pair up into 2 variables.
In the expression `let (_, gs')` we didn't care what the first value was so we used underscore.
Notice that nextTurn is capturing the updated game state from `chooseRandomMove` in the variable
`gs`, which it is then passing to `applyMove` which returns `gs'` which is passed to `isGameDone`
and that function returns `gs''` which we then return from `nextTurnManually`. Phew, what a lot
of work you don't have to do when you use `do` notation!
## StateM vs ReaderM
@@ -228,11 +220,6 @@ In this function `chooseRandomMove` is modifying the state that `applyMove` is g
and `chooseRandomMove` knows nothing about `applyMove`. So `StateM` functions can have this
kind of downstream effect outside their own scope, whereas, `withReader` cannot do that.
So there is no equivalent to `withReader` for `StateM`, besides you can always use the `StateM`
`set` function to modify the state before calling the next function anyway. You could however,
manually call a `StateM` function like you see in `nextTurnManually` and completely override
the state at any point that way.
## State, IO and other languages
When thinking about Lean, it is often seen as a restriction that you can't have global variables or
@@ -255,7 +242,7 @@ your code cannot communicate with the outside world, you can be far more certain
The `StateM` monad is also a more disciplined way of managing side effects. Top level code could
call a `StateM` function multiple times with different independent initial states, even doing that
across multiple tasks in parallel and each of these cannot clobber the state belonging to other
tasks. Monadic code is more predictable and reusable than code that uses global variables.
tasks. Monadic code is more reusable than code that uses global variables.
## Summary

View File

@@ -117,7 +117,7 @@ lifting. You already used lifting in the above code, because you were able to c
`parseArguments` which has a bigger type `StateT Config (ReaderT Arguments (Except String))`.
This "just worked" because Lean did some magic with monad lifting.
To give you a simpler example of this, suppose you have the following function:
To give you a simpler example of this, suppose you have the following funciton:
-/
def divide (x : Float ) (y : Float): ExceptT String Id Float :=
if y == 0 then
@@ -193,9 +193,9 @@ If you have an instance `MonadLift m n` that means there is a way to turn a comp
inside of `m` into one that happens inside of `n` and (this is the key part) usually *without* the
instance itself creating any additional data that feeds into the computation. This means you can in
principle declare lifting instances from any monad to any other monad, it does not, however, mean
that you should do this in all cases. You can get a very nice report on how all this was done by
adding the line `set_option trace.Meta.synthInstance true in` before `divideCounter` and moving you
cursor to the end of the first line after `do`.
that you should do this in all cases. You can get a report from Lean of how all this was done by
add the line `set_option trace.Meta.synthInstance true in` before main and moving the
cursor to the end of the first line after `do` and you will see a nice detailed report.
This was a lot of detail, but it is very important to understand how monad lifting works because it
is used heavily in Lean programs.
@@ -225,7 +225,7 @@ instance : MonadLift m (ReaderT ρ m) where
This lift operation creates a function that defines the required `ReaderT` input
argument, but the inner monad doesn't know or care about `ReaderT` so the
monadLift function throws it away with the `_` then calls the inner monad action `x`.
This is a perfectly legal implementation of the `ReaderM` monad.
This is a perfectly legal and trivial way to implement a `ReaderM` monad.
## Add your own Custom MonadLift
@@ -270,10 +270,10 @@ def main3 : IO Unit := do
#eval main3 -- (2.500000, 1)
/-!
It turns out that the `IO` monad you see in your `main` function is based on the `EStateM.Result` type
It turns out that the `IO` monad you see in your `main` function is based on a `Result` type
which is similar to the `Except` type but it has an additional return value. The `liftIO` function
converts any `Except String α` into `IO α` by simply mapping the ok case of the `Except` to the
`Result.ok` and the error case to the `Result.error`.
`Result.ok` and the error case to the Result.error.
## Lifting ExceptT

View File

@@ -7,7 +7,7 @@ overloading existing) prefix, infix, and postfix operators.
infixl:65 " + " => HAdd.hAdd -- left-associative
infix:50 " = " => Eq -- non-associative
infixr:80 " ^ " => HPow.hPow -- right-associative
prefix:75 "-" => Neg.neg
prefix:100 "-" => Neg.neg
# set_option quotPrecheck false
postfix:max "⁻¹" => Inv.inv
```
@@ -26,7 +26,7 @@ can make this more precise by looking at what the commands above unfold to:
notation:65 lhs:65 " + " rhs:66 => HAdd.hAdd lhs rhs
notation:50 lhs:51 " = " rhs:51 => Eq lhs rhs
notation:80 lhs:81 " ^ " rhs:80 => HPow.hPow lhs rhs
notation:75 "-" arg:75 => Neg.neg arg
notation:100 "-" arg:100 => Neg.neg arg
# set_option quotPrecheck false
notation:1024 arg:1024 "⁻¹" => Inv.inv arg -- `max` is a shorthand for precedence 1024
```

View File

@@ -18,11 +18,9 @@ See quick [walkthrough demo video](https://www.youtube.com/watch?v=yZo6k48L0VY).
```
info: syncing channel updates for 'nightly'
info: latest update on nightly, lean version nightly-2023-06-27
info: latest update on nightly, lean version nightly-2021-12-05
info: downloading component 'lean'
```
If there is no popup, you probably have Elan installed already.
You may want to make sure that your default toolchain is Lean 4 in this case by running `elan default leanprover/lean4:nightly` and reopen the file, as the next step will fail otherwise.
1. While it is installing, you can paste the following Lean program into the new file:
@@ -39,8 +37,6 @@ You are set up!
## Create a Lean Project
*If your goal is to contribute to [mathlib4](https://github.com/leanprover-community/mathlib4) or use it as a depdency, please see its readme for specific instructions on how to do that.*
You can now create a Lean project in a new folder. Run `lake init foo` from "View > Terminal" to create a package, followed by `lake build` to get an executable version of your Lean program.
On Linux/macOS, you first have to follow the instructions printed by the Lean installation or log out and in again for the Lean executables to be available in you terminal.

View File

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

View File

@@ -2,7 +2,7 @@
### Tier 1
Platforms built & tested by our CI, available as nightly releases via elan (see below)
Platforms built & tested by our CI, available as nightly & stable releases via elan (see above)
* x86-64 Linux with glibc 2.27+
* x86-64 macOS 10.15+
@@ -10,13 +10,13 @@ Platforms built & tested by our CI, available as nightly releases via elan (see
### Tier 2
Platforms cross-compiled but not tested by our CI, available as nightly releases
Platforms cross-compiled but not tested by our CI, available as nightly & stable 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+
* aarch64 (Apple Silicon) macOS
* aarch64 (M1) macOS
<!--
### Tier 3
@@ -26,17 +26,28 @@ Platforms that are known to work from manual testing, but do not come with CI or
# Setting Up Lean
See also the [quickstart](./quickstart.md) instructions for a standard setup with VS Code as the editor.
There are currently two ways to set up a Lean 4 development environment:
* [basic setup](./setup.md#basic-setup) (Linux/macOS/Windows): uses [`elan`](https://github.com/leanprover/elan) + your preinstalled editor
* [Nix setup](./setup.md#nix-setup) (Linux/macOS/WSL): uses the [Nix](https://nixos.org/nix/) package manager for installing all dependencies localized to your project
See also the [quickstart](./quickstart.md) instructions for using the basic setup with VS Code as the editor.
## Basic Setup
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 nightly release (https://github.com/leanprover/lean4-nightly/releases)
# download & activate latest Lean 4 release (https://github.com/leanprover/lean4/releases)
$ elan default leanprover/lean4:stable
# alternatively, use the latest nightly build (https://github.com/leanprover/lean4-nightly/releases)
$ elan default leanprover/lean4:nightly
# alternatively, activate Lean 4 in current directory only
$ elan override set leanprover/lean4:stable
```
## `lake`
### `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.
@@ -56,8 +67,80 @@ After running `lake build` you will see a binary named `./build/bin/foo` and whe
Hello, world!
```
## Editing
### 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).
## Nix Setup
The alternative setup based on Nix provides a perfectly reproducible development environment for your project from the Lean version down to the editor and Lean extension.
However, it is still experimental and subject to change; in particular, it is heavily based on an unreleased version of Nix enabling [Nix Flakes](https://www.tweag.io/blog/2020-05-25-flakes/). The setup has been tested on NixOS, other Linux distributions, and macOS.
After installing (any version of) Nix (<https://nixos.org/download.html>), you can easily open a shell with the particular pre-release version of Nix needed by and tested with our setup (called the "Lean shell" from here on):
```bash
$ nix-shell https://github.com/leanprover/lean4/archive/master.tar.gz -A nix
```
While this shell is sufficient for executing the steps below, it is recommended to also set the following options in `/etc/nix/nix.conf` (`nix.extraOptions` in NixOS):
```
max-jobs = auto # Allow building multiple derivations in parallel
keep-outputs = true # Do not garbage-collect build time-only dependencies (e.g. clang)
# Allow fetching build results from the Lean Cachix cache
trusted-substituters = https://lean4.cachix.org/
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= lean4.cachix.org-1:mawtxSxcaiWE24xCXXgh3qnvlTkyU7evRRnGeAhD4Wk=
```
On a multi-user installation of Nix (the default), you need to restart the Nix daemon afterwards:
```bash
sudo pkill nix-daemon
```
The [Cachix](https://cachix.org/) integration will magically beam any build steps already executed by the CI right onto your machine when calling Nix commands in the shell opened above.
It can be set up analogously as a cache for your own project.
Note: Your system Nix might print warnings about not knowing some of the settings used by the Lean shell Nix, which can be ignored.
### Basic Commands
From a Lean shell, run
```bash
$ nix flake new mypkg -t github:leanprover/lean4
```
to create a new Lean package in directory `mypkg` using the latest commit of Lean 4.
Such packages follow the same directory layout as described in the basic setup above, except for a `lakefile.lean` replaced by a `flake.nix` file set up so you can run Nix commands on it, for example:
```bash
$ nix build # build package and all dependencies
$ nix build .#executable # compile `main` definition into executable (after you've added one)
$ nix run .#emacs-dev # open a pinned version of Emacs with lean4-mode fully set up
$ nix run .#emacs-dev MyPackage.lean # arguments can be passed as well, e.g. the file to open
$ nix run .#vscode-dev MyPackage.lean # ditto, using VS Code
```
Note that if you rename `MyPackage.lean`, you also have to adjust the `name` attribute in `flake.nix` accordingly.
Also note that if you turn the package into a Git repository, only tracked files will be visible to Nix.
As in the basic setup, changes need to be saved to be visible in other files, which have then to be invalidated via an editor command.
If you don't want to or cannot start the pinned editor from Nix, e.g. because you're running Lean inside WSL/a container/on a different machine, you can manually point your editor at the `lean` wrapper script the commands above use internally:
```bash
$ nix build .#lean-dev -o result-lean-dev
```
The resulting `./result-lean-dev/bin/lean` script essentially runs `nix run .#lean` in the current project's root directory when you open a Lean file or use the "refresh dependencies" command such that the correct Lean version for that project is executed.
This includes selecting the correct stage of Lean (which it will compile on the fly, though without progress output) if you are [working on Lean itself](./make/nix.md#editor-integration).
Package dependencies can be added as further input flakes and passed to the `deps` list of `buildLeanPackage`. Example: <https://github.com/Kha/testpkg2/blob/master/flake.nix#L5>
For hacking, it can be useful to temporarily override an input with a local checkout/different version of a dependency:
```bash
$ nix build --override-input somedep path/to/somedep
```
On a build error, Nix will show the last 10 lines of the output by default. You can pass `-L` to `nix build` to show all lines, or pass the shown `*.drv` path to `nix log` to show the full log after the fact.
Keeping all outputs ever built on a machine alive can accumulate to quite impressive amounts of disk space, so you might want to trigger the Nix GC when `/nix/store/` has grown too large:
```bash
nix-collect-garbage
```
This will remove everything not reachable from "GC roots" such as the `./result` symlink created by `nix build`.
Note that the package information in `flake.nix` is currently completely independent from `lakefile.lean` used in the basic setup.
Unifying the two formats is TBD.

View File

@@ -1,71 +0,0 @@
# Nix Setup
An alternative setup based on Nix provides a perfectly reproducible development environment for your project from the Lean version down to the editor and Lean extension.
However, it is still experimental and subject to change; in particular, it is heavily based on an unreleased version of Nix enabling [Nix Flakes](https://www.tweag.io/blog/2020-05-25-flakes/). The setup has been tested on NixOS, other Linux distributions, and macOS.
After installing (any version of) Nix (<https://nixos.org/download.html>), you can easily open a shell with the particular pre-release version of Nix needed by and tested with our setup (called the "Lean shell" from here on):
```bash
$ nix-shell https://github.com/leanprover/lean4/archive/master.tar.gz -A nix
```
While this shell is sufficient for executing the steps below, it is recommended to also set the following options in `/etc/nix/nix.conf` (`nix.extraOptions` in NixOS):
```
max-jobs = auto # Allow building multiple derivations in parallel
keep-outputs = true # Do not garbage-collect build time-only dependencies (e.g. clang)
# Allow fetching build results from the Lean Cachix cache
trusted-substituters = https://lean4.cachix.org/
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= lean4.cachix.org-1:mawtxSxcaiWE24xCXXgh3qnvlTkyU7evRRnGeAhD4Wk=
```
On a multi-user installation of Nix (the default), you need to restart the Nix daemon afterwards:
```bash
sudo pkill nix-daemon
```
The [Cachix](https://cachix.org/) integration will magically beam any build steps already executed by the CI right onto your machine when calling Nix commands in the shell opened above.
It can be set up analogously as a cache for your own project.
Note: Your system Nix might print warnings about not knowing some of the settings used by the Lean shell Nix, which can be ignored.
## Basic Commands
From a Lean shell, run
```bash
$ nix flake new mypkg -t github:leanprover/lean4
```
to create a new Lean package in directory `mypkg` using the latest commit of Lean 4.
Such packages follow the same directory layout as described in the standard setup, except for a `lakefile.lean` replaced by a `flake.nix` file set up so you can run Nix commands on it, for example:
```bash
$ nix build # build package and all dependencies
$ nix build .#executable # compile `main` definition into executable (after you've added one)
$ nix run .#emacs-dev # open a pinned version of Emacs with lean4-mode fully set up
$ nix run .#emacs-dev MyPackage.lean # arguments can be passed as well, e.g. the file to open
$ nix run .#vscode-dev MyPackage.lean # ditto, using VS Code
```
Note that if you rename `MyPackage.lean`, you also have to adjust the `name` attribute in `flake.nix` accordingly.
Also note that if you turn the package into a Git repository, only tracked files will be visible to Nix.
As in the standard setup, changes need to be saved to be visible in other files, which have then to be invalidated via an editor command.
If you don't want to or cannot start the pinned editor from Nix, e.g. because you're running Lean inside WSL/a container/on a different machine, you can manually point your editor at the `lean` wrapper script the commands above use internally:
```bash
$ nix build .#lean-dev -o result-lean-dev
```
The resulting `./result-lean-dev/bin/lean` script essentially runs `nix run .#lean` in the current project's root directory when you open a Lean file or use the "refresh dependencies" command such that the correct Lean version for that project is executed.
This includes selecting the correct stage of Lean (which it will compile on the fly, though without progress output) if you are [working on Lean itself](./make/nix.md#editor-integration).
Package dependencies can be added as further input flakes and passed to the `deps` list of `buildLeanPackage`. Example: <https://github.com/Kha/testpkg2/blob/master/flake.nix#L5>
For hacking, it can be useful to temporarily override an input with a local checkout/different version of a dependency:
```bash
$ nix build --override-input somedep path/to/somedep
```
On a build error, Nix will show the last 10 lines of the output by default. You can pass `-L` to `nix build` to show all lines, or pass the shown `*.drv` path to `nix log` to show the full log after the fact.
Keeping all outputs ever built on a machine alive can accumulate to quite impressive amounts of disk space, so you might want to trigger the Nix GC when `/nix/store/` has grown too large:
```bash
nix-collect-garbage
```
This will remove everything not reachable from "GC roots" such as the `./result` symlink created by `nix build`.
Note that the package information in `flake.nix` is currently completely independent from `lakefile.lean` used in the standard setup.
Unifying the two formats is TBD.

View File

@@ -224,7 +224,7 @@ example (n : Nat) : (binToChar n).isSome -> n = 0 n = 1 := by
next => exact fun _ => Or.inr rfl
next => intro h; cases h
/- Hypotheses about previous cases can be accessed by assigning them a
/- Hypotheses about previous cases can be accessesd 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

View File

@@ -382,7 +382,7 @@ class HMul (α : Type u) (β : Type v) (γ : outParam (Type w)) where
export HMul (hMul)
@[default_instance]
@[defaultInstance]
instance : HMul Int Int Int where
hMul := Int.mul
@@ -391,7 +391,7 @@ def xs : List Int := [1, 2, 3]
#check fun y => xs.map (fun x => hMul x y) -- Int -> List Int
# end Ex
```
By tagging the instance above with the attribute `default_instance`, we are instructing Lean
By tagging the instance above with the attribute `defaultInstance`, we are instructing Lean
to use this instance on pending type class synthesis problems.
The actual Lean implementation defines homogeneous and heterogeneous classes for arithmetical operators.
Moreover, `a+b`, `a*b`, `a-b`, `a/b`, and `a%b` are notations for the heterogeneous versions.
@@ -404,7 +404,7 @@ structure Rational where
den : Nat
inv : den ≠ 0
@[default_instance 200]
@[defaultInstance 200]
instance : OfNat Rational n where
ofNat := { num := n, den := 1, inv := by decide }
@@ -423,7 +423,7 @@ Now, we reveal how the notation `a*b` is defined in Lean.
class OfNat (α : Type u) (n : Nat) where
ofNat : α
@[default_instance]
@[defaultInstance]
instance (n : Nat) : OfNat Nat n where
ofNat := n
@@ -433,7 +433,7 @@ class HMul (α : Type u) (β : Type v) (γ : outParam (Type w)) where
class Mul (α : Type u) where
mul : ααα
@[default_instance 10]
@[defaultInstance 10]
instance [Mul α] : HMul α α α where
hMul a b := Mul.mul a b

22
flake.lock generated
View File

@@ -15,14 +15,29 @@
"type": "github"
}
},
"lean-stage0": {
"locked": {
"lastModified": 0,
"narHash": "sha256-3K/43lSW4WIHNG+HHVKCD1odS63mHuaQ4ueHyTIkcls=",
"owner": "leanprover",
"repo": "lean4",
"rev": "0000000000000000000000000000000000000000",
"type": "github"
},
"original": {
"owner": "leanprover",
"repo": "lean4",
"type": "github"
}
},
"lean4-mode": {
"flake": false,
"locked": {
"lastModified": 1676498134,
"narHash": "sha256-u3WvyKxOViZG53hkb8wd2/Og6muTecbh+NdflIgVeyk=",
"lastModified": 1656864638,
"narHash": "sha256-Th+IOKGspSZEXJZMnoP9GqsuVZLt137JfgZg7Q0sHxQ=",
"owner": "leanprover",
"repo": "lean4-mode",
"rev": "2c6ef33f476fdf5eb5e4fa4fa023ba8b11372440",
"rev": "f23510741b3291d8f9797df9407b1e45d7f22b23",
"type": "github"
},
"original": {
@@ -118,6 +133,7 @@
"root": {
"inputs": {
"flake-utils": "flake-utils",
"lean-stage0": "lean-stage0",
"lean4-mode": "lean4-mode",
"nix": "nix",
"nixpkgs": "nixpkgs_2"

View File

@@ -1,36 +1,36 @@
{
description = "Lean interactive theorem prover";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.nix.url = "github:NixOS/nix";
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixpkgs-unstable;
inputs.flake-utils.url = github:numtide/flake-utils;
inputs.nix.url = github:NixOS/nix;
inputs.lean4-mode = {
url = "github:leanprover/lean4-mode";
url = github:leanprover/lean4-mode;
flake = false;
};
# used *only* by `stage0-from-input` below
#inputs.lean-stage0 = {
# url = github:leanprover/lean4;
# inputs.nixpkgs.follows = "nixpkgs";
# inputs.flake-utils.follows = "flake-utils";
# inputs.nix.follows = "nix";
# inputs.lean4-mode.follows = "lean4-mode";
#};
inputs.lean-stage0 = {
url = github:leanprover/lean4;
inputs.nixpkgs.follows = "nixpkgs";
inputs.flake-utils.follows = "flake-utils";
inputs.nix.follows = "nix";
inputs.lean4-mode.follows = "lean4-mode";
};
outputs = { self, nixpkgs, flake-utils, nix, lean4-mode, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
outputs = { self, nixpkgs, flake-utils, nix, lean4-mode, lean-stage0 }: flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
# for `vscode-with-extensions`
config.allowUnfree = true;
};
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; inherit nix lean4-mode; };
lean-packages = pkgs.callPackage (./nix/packages.nix) { inherit nix lean4-mode; };
in {
packages = lean-packages // rec {
debug = lean-packages.override { debug = true; };
stage0debug = lean-packages.override { stage0debug = true; };
asan = lean-packages.override { extraCMakeFlags = [ "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address" "-DLEANC_EXTRA_FLAGS=-fsanitize=address" "-DSMALL_ALLOCATOR=OFF" "-DSYMBOLIC=OFF" ]; };
asandebug = asan.override { debug = true; };
sanitized = lean-packages.override { extraCMakeFlags = [ "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address,undefined" "-DLEANC_EXTRA_FLAGS=-fsanitize=address,undefined" "-DSMALL_ALLOCATOR=OFF" "-DSYMBOLIC=OFF" ]; };
sandebug = sanitized.override { debug = true; };
tsan = lean-packages.override {
extraCMakeFlags = [ "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=thread" "-DLEANC_EXTRA_FLAGS=-fsanitize=thread" "-DCOMPRESSED_OBJECT_HEADER=OFF" ];
stage0 = (lean-packages.override {
@@ -42,7 +42,7 @@
tsandebug = tsan.override { debug = true; };
stage0-from-input = lean-packages.override {
stage0 = pkgs.writeShellScriptBin "lean" ''
exec ${inputs.lean-stage0.packages.${system}.lean}/bin/lean -Dinterpreter.prefer_native=false "$@"
exec ${lean-stage0.packages.${system}.lean}/bin/lean -Dinterpreter.prefer_native=false "$@"
'';
};
inherit self;

View File

@@ -1,13 +1,12 @@
{ src, debug ? false, stage0debug ? false, extraCMakeFlags ? [],
stdenv, lib, cmake, gmp, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
{ debug ? false, stage0debug ? false, extraCMakeFlags ? [],
stdenv, lib, cmake, gmp, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin,
... } @ args:
with builtins;
rec {
inherit stdenv;
sourceByRegex = p: rs: lib.sourceByRegex p (map (r: "(/src/)?${r}") rs);
buildCMake = args: stdenv.mkDerivation ({
nativeBuildInputs = [ cmake ];
buildInputs = [ gmp llvmPackages.llvm ];
buildInputs = [ gmp ];
# https://github.com/NixOS/nixpkgs/issues/60919
hardeningDisable = [ "all" ];
dontStrip = (args.debug or debug);
@@ -16,7 +15,7 @@ rec {
patchShebangs .
'';
} // args // {
src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
src = args.realSrc or (lib.sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
preConfigure = args.preConfigure or "" + ''
# ignore absence of submodule
@@ -26,7 +25,7 @@ rec {
lean-bin-tools-unwrapped = buildCMake {
name = "lean-bin-tools";
outputs = [ "out" "leanc_src" ];
realSrc = sourceByRegex (src + "/src") [ "CMakeLists\.txt" "cmake.*" "bin.*" "include.*" ".*\.in" "Leanc\.lean" ];
realSrc = lib.sourceByRegex ../src [ "CMakeLists\.txt" "cmake.*" "bin.*" "include.*" ".*\.in" "Leanc\.lean" ];
preConfigure = ''
touch empty.cpp
sed -i 's/add_subdirectory.*//;s/set(LEAN_OBJS.*/set(LEAN_OBJS empty.cpp)/' CMakeLists.txt
@@ -44,7 +43,7 @@ rec {
};
leancpp = buildCMake {
name = "leancpp";
src = src + "/src";
src = ../src;
buildFlags = [ "leancpp" "leanrt" "leanrt_initial-exec" "shell" ];
installPhase = ''
mkdir -p $out
@@ -53,14 +52,18 @@ rec {
mv runtime/libleanrt_initial-exec.a $out/lib
'';
};
stage0 = args.stage0 or (buildCMake {
# rename derivation so `nix run` uses the right executable name but we still see the stage in the build log
wrapStage = stage: runCommand "lean" {} ''
ln -s ${stage} $out
'';
stage0 = wrapStage (args.stage0 or (buildCMake {
name = "lean-stage0";
realSrc = src + "/stage0/src";
realSrc = ../stage0/src;
debug = stage0debug;
cmakeFlags = [ "-DSTAGE=0" ];
extraCMakeFlags = [];
preConfigure = ''
ln -s ${src + "/stage0/stdlib"} ../stdlib
ln -s ${../stage0/stdlib} ../stdlib
'';
installPhase = ''
mkdir -p $out/bin $out/lib/lean
@@ -72,8 +75,7 @@ rec {
done
otool -L $out/bin/lean
'';
meta.mainProgram = "lean";
});
}));
stage = { stage, prevStage, self }:
let
desc = "stage${toString stage}";
@@ -85,9 +87,9 @@ rec {
lean-final = self;
leanFlags = [ "-DwarningAsError=true" ];
} ({
src = src + "/src";
src = ../src;
roots = [ { mod = args.name; glob = "andSubmodules"; } ];
fullSrc = src;
fullSrc = ../.;
srcPrefix = "src";
inherit debug;
} // args);
@@ -103,29 +105,27 @@ rec {
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Init ]; };
stdlib = [ Init Lean ];
modDepsFiles = symlinkJoin { name = "modDepsFiles"; paths = map (l: l.modDepsFile) (stdlib ++ [ Leanc ]); };
depRoots = symlinkJoin { name = "depRoots"; paths = map (l: l.depRoots) stdlib; };
iTree = symlinkJoin { name = "ileans"; paths = map (l: l.iTree) stdlib; };
extlib = stdlib; # TODO: add Lake
Leanc = build { name = "Leanc"; src = lean-bin-tools-unwrapped.leanc_src; deps = stdlib; roots = [ "Leanc" ]; };
stdlibLinkFlags = "-L${Init.staticLib} -L${Lean.staticLib} -L${leancpp}/lib/lean";
leanshared = runCommand "leanshared" { buildInputs = [ stdenv.cc ]; libName = "libleanshared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Wl,-Bsymbolic"} \
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Bsymbolic"} \
${if stdenv.isDarwin then "-Wl,-force_load,${Init.staticLib}/libInit.a -Wl,-force_load,${Lean.staticLib}/libLean.a -Wl,-force_load,${leancpp}/lib/lean/libleancpp.a ${leancpp}/lib/libleanrt_initial-exec.a -lc++"
else "-Wl,--whole-archive -lInit -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++"} -lm ${stdlibLinkFlags} \
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
-o $out/$libName
'';
mods = Init.mods // Lean.mods;
leanc = writeShellScriptBin "leanc" ''
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${leanshared} "$@"
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable.override { withSharedStdlib = true; }}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${leanshared} "$@"
'';
lean = runCommand "lean" { buildInputs = lib.optional stdenv.isDarwin darwin.cctools; } ''
mkdir -p $out/bin
${leanc}/bin/leanc ${leancpp}/lib/lean.cpp.o ${leanshared}/* -o $out/bin/lean
'';
# derivation following the directory layout of the "basic" setup, mostly useful for running tests
lean-all = stdenv.mkDerivation {
lean-all = wrapStage(stdenv.mkDerivation {
name = "lean-${desc}";
buildCommand = ''
mkdir -p $out/bin $out/lib/lean
@@ -135,21 +135,14 @@ rec {
# NOTE: `lndir` will not override existing `bin/leanc`
${lndir}/bin/lndir -silent ${lean-bin-tools-unwrapped} $out
'';
meta.mainProgram = "lean";
};
cacheRoots = linkFarmFromDrvs "cacheRoots" [
stage0 lean leanc lean-all iTree modDepsFiles depRoots Leanc.src
# .o files are not a runtime dependency on macOS because of lack of thin archives
Lean.oTree
];
});
test = buildCMake {
name = "lean-test-${desc}";
realSrc = lib.sourceByRegex src [ "src.*" "tests.*" ];
realSrc = lib.sourceByRegex ../. [ "src.*" "tests.*" ];
buildInputs = [ gmp perl ];
preConfigure = ''
cd src
'';
extraCMakeFlags = [ "-DLLVM=OFF" ];
postConfigure = ''
patchShebangs ../../tests
rm -r bin lib include share
@@ -165,7 +158,7 @@ rec {
update-stage0 =
let cTree = symlinkJoin { name = "cs"; paths = map (l: l.cTree) stdlib; }; in
writeShellScriptBin "update-stage0" ''
CSRCS=${cTree} CP_C_PARAMS="--dereference --no-preserve=all" ${src + "/script/update-stage0"}
CSRCS=${cTree} CP_PARAMS="--dereference --no-preserve=all" ${../script/update-stage0}
'';
update-stage0-commit = writeShellScriptBin "update-stage0-commit" ''
set -euo pipefail
@@ -174,17 +167,16 @@ rec {
'';
link-ilean = writeShellScriptBin "link-ilean" ''
dest=''${1:-src}
rm -rf $dest/build/lib || true
mkdir -p $dest/build/lib
ln -s ${iTree}/* $dest/build/lib
ln -sf ${iTree}/* $dest/build/lib
'';
benchmarks =
let
entries = attrNames (readDir (src + "/tests/bench"));
entries = attrNames (readDir ../tests/bench);
leanFiles = map (n: elemAt n 0) (filter (n: n != null) (map (match "(.*)\.lean") entries));
in lib.genAttrs leanFiles (n: (buildLeanPackage {
name = n;
src = filterSource (e: _: baseNameOf e == "${n}.lean") (src + "/tests/bench");
src = filterSource (e: _: baseNameOf e == "${n}.lean") ../tests/bench;
}).executable);
};
stage1 = stage { stage = 1; prevStage = stage0; self = stage1; };

View File

@@ -1,6 +1,6 @@
{ lean, lean-leanDeps ? lean, lean-final ? lean, leanc,
stdenv, lib, coreutils, gnused, writeShellScriptBin, bash, lean-emacs, lean-vscode, nix, substituteAll, symlinkJoin, linkFarmFromDrvs,
runCommand, darwin, mkShell, ... }:
runCommand, gmp, darwin, mkShell, ... }:
let lean-final' = lean-final; in
lib.makeOverridable (
{ name, src, fullSrc ? src, srcPrefix ? "",
@@ -28,18 +28,13 @@ lib.makeOverridable (
precompilePackage ? precompileModules,
# Lean plugin dependencies. Each derivation `plugin` should contain a plugin library at path `${plugin}/${plugin.name}`.
pluginDeps ? [],
# `overrideAttrs` for `buildMod`
overrideBuildModAttrs ? null,
debug ? false, leanFlags ? [], leancFlags ? [], linkFlags ? [], executableName ? lib.toLower name, libName ? name,
srcTarget ? "..#stage0", srcArgs ? "(\${args[*]})", lean-final ? lean-final' }@args:
with builtins; let
# "Init.Core" ~> "Init/Core"
modToPath = mod: replaceStrings ["."] ["/"] mod;
modToAbsPath = mod: "${src}/${modToPath mod}";
# sanitize file name before copying to store, except when already in store
copyToStoreSafe = base: suffix: if lib.isDerivation base then base + suffix else
builtins.path { name = lib.strings.sanitizeDerivationName (baseNameOf suffix); path = base + suffix; };
modToLean = mod: copyToStoreSafe src "/${modToPath mod}.lean";
modToLean = mod: "${modToAbsPath mod}.lean";
bareStdenv = ./bareStdenv;
mkBareDerivation = args: derivation (args // {
name = lib.strings.sanitizeDerivationName args.name;
@@ -52,7 +47,7 @@ with builtins; let
set -u
${args.buildCommand}
'' ];
}) // { overrideAttrs = f: mkBareDerivation (lib.fix (lib.extends f (_: args))); };
});
runBareCommand = name: args: buildCommand: mkBareDerivation (args // { inherit name buildCommand; });
runBareCommandLocal = name: args: buildCommand: runBareCommand name (args // {
preferLocalBuild = true;
@@ -63,23 +58,24 @@ with builtins; let
libName = "${name}${stdenv.hostPlatform.extensions.sharedLibrary}";
} ''
mkdir -p $out
${leanc}/bin/leanc -shared ${args} -o $out/$libName
${leanc}/bin/leanc -fPIC -shared ${lib.optionalString stdenv.isLinux "-Bsymbolic"} ${lib.optionalString stdenv.isDarwin "-Wl,-undefined,dynamic_lookup"} -L ${gmp}/lib \
${args} -o $out/$libName
'';
depRoot = name: deps: mkBareDerivation {
name = "${name}-depRoot";
inherit deps;
depRoots = map (drv: drv.LEAN_PATH) deps;
passAsFile = [ "deps" "depRoots" ];
buildCommand = ''
mkdir -p $out
for i in $(cat $depRootsPath); do
for i in $depRoots; do
cp -dru --no-preserve=mode $i/. $out
done
for i in $(cat $depsPath); do
for i in $deps; do
cp -drsu --no-preserve=mode $i/. $out
done
'';
preferLocalBuild = true;
allowSubstitutes = false;
};
srcRoot = src;
@@ -115,40 +111,33 @@ with builtins; let
# we can't know the required files without parsing dependencies (which is what we want this
# function for), so we approximate to the entire package.
let root = (head (split "\\." g));
in lib.optional (pathExists (src + "/${modToPath root}.lean")) root ++ lib.optionals (pathExists (modToAbsPath root)) (submodules root)
in lib.optional (pathExists (modToLean root)) root ++ lib.optionals (pathExists (modToAbsPath root)) (submodules root)
else if g.glob == "one" then expandGlobAllApprox g.mod
else if g.glob == "submodules" then submodules g.mod
else if g.glob == "andSubmodules" then [g.mod] ++ submodules g.mod
else throw "unknown glob kind '${g}'";
# list of modules that could potentially be involved in the build
candidateMods = lib.unique (concatMap expandGlobAllApprox roots);
candidateMods = filter (m: pathExists (modToLean m)) (lib.unique (concatMap expandGlobAllApprox roots));
candidateFiles = map modToLean candidateMods;
modDepsFile = args.modDepsFile or mkBareDerivation {
name = "${name}-deps.json";
candidateFiles = lib.concatStringsSep " " candidateFiles;
passAsFile = [ "candidateFiles" ];
buildCommand = ''
mkdir $out
${lean-leanDeps}/bin/lean --deps-json --stdin < $candidateFilesPath > $out/$name
${lean-leanDeps}/bin/lean --deps-json ${lib.concatStringsSep " " candidateFiles} > $out/$name
'';
};
modDeps = fromJSON (
# the only possible references to store paths in the JSON should be inside errors, so no chance of missed dependencies from this
unsafeDiscardStringContext (readFile "${modDepsFile}/${modDepsFile.name}"));
# map from module name to list of imports
modDepsMap = listToAttrs (lib.zipListsWith lib.nameValuePair candidateMods modDeps.imports);
maybeOverrideAttrs = f: x: if f != null then x.overrideAttrs f else x;
# build module (.olean and .c) given derivations of all (immediate) dependencies
# TODO: make `rec` parts override-compatible?
buildMod = mod: deps: maybeOverrideAttrs overrideBuildModAttrs (mkBareDerivation rec {
buildMod = mod: deps: mkBareDerivation rec {
name = "${mod}";
LEAN_PATH = depRoot mod deps;
LEAN_ABORT_ON_PANIC = "1";
relpath = modToPath mod;
buildInputs = [ lean ];
leanPath = relpath + ".lean";
# should be either single .lean file or directory directly containing .lean file plus dependencies
src = copyToStoreSafe srcRoot ("/" + leanPath);
src = srcRoot + ("/" + leanPath);
outputs = [ "out" "ilean" "c" ];
oleanPath = relpath + ".olean";
ileanPath = relpath + ".ilean";
@@ -158,10 +147,10 @@ with builtins; let
buildCommand = ''
dir=$(dirname $relpath)
mkdir -p $dir $out/$dir $ilean/$dir $c/$dir
if [ -d $src ]; then cp -r $src/. $dir/; else cp $src $leanPath; fi
cp $src $leanPath
lean -o $out/$oleanPath -i $ilean/$ileanPath -c $c/$cPath $leanPath $leanFlags $leanPluginFlags $leanLoadDynlibFlags
'';
}) // {
} // {
inherit deps;
propagatedLoadDynlibs = loadDynlibsOfDeps deps;
};
@@ -190,13 +179,17 @@ with builtins; let
propagatedLoadDynlibs = [sharedLib];
};
externalModMap = lib.foldr (dep: depMap: depMap // dep.mods) {} allExternalDeps;
# map from module name to derivation
modCandidates = mapAttrs (mod: header:
# Recursively build `mod` and its dependencies. `modMap` maps module names to
# `{ deps, drv }` pairs of a derivation and its transitive dependencies (as a nested
# mapping from module names to derivations). It is passed linearly through the
# recursion to memoize common dependencies.
buildModAndDeps = mod: modMap: if modMap ? ${mod} || externalModMap ? ${mod} then modMap else
let
deps = if header.errors == []
then map (m: m.module) header.imports
else abort "errors while parsing imports of ${mod}:\n${lib.concatStringsSep "\n" header.errors}";
in mkMod mod (map (dep: if modDepsMap ? ${dep} then modCandidates.${dep} else externalModMap.${dep}) deps)) modDepsMap;
deps = if modDepsMap.${mod}.errors == []
then map (m: m.module) modDepsMap.${mod}.imports
else abort "errors while parsing imports of ${mod}:\n${lib.concatStringsSep "\n" modDepsMap.${mod}.errors}";
modMap' = lib.foldr buildModAndDeps modMap deps;
in modMap' // { ${mod} = mkMod mod (map (dep: if modMap' ? ${dep} then modMap'.${dep} else externalModMap.${dep}) deps); };
makeEmacsWrapper = name: emacs: lean: writeShellScriptBin name ''
${emacs} --eval "(progn (setq lean4-rootdir \"${lean}\"))" "$@"
'';
@@ -217,11 +210,7 @@ with builtins; let
else if g.glob == "submodules" then submodules g.mod
else if g.glob == "andSubmodules" then [g.mod] ++ submodules g.mod
else throw "unknown glob kind '${g}'";
# subset of `modCandidates` that is transitively reachable from `roots`
mods' = listToAttrs (map (e: { name = e.key; value = modCandidates.${e.key}; }) (genericClosure {
startSet = map (m: { key = m; }) (concatMap expandGlob roots);
operator = e: if modDepsMap ? ${e.key} then map (m: { key = m.module; }) (filter (m: modCandidates ? ${m.module}) modDepsMap.${e.key}.imports) else [];
}));
mods' = lib.foldr buildModAndDeps {} (concatMap expandGlob roots);
allLinkFlags = lib.foldr (shared: acc: acc ++ [ "-L${shared}" "-l${shared.linkName or shared.name}" ]) linkFlags allNativeSharedLibs;
objects = mapAttrs (_: m: m.obj) mods';
@@ -242,14 +231,13 @@ in rec {
lib.optionalAttrs precompilePackage { propagatedLoadDynlibs = [sharedLib]; })
mods';
modRoot = depRoot name (attrValues mods);
depRoots = linkFarmFromDrvs "depRoots" (map (m: m.LEAN_PATH) (attrValues mods));
cTree = symlinkJoin { name = "${name}-cTree"; paths = map (mod: mod.c) (attrValues mods); };
oTree = symlinkJoin { name = "${name}-oTree"; paths = (attrValues objects); };
iTree = symlinkJoin { name = "${name}-iTree"; paths = map (mod: mod.ilean) (attrValues mods); };
sharedLib = mkSharedLib "lib${libName}" ''
${if stdenv.isDarwin then "-Wl,-force_load,${staticLib}/lib${libName}.a" else "-Wl,--whole-archive ${staticLib}/lib${libName}.a -Wl,--no-whole-archive"} \
${lib.concatStringsSep " " (map (d: "${d.sharedLib}/*") deps)}'';
executable = lib.makeOverridable ({ withSharedStdlib ? true }: let
executable = lib.makeOverridable ({ withSharedStdlib ? false }: let
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.leanshared}/*";
in runCommand executableName { buildInputs = [ stdenv.cc leanc ]; } ''
mkdir -p $out/bin

View File

@@ -1,4 +1,4 @@
{ src, pkgs, nix, ... } @ args:
{ pkgs, nix, ... } @ args:
with pkgs;
let
nix-pinned = writeShellScriptBin "nix" ''
@@ -29,7 +29,7 @@ let
stdenv' = if stdenv.isLinux then useGoldLinker stdenv else stdenv;
lean = callPackage (import ./bootstrap.nix) (args // {
stdenv = overrideCC stdenv' cc;
inherit src buildLeanPackage llvmPackages;
inherit buildLeanPackage;
});
makeOverridableLeanPackage = f:
let newF = origArgs: f origArgs // {
@@ -52,10 +52,7 @@ let
src = args.lean4-mode;
packageRequires = with pkgs.emacsPackages.melpaPackages; [ dash f flycheck magit-section lsp-mode s ];
recipe = pkgs.writeText "recipe" ''
(lean4-mode
:repo "leanprover/lean4-mode"
:fetcher github
:files ("*.el" "data"))
(lean4-mode :repo "leanprover/lean4-mode" :fetcher github)
'';
};
lean-emacs = emacsWithPackages [ lean4-mode ];

View File

@@ -1,8 +1,8 @@
{
description = "My Lean package";
inputs.lean.url = "github:leanprover/lean4";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.lean.url = github:leanprover/lean4;
inputs.flake-utils.url = github:numtide/flake-utils;
outputs = { self, lean, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
let

View File

@@ -1,163 +1,169 @@
import Lean.Runtime
import init.lean.config init.io
open io
abbrev M := ReaderT IO.FS.Stream IO
@[reducible] def m := reader_t handle io
def M.run (a : M Unit) (out? : Option String := none) : IO Unit :=
match out? with
| some out => do
IO.FS.withFile out .write (ReaderT.run a .ofHandle)
| none => IO.getStdout >>= (ReaderT.run a)
def m.run (a : m unit) (out : option string := none) : io unit :=
match out with
| some out := do
h mk_file_handle out io.mode.write,
a.run h,
fs.close h
| none := stdout >>= a.run
def emit (s : String) : M Unit := do
( read).putStr s
def emit (s : string) : m unit :=
λ h, fs.write h s
def mkTypedefFn (i : Nat) : M Unit := do
let args := (List.replicate i "obj*").intersperse ", " |> String.join
emit s!"typedef obj* (*fn{i})({args}); // NOLINT\n"
emit s!"#define FN{i}(f) reinterpret_cast<fn{i}>(lean_closure_fun(f))\n"
def mk_typedef_fn (i : nat) : m unit :=
let args := string.join $ (list.repeat "obj*" i).intersperse ", " in
do emit $ sformat! "typedef obj* (*fn{i})({args}); // NOLINT\n",
emit $ sformat! "#define FN{i}(f) reinterpret_cast<fn{i}>(closure_fun(f))\n"
def genSeq (n : Nat) (f : Nat String) (sep := ", ") : String :=
List.range n |>.map f |>.intersperse sep |> .join
-- Make string: "obj* a1, obj* a2, ..., obj* an"
def mk_arg_decls (n : nat) : string :=
string.join $ (n.repeat (λ i r, r ++ [sformat! "obj* a{i+1}"]) []).intersperse ", "
-- make string: "obj* a1, obj* a2, ..., obj* an"
def mkArgDecls (n : Nat) : String :=
genSeq n (s!"obj* a{·+1}")
-- Make string: "a1, a2, ..., a{n}"
def mk_args (n : nat) : string :=
string.join $ (n.repeat (λ i r, r ++ [sformat! "a{i+1}"]) []).intersperse ", "
-- make string: "a{s+1}, ..., a{n}"
def mkArgsFrom (s n : Nat) : String :=
genSeq (n-s) (s!"a{s+·+1}")
-- Make string: "a{s}, a{s+1}, ..., a{n}"
def mk_args_from (s n : nat) : string :=
string.join $ ((n-s).repeat (λ i r, r ++ [sformat! "a{s+i+1}"]) []).intersperse ", "
-- make string: "a1, a2, ..., a{n}"
def mkArgs (n : Nat) : String := mkArgsFrom 0 n
-- Make string: "as[0], as[1], ..., as[n-1]"
def mk_as_args (n : nat) : string :=
string.join $ (n.repeat (λ i r, r ++ [sformat! "as[{i}]"]) []).intersperse ", "
-- make string: "as[0], as[1], ..., as[n-1]"
def mkAsArgs (n : Nat) : String :=
genSeq n (s!"as[{·}]")
-- Make string: "fx(0), ..., fx(n-1)"
def mk_fs_args (n : nat) : string :=
string.join $ (n.repeat (λ i r, r ++ [sformat! "fx({i})"]) []).intersperse ", "
-- make string: "fx(0), ..., fx(n-1)"
def mkFsArgs (n : Nat) : String :=
genSeq n (s!"fx({·})")
-- Make string: "inc(fx(0)); ...; inc(fx(n-1))"
def mk_inc_fs (n : nat) : string :=
string.join $ (n.repeat (λ i r, r ++ [sformat! "inc(fx({i})); "]) [])
-- make string: "lean_inc(fx(0)); ...; lean_inc(fx(n-1))"
def mkIncFs (n : Nat) : String :=
genSeq n (s!"lean_inc(fx({·})); ") (sep := "")
def mkApplyI (n : Nat) (max : Nat) : M Unit := do
let argDecls := mkArgDecls n
let args := mkArgs n
emit s!"extern \"C\" LEAN_EXPORT obj* lean_apply_{n}(obj* f, {argDecls}) \{
if (lean_is_scalar(f)) \{ {genSeq n (s!"lean_dec(a{·+1}); ") (sep := "")}return f; } // f is an erased proof
unsigned arity = lean_closure_arity(f);
unsigned fixed = lean_closure_num_fixed(f);
if (arity == fixed + {n}) \{
if (lean_is_exclusive(f)) \{
switch (arity) \{\n"
for j in [n:max + 1] do
let fs := mkFsArgs (j - n)
let sep := if j = n then "" else ", "
emit s!" case {j}: \{ obj* r = FN{j}(f)({fs}{sep}{args}); lean_free_small_object(f); return r; }\n"
emit " }
}
switch (arity) {\n"
for j in [n:max + 1] do
let lean_incfs := mkIncFs (j - n)
let fs := mkFsArgs (j - n)
let sep := if j = n then "" else ", "
emit s!" case {j}: \{ {lean_incfs}obj* r = FN{j}(f)({fs}{sep}{args}); lean_dec_ref(f); return r; }\n"
emit s!" default:
lean_assert(arity > {max});
obj * as[{n}] = \{ {args} };
obj ** args = static_cast<obj**>(LEAN_ALLOCA(arity*sizeof(obj*))); // NOLINT
for (unsigned i = 0; i < fixed; i++) \{ lean_inc(fx(i)); args[i] = fx(i); }
for (unsigned i = 0; i < {n}; i++) args[fixed+i] = as[i];
obj * r = FNN(f)(args);
lean_dec_ref(f);
return r;
}
} else if (arity < fixed + {n}) \{\n"
if n 2 then do
emit s!" obj * as[{n}] = \{ {args} };
obj ** args = static_cast<obj**>(LEAN_ALLOCA(arity*sizeof(obj*))); // NOLINT
for (unsigned i = 0; i < fixed; i++) \{ lean_inc(fx(i)); args[i] = fx(i); }
for (unsigned i = 0; i < arity-fixed; i++) args[fixed+i] = as[i];
obj * new_f = curry(f, arity, args);
lean_dec_ref(f);
return lean_apply_n(new_f, {n}+fixed-arity, &as[arity-fixed]);\n"
else emit s!" lean_assert(fixed < arity);
lean_unreachable();\n"
emit s!"} else \{
return fix_args(f, \{{args}});
}
}\n"
def mk_apply_i (n : nat) (max : nat) : m unit :=
let arg_decls := mk_arg_decls n in
let args := mk_args n in
do emit $ sformat! "obj* apply_{n}(obj* f, {arg_decls}) {{\n",
emit "unsigned arity = closure_arity(f);\n",
emit "unsigned fixed = closure_num_fixed(f);\n",
emit $ sformat! "if (arity == fixed + {n}) {{\n",
emit $ sformat! " if (is_exclusive(f)) {{\n",
emit $ sformat! " switch (arity) {{\n",
max.mrepeat $ λ i, do {
let j := i + 1,
when (j n) $
let fs := mk_fs_args (j - n) in
let sep := if j = n then "" else ", " in
emit $ sformat! " case {j}: {{ obj* r = FN{j}(f)({fs}{sep}{args}); free_closure_obj(f); return r; }\n"
},
emit " }\n",
emit " }\n",
emit $ sformat! " switch (arity) {{\n",
max.mrepeat $ λ i, do {
let j := i + 1,
when (j n) $
let incfs := mk_inc_fs (j - n) in
let fs := mk_fs_args (j - n) in
let sep := if j = n then "" else ", " in
emit $ sformat! " case {j}: {{ {incfs}obj* r = FN{j}(f)({fs}{sep}{args}); dec_ref(f); return r; }\n"
},
emit " default:\n",
emit $ sformat! " lean_assert(arity > {max});\n",
emit $ sformat! " obj * as[{n}] = {{ {args} };\n",
emit " obj ** args = static_cast<obj**>(LEAN_ALLOCA(arity*sizeof(obj*))); // NOLINT\n",
emit " for (unsigned i = 0; i < fixed; i++) { inc(fx(i)); args[i] = fx(i); }\n",
emit $ sformat! " for (unsigned i = 0; i < {n}; i++) args[fixed+i] = as[i];\n",
emit " obj * r = FNN(f)(args);\n",
emit " dec_ref(f);\n",
emit " return r;\n",
emit " }\n",
emit $ sformat! "} else if (arity < fixed + {n}) {{\n",
if n 2 then do
emit $ sformat! " obj * as[{n}] = {{ {args} };\n",
emit " obj ** args = static_cast<obj**>(LEAN_ALLOCA(arity*sizeof(obj*))); // NOLINT\n",
emit " for (unsigned i = 0; i < fixed; i++) { inc(fx(i)); args[i] = fx(i); }\n",
emit " for (unsigned i = 0; i < arity-fixed; i++) args[fixed+i] = as[i];\n",
emit " obj * new_f = curry(f, arity, args);\n",
emit " dec_ref(f);\n",
emit $ sformat! " return apply_n(new_f, {n}+fixed-arity, as+arity-fixed);\n"
else emit " lean_assert(fixed < arity);\n lean_unreachable();\n",
emit "} else {\n",
emit $ sformat! " return fix_args(f, {{{args}});\n",
emit "}\n",
emit "}\n"
def mkCurry (max : Nat) : M Unit := do
emit "obj* curry(void* f, unsigned n, obj** as) {
switch (n) {
case 0: lean_unreachable();\n"
for i in [0:max] do
let as := mkAsArgs (i+1)
emit s!"case {i+1}: return reinterpret_cast<fn{i+1}>(f)({as});\n"
emit "default: return reinterpret_cast<fnn>(f)(as);
}
}
static obj* curry(obj* f, unsigned n, obj** as) { return curry(lean_closure_fun(f), n, as); }\n"
def mk_curry (max : nat) : m unit :=
do emit "static obj* curry(void* f, unsigned n, obj** as) {\n",
emit "switch (n) {\n",
emit "case 0: lean_unreachable();\n",
max.mrepeat $ λ i,
let as := mk_as_args (i+1) in
emit $ sformat! "case {i+1}: return reinterpret_cast<fn{i+1}>(f)({as});\n",
emit "default: return reinterpret_cast<fnn>(f)(as);\n",
emit "}\n",
emit "}\n",
emit "static obj* curry(obj* f, unsigned n, obj** as) { return curry(closure_fun(f), n, as); }\n"
def mkApplyN (max : Nat) : M Unit := do
emit "extern \"C\" LEAN_EXPORT obj* lean_apply_n(obj* f, unsigned n, obj** as) {
switch (n) {
case 0: lean_unreachable();\n"
for i in [0:max] do
let as := mkAsArgs (i+1)
emit s!"case {i+1}: return lean_apply_{i+1}(f, {as});\n"
emit "default: return lean_apply_m(f, n, as);
}
}\n"
def mk_apply_n (max : nat) : m unit :=
do emit "obj* apply_n(obj* f, unsigned n, obj** as) {\n",
emit "switch (n) {\n",
emit "case 0: lean_unreachable();\n",
max.mrepeat $ λ i,
let as := mk_as_args (i+1) in
emit $ sformat! "case {i+1}: return apply_{i+1}(f, {as});\n",
emit "default: return apply_m(f, n, as);\n",
emit "}\n",
emit "}\n"
def mkApplyM (max : Nat) : M Unit :=
emit s!"extern \"C\" LEAN_EXPORT obj* lean_apply_m(obj* f, unsigned n, obj** as) \{
lean_assert(n > {max});
if (lean_is_scalar(f)) \{ for (unsigned i = 0; i < n; i++) \{ lean_dec(as[i]); } return f; } // f is an erased proof
unsigned arity = lean_closure_arity(f);
unsigned fixed = lean_closure_num_fixed(f);
if (arity == fixed + n) \{
obj ** args = static_cast<obj**>(LEAN_ALLOCA(arity*sizeof(obj*))); // NOLINT
for (unsigned i = 0; i < fixed; i++) \{ lean_inc(fx(i)); args[i] = fx(i); }
for (unsigned i = 0; i < n; i++) args[fixed+i] = as[i];
obj * r = FNN(f)(args);
lean_dec_ref(f);
return r;
} else if (arity < fixed + n) \{
obj ** args = static_cast<obj**>(LEAN_ALLOCA(arity*sizeof(obj*))); // NOLINT
for (unsigned i = 0; i < fixed; i++) \{ lean_inc(fx(i)); args[i] = fx(i); }
for (unsigned i = 0; i < arity-fixed; i++) args[fixed+i] = as[i];
obj * new_f = FNN(f)(args);
lean_dec_ref(f);
return lean_apply_n(new_f, n+fixed-arity, &as[arity-fixed]);
} else \{
return fix_args(f, n, as);
}
}\n"
def mk_apply_m (max : nat) : m unit :=
do emit "obj* apply_m(obj* f, unsigned n, obj** as) {\n",
emit $ sformat! "lean_assert(n > {max});\n",
emit "unsigned arity = closure_arity(f);\n",
emit "unsigned fixed = closure_num_fixed(f);\n",
emit $ sformat! "if (arity == fixed + n) {{\n",
emit " obj ** args = static_cast<obj**>(LEAN_ALLOCA(arity*sizeof(obj*))); // NOLINT\n",
emit " for (unsigned i = 0; i < fixed; i++) { inc(fx(i)); args[i] = fx(i); }\n",
emit " for (unsigned i = 0; i < n; i++) args[fixed+i] = as[i];\n",
emit " obj * r = FNN(f)(args);\n",
emit " dec_ref(f);\n",
emit " return r;\n",
emit $ sformat! "} else if (arity < fixed + n) {{\n",
emit " obj ** args = static_cast<obj**>(LEAN_ALLOCA(arity*sizeof(obj*))); // NOLINT\n",
emit " for (unsigned i = 0; i < fixed; i++) { inc(fx(i)); args[i] = fx(i); }\n",
emit " for (unsigned i = 0; i < arity-fixed; i++) args[fixed+i] = as[i];\n",
emit " obj * new_f = FNN(f)(args);\n",
emit " dec_ref(f);\n",
emit " return apply_n(new_f, n+fixed-arity, as+arity-fixed);\n",
emit "} else {\n",
emit " return fix_args(f, n, as);\n",
emit "}\n",
emit "}\n"
def mkFixArgs : M Unit := emit "
def mk_fix_args : m unit :=
emit "
static obj* fix_args(obj* f, unsigned n, obj*const* as) {
unsigned arity = lean_closure_arity(f);
unsigned fixed = lean_closure_num_fixed(f);
unsigned arity = closure_arity(f);
unsigned fixed = closure_num_fixed(f);
unsigned new_fixed = fixed + n;
lean_assert(new_fixed < arity);
obj * r = lean_alloc_closure(lean_closure_fun(f), arity, new_fixed);
obj ** source = lean_closure_arg_cptr(f);
obj ** target = lean_closure_arg_cptr(r);
if (!lean_is_exclusive(f)) {
obj * r = alloc_closure(closure_fun(f), arity, new_fixed);
obj ** source = closure_arg_cptr(f);
obj ** target = closure_arg_cptr(r);
if (!is_exclusive(f)) {
for (unsigned i = 0; i < fixed; i++, source++, target++) {
*target = *source;
lean_inc(*target);
inc(*target);
}
lean_dec_ref(f);
dec_ref(f);
} else {
for (unsigned i = 0; i < fixed; i++, source++, target++) {
*target = *source;
}
lean_free_small_object(f);
free_closure_obj(f);
}
for (unsigned i = 0; i < n; i++, as++, target++) {
*target = *as;
@@ -170,7 +176,9 @@ static inline obj* fix_args(obj* f, std::initializer_list<obj*> const & l) {
}
"
def mkCopyright : M Unit := emit "/*
def mk_copyright : m unit :=
emit
"/*
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
@@ -178,25 +186,46 @@ Author: Leonardo de Moura
*/
"
def mkApplyCpp (max : Nat) : M Unit := do
mkCopyright
emit "// DO NOT EDIT, this is an automatically generated file
// Generated using script: ../../gen/apply.lean
#include \"runtime/apply.h\"
namespace lean {
#define obj lean_object
#define fx(i) lean_closure_arg_cptr(f)[i]\n"
mkFixArgs
for i in [0:max] do mkTypedefFn (i+1)
emit "typedef obj* (*fnn)(obj**); // NOLINT
#define FNN(f) reinterpret_cast<fnn>(lean_closure_fun(f))\n"
mkCurry max
emit "extern \"C\" obj* lean_apply_n(obj*, unsigned, obj**);\n"
for i in [0:max] do mkApplyI (i+1) max
mkApplyM max
mkApplyN max
emit "}\n"
def mk_apply_cpp (max : nat) : m unit :=
do mk_copyright,
emit "// DO NOT EDIT, this is an automatically generated file\n",
emit "// Generated using script: ../../gen/apply.lean\n",
emit "#include \"runtime/apply.h\"\n",
emit "namespace lean {\n",
emit "#define obj object\n",
emit "#define fx(i) closure_arg_cptr(f)[i]\n",
mk_fix_args,
max.mrepeat $ λ i, mk_typedef_fn (i+1),
emit "typedef obj* (*fnn)(obj**); // NOLINT\n",
emit "#define FNN(f) reinterpret_cast<fnn>(closure_fun(f))\n",
mk_curry max,
emit "obj* apply_n(obj*, unsigned, obj**);\n",
max.mrepeat $ λ i, do { mk_apply_i (i+1) max },
mk_apply_m max,
mk_apply_n max,
emit "}\n"
-- #eval (mkApplyCpp 4).run none
-- Make string: "object* a1, object* a2, ..., object** an"
def mk_arg_decls' (n : nat) : string :=
string.join $ (n.repeat (λ i r, r ++ [sformat! "object* a{i+1}"]) []).intersperse ", "
#eval (mkApplyCpp Lean.closureMaxArgs).run "src/runtime/apply.cpp"
def mk_apply_h (max : nat) : m unit :=
do mk_copyright,
emit "// DO NOT EDIT, this is an automatically generated file\n",
emit "// Generated using script: ../../gen/apply.lean\n",
emit "#pragma once\n",
emit "#include \"runtime/object.h\"\n",
emit $ sformat! "#define LEAN_CLOSURE_MAX_ARGS {max}"
emit "namespace lean {\n",
max.mrepeat $ λ i,
let args := mk_arg_decls' (i+1) in
emit $ sformat! "object* apply_{i+1}(object* f, {args});\n",
emit "object* apply_n(object* f, unsigned n, object** args);\n",
emit $ sformat! "// pre: n > {max}\n",
emit "object* apply_m(object* f, unsigned n, object** args);\n",
emit "}\n"
-- #eval (mk_apply_cpp 4).run none
#eval (mk_apply_cpp lean.closure_max_args).run "..//src//runtime//apply.cpp"
#eval (mk_apply_h lean.closure_max_args).run "..//src//runtime//apply.h"

View File

@@ -25,30 +25,19 @@ cp -L llvm/bin/llvm-ar stage1/bin/
# dependencies of the above
$CP llvm/lib/lib{clang-cpp,LLVM}*.so* stage1/lib/
$CP $ZLIB/lib/libz.so* stage1/lib/
# bundle libatomic (referenced by LLVM >= 15, and required by the lean executable to run)
$CP $GCC_LIB/lib/libatomic.so* stage1/lib/
find stage1 -type f -exec strip --strip-unneeded '{}' \; 2> /dev/null
# lean.h dependencies
$CP llvm/lib/clang/*/include/{std*,__std*,limits}.h stage1/include/clang
# ELF dependencies, must be put there for `--sysroot`
$CP $GLIBC/lib/*crt* llvm/lib/
$CP $GLIBC/lib/*crt* stage1/lib/
$CP $GLIBC/lib/crt* llvm/lib/
$CP $GLIBC/lib/crt* stage1/lib/
# runtime
(cd llvm; $CP --parents lib/clang/*/lib/*/{clang_rt.*.o,libclang_rt.builtins*} ../stage1)
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a stage1/lib/
# LLVM 15 appears to ship the dependencies in 'llvm/lib/<target-triple>/' and 'llvm/include/<target-triple>/'
# but clang-15 that we use to compile is linked against 'llvm/lib/' and 'llvm/include'
# https://github.com/llvm/llvm-project/issues/54955
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* llvm/lib/
$CP llvm-host/lib/*/lib{c++,c++abi,unwind}.* llvm-host/lib/
# libc++ headers are looked up in the host compiler's root, so copy over target-specific includes
$CP -r llvm/include/*-*-* llvm-host/include/
$CP llvm/lib/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a stage1/lib/
# glibc: use for linking (so Lean programs don't embed newer symbol versions), but not for running (because libc.so, librt.so, and ld.so must be compatible)!
$CP $GLIBC/lib/libc_nonshared.a stage1/lib/glibc
for f in $GLIBC/lib/lib{c,dl,m,rt,pthread}-*; do b=$(basename $f); cp $f stage1/lib/glibc/${b%-*}.so; done
OPTIONS=()
echo -n " -DLLVM=ON -DLLVM_CONFIG=$PWD/llvm-host/bin/llvm-config" # manually point to `llvm-config` location
echo -n " -DLEAN_STANDALONE=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:-}'"
@@ -56,7 +45,7 @@ echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter $GLIBC_DEV/incl
if [[ -L llvm-host ]]; then
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang"
else
echo -n " -DCMAKE_C_COMPILER=$PWD/llvm-host/bin/clang -DLEANC_OPTS='--sysroot $PWD/stage1 -resource-dir $PWD/stage1/lib/clang/15.0.1 ${EXTRA_FLAGS:-}'"
echo -n " -DCMAKE_C_COMPILER=$PWD/llvm-host/bin/clang -DLEANC_OPTS='--sysroot $PWD/stage1 -resource-dir $PWD/stage1/lib/clang/14.0.0 ${EXTRA_FLAGS:-}'"
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

View File

@@ -36,7 +36,6 @@ $CP llvm/lib/clang/*/include/{std*,__std*,limits}.h stage1/include/clang
cp $SDK/usr/lib/libSystem.tbd stage1/lib/libc
# use for linking, use system libs for running
gcp llvm/lib/lib{c++,c++abi,unwind}.dylib stage1/lib/libc
echo -n " -DLLVM=ON -DLLVM_CONFIG=$PWD/llvm-host/bin/llvm-config" # manually point to `llvm-config` location
echo -n " -DLEAN_STANDALONE=ON"
# do not change C++ compiler; libc++ etc. being system libraries means there's no danger of conflicts,
# and the custom clang++ outputs a myriad of warnings when consuming the SDK
@@ -47,7 +46,7 @@ if [[ -L llvm-host ]]; then
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'"
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp'"
else
echo -n " -DCMAKE_C_COMPILER=$PWD/llvm-host/bin/clang -DLEANC_OPTS='--sysroot $PWD/stage1 -resource-dir $PWD/stage1/lib/clang/15.0.1 ${EXTRA_FLAGS:-}'"
echo -n " -DCMAKE_C_COMPILER=$PWD/llvm-host/bin/clang -DLEANC_OPTS='--sysroot $PWD/stage1 -resource-dir $PWD/stage1/lib/clang/14.0.0 ${EXTRA_FLAGS:-}'"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'"
fi
echo -n " -DLEANC_INTERNAL_FLAGS='-nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang"

View File

@@ -32,11 +32,10 @@ cp /clang64/lib/{crtbegin,crtend,crt2,dllcrt2}.o stage1/lib/
(cd llvm; cp --parents lib/clang/*/lib/*/libclang_rt.builtins* ../stage1)
# further dependencies
cp /clang64/lib/lib{m,bcrypt,mingw32,moldname,mingwex,msvcrt,pthread,advapi32,shell32,user32,kernel32,ucrtbase}.* /clang64/lib/libgmp.a llvm/lib/lib{c++,c++abi,unwind}.a stage1/lib/
echo -n " -DLLVM=ON -DLLVM_CONFIG=$PWD/llvm/bin/llvm-config" # manually point to `llvm-config` location
echo -n " -DLEAN_STANDALONE=ON"
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang.exe -DCMAKE_C_COMPILER_WORKS=1 -DCMAKE_CXX_COMPILER=$PWD/llvm/bin/clang++.exe -DCMAKE_CXX_COMPILER_WORKS=1 -DLEAN_CXX_STDLIB='-lc++ -lc++abi'"
echo -n " -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_CXX_COMPILER=clang++"
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter /clang64/include/'"
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -isystem /clang64/include/ -isystem /clang64/x86_64-w64-mingw32/include/'"
echo -n " -DLEANC_INTERNAL_FLAGS='--sysroot ROOT -nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang.exe"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -static-libgcc -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -fuse-ld=lld'"
# when not using the above flags, link GMP dynamically/as usual

View File

@@ -17,7 +17,7 @@ partial def reprintCore : Syntax → Option Format
| Syntax.missing => none
| Syntax.atom _ val => val.trim
| Syntax.ident _ rawVal _ _ => rawVal.toString
| Syntax.node _ _ args =>
| Syntax.node _ kind args =>
match args.toList.filterMap reprintCore with
| [] => none
| [arg] => arg
@@ -29,7 +29,7 @@ def reprint (stx : Syntax) : Format :=
def printCommands (cmds : Syntax) : CoreM Unit := do
for cmd in getCommands cmds |>.run #[] |>.2 do
try
IO.println ( ppCommand cmd).pretty
IO.println ( ppCommand cmd).pretty
catch e =>
IO.println f!"/-\ncannot print: {← e.toMessageData.format}\n{reprint cmd}\n-/"
@@ -70,4 +70,4 @@ unsafe def main (args : List String) : IO Unit := do
let mut first := true
for {env, currNamespace, openDecls, ..} in moduleStx, stx in leadingUpdated do
if first then first := false else IO.print "\n"
let _ printCommands stx |>.toIO {fileName, fileMap := FileMap.ofString input, currNamespace, openDecls} {env}
let _ printCommands stx |>.toIO {currNamespace, openDecls} {env}

View File

@@ -2,17 +2,11 @@
set -euo pipefail
rm -r stage0 || true
# don't copy untracked files
for f in $(git ls-files src); do
[[ $f != src/lake && $f != src/Leanc.lean ]] || continue
if [[ $f == *.lean ]]; then
f=${f#src/}
f=${f%.lean}.c
mkdir -p $(dirname stage0/stdlib/$f)
cp ${CP_C_PARAMS:-} $CSRCS/$f stage0/stdlib/$f
else
mkdir -p $(dirname stage0/$f)
cp $f stage0/$f
fi
for pkg in Init Lean; do
# ensure deterministic ordering
c_files="$pkg.c $(cd src; find $pkg -name '*.lean' | sed 's/\.lean/.c/' | LC_ALL=C sort | tr '\n' ' ')"
for f in $c_files; do mkdir -p $(dirname stage0/stdlib/$f); cp ${CP_PARAMS:-} $CSRCS/$f stage0/stdlib/$f; done
done
# don't copy untracked crap
git ls-files -z src | xargs -0 -I '{}' bash -c '[ "{}" = "src/lake" ] || (mkdir -p `dirname stage0/{}` && cp {} stage0/{})'
git add stage0

View File

@@ -19,9 +19,7 @@ in { pkgs ? flakePkgs.nixpkgs, pkgsDist ? pkgs }:
GMP = pkgsDist.gmp.override { withStatic = true; };
GLIBC = pkgsDist.glibc;
GLIBC_DEV = pkgsDist.glibc.dev;
GCC_LIB = pkgsDist.gcc.cc.lib;
ZLIB = pkgsDist.zlib;
GDB = pkgsDist.gdb;
});
nix = flake.devShell.${builtins.currentSystem};
}

View File

@@ -146,9 +146,6 @@ if ((${MULTI_THREAD} MATCHES "ON") AND (${CMAKE_SYSTEM_NAME} MATCHES "Darwin"))
set(LEAN_EXTRA_MAKE_OPTS -s40000 ${LEAN_EXTRA_MAKE_OPTS})
endif ()
# We want explicit stack probes in huge Lean stack frames for robust stack overflow detection
string(APPEND LEANC_EXTRA_FLAGS " -fstack-clash-protection")
if(NOT MULTI_THREAD)
message(STATUS "Disabled multi-thread support, it will not be safe to run multiple threads in parallel")
set(AUTO_THREAD_FINALIZATION OFF)
@@ -160,6 +157,19 @@ if(AUTO_THREAD_FINALIZATION AND NOT MSVC)
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_AUTO_THREAD_FINALIZATION")
endif()
if(LLVM)
if(NOT (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_ID STREQUAL "Clang"))
message(FATAL_ERROR "LLVM=ON must be used with clang. Set `CMAKE_CXX_COMPILER` and `CMAKE_C_COMPILER` accordingly.")
endif()
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
include_directories(${LLVM_INCLUDE_DIRS})
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_LLVM")
else()
#message(WARNING "Disabling LLVM support. JIT compilation will not be available")
endif()
# Set Module Path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
@@ -253,48 +263,9 @@ find_package(PythonInterp)
include_directories(${CMAKE_BINARY_DIR}/include)
# Pick up `llvm-config` to setup LLVM flags.
if(LLVM)
if(NOT LLVM_CONFIG) # look for `llvm-config` if not supplied
message(STATUS "'-DLLVM_CONFIG=<path/to/llvm-config>' not passed as CMake flag, finding `llvm-config` via CMake...")
find_program(LLVM_CONFIG "llvm-config")
if(NOT LLVM_CONFIG) # check that it was found
message(FATAL_ERROR "Unable to find 'llvm-config'")
endif()
endif()
# check that we have 'llvm-config' version.
message(STATUS "Executing 'llvm-config --version' at '${LLVM_CONFIG}' to check configuration.")
execute_process(COMMAND ${LLVM_CONFIG} --version COMMAND_ERROR_IS_FATAL ANY OUTPUT_VARIABLE LLVM_CONFIG_VERSION ECHO_OUTPUT_VARIABLE OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REGEX MATCH "^[0-9]*" LLVM_CONFIG_MAJOR_VERSION ${LLVM_CONFIG_VERSION})
message(STATUS "Found 'llvm-config' at '${LLVM_CONFIG}' with version '${LLVM_CONFIG_VERSION}', major version '${LLVM_CONFIG_MAJOR_VERSION}'")
if (NOT LLVM_CONFIG_MAJOR_VERSION STREQUAL "15")
message(FATAL_ERROR "Unable to find llvm-config version 15. Found invalid version '${LLVM_CONFIG_MAJOR_VERSION}'")
endif()
# -DLEAN_LLVM is used to conditionally compile Lean features that depend on LLVM
string(APPEND CMAKE_CXX_FLAGS " -D LEAN_LLVM")
execute_process(COMMAND ${LLVM_CONFIG} --ldflags OUTPUT_VARIABLE LLVM_CONFIG_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${LLVM_CONFIG} --libs OUTPUT_VARIABLE LLVM_CONFIG_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${LLVM_CONFIG} --libdir OUTPUT_VARIABLE LLVM_CONFIG_LIBDIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${LLVM_CONFIG} --includedir OUTPUT_VARIABLE LLVM_CONFIG_INCLUDEDIR OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${LLVM_CONFIG} --cxxflags OUTPUT_VARIABLE LLVM_CONFIG_CXXFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${LLVM_CONFIG} --system-libs OUTPUT_VARIABLE LLVM_CONFIG_SYSTEM_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "llvm-config: libdir '${LLVM_CONFIG_LIBDIR}' | ldflags '${LLVM_CONFIG_LDFLAGS}' | libs '${LLVM_CONFIG_LIBS}' | system libs '${LLVM_CONFIG_SYSTEM_LIBS}' | cxxflags: ${LLVM_CONFIG_CXXFLAGS} | includedir: ${LLVM_CONFIG_INCLUDEDIR}")
else()
message(WARNING "Disabling LLVM support")
endif()
# libleancpp/Lean as well as libleanrt/Init are cyclically dependent. This works by default on macOS, which also doesn't like
# the linker flags necessary on other platforms.
# Furthermore, add LLVM flags into the list of flags we need when linking.
# when using shared libraries, set the `rpath` to help the dynamic loader
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if(LLVM) # link to Zlib because LLVM depends on it.
find_package(ZLIB REQUIRED)
message(STATUS "ZLIB_LIBRARY: ${ZLIB_LIBRARY}")
cmake_path(GET ZLIB_LIBRARY PARENT_PATH ZLIB_LIBRARY_PARENT_PATH)
string(APPEND LEAN_EXTRA_LINKER_FLAGS " -L ${ZLIB_LIBRARY_PARENT_PATH}")
endif()
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
@@ -311,27 +282,6 @@ endif()
string(APPEND LEANC_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
string(APPEND LEANSHARED_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
if(LLVM)
# Here, we perform a replacement of `llvm-host` with `llvm`. This is necessary for our cross-compile
# builds in `script/prepare-llvm-*.sh`.
# - Recall that the host's copy of LLVM binaries and libraries is at
# `llvm-host`, and the target's copy of LLVM binaries and libraries is at
# `llvm`.
# - In an ideal world, we would run the target's `llvm/bin/llvm-config` and get the corrct link options for the target
# (e.g. `-Lllvm/lib/libLLVM`.)
# - However, the target's `llvm/bin/llvm-config` has a different target
# triple from the host, and thus cannot be run on the host.
# - So, we run the host `llvm-host/bin/llvm-config` from which we pick up
# compiler options, and change the output of the host to point to the target.
# - In particular, `host/bin/llvm-config` produces flags like `-Lllvm-host/lib/libLLVM`, while
# we need the path to be `-Lllvm/lib/libLLVM`. Thus, we perform this replacement here.
string(APPEND LEANSHARED_LINKER_FLAGS " -L${LLVM_CONFIG_LIBDIR} ${LLVM_CONFIG_LDFLAGS} ${LLVM_CONFIG_LIBS} ${LLVM_CONFIG_SYSTEM_LIBS}")
string(REPLACE "llvm-host" "llvm" LEANSHARED_LINKER_FLAGS ${LEANSHARED_LINKER_FLAGS})
string(APPEND CMAKE_CXX_FLAGS " -I${LLVM_CONFIG_INCLUDEDIR}")
string(REPLACE "llvm-host" "llvm" LEAN_EXTRA_CXX_FLAGS ${LEAN_EXTRA_CXX_FLAGS})
message(VERBOSE "leanshared linker flags: '${LEANSHARED_LINKER_FLAGS}' | lean extra cxx flags '${LEAN_EXTR_CXX_FLAGS}'")
endif()
# get rid of unused parts of C++ stdlib
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,-dead_strip")
@@ -340,7 +290,8 @@ else()
endif()
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND LEAN_EXTRA_LINKER_FLAGS " -lm")
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lm")
string(APPEND LEANSHARED_LINKER_FLAGS " -lm")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
@@ -465,11 +416,6 @@ if(${STAGE} GREATER 1)
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleanrt.a" "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a"
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleancpp.a" "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a")
add_dependencies(leancpp copy-leancpp)
if(LLVM)
add_custom_target(copy-lean-h-bc
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/lean.h.bc" "${CMAKE_BINARY_DIR}/lib/lean/lean.h.bc")
add_dependencies(leancpp copy-lean-h-bc)
endif()
else()
add_subdirectory(runtime)

View File

@@ -126,7 +126,7 @@ theorem byContradiction {p : Prop} (h : ¬p → False) : p :=
/--
`by_cases (h :)? p` splits the main goal into two cases, assuming `h : p` in the first branch, and `h : ¬ p` in the second branch.
-/
syntax "by_cases " (atomic(ident " : "))? term : tactic
syntax "by_cases" (atomic(ident ":"))? term : tactic
macro_rules
| `(tactic| by_cases $h : $e) =>

View File

@@ -34,7 +34,6 @@ which is not a type error anymore.
You can also use the `↑` operator to explicitly indicate a coercion. Using `↑x`
instead of `x` in the example will result in the same output.
Because there are many polymorphic functions in Lean, it is often ambiguous where
the coercion can go. For example:
```
@@ -48,44 +47,10 @@ between these possibilities, but generally Lean will elaborate working from the
and assign the `+` to be the one for `Int`, and then need to insert coercions
for the subterms `↑x : Int` and `↑y : Int`, resulting in the `↑x + ↑y` version.
Note that unlike most operators like `+`, `↑` is always eagerly unfolded at
parse time into its definition. So if we look at the definition of `f` from
before, we see no trace of the `CoeT.coe` function:
```
def f (x : Nat) : Int := x
#print f
-- def f : Nat → Int :=
-- fun (x : Nat) => Int.ofNat x
```
## Important typeclasses
Lean resolves a coercion by either inserting a `CoeDep` instance
or chaining `CoeHead? CoeOut* Coe* CoeTail?` instances.
(That is, zero or one `CoeHead` instances, an arbitrary number of `CoeOut`
instances, etc.)
The `CoeHead? CoeOut*` instances are chained from the "left" side.
So if Lean looks for a coercion from `Nat` to `Int`, it starts by trying coerce
`Nat` using `CoeHead` by looking for a `CoeHead Nat ?α` instance, and then
continuing with `CoeOut`. Similarly `Coe* CoeTail?` are chained from the "right".
These classes should be implemented for coercions:
* `Coe α β` is the most basic class, and the usual one you will want to use
when implementing a coercion for your own types.
The variables in the type `α` must be a subset of the variables in `β`
(or out-params of type class parameters),
because `Coe` is chained right-to-left.
* `CoeOut α β` is like `Coe α β` but chained left-to-right.
Use this if the variables in the type `α` are a superset of the variables in `β`.
* `CoeTail α β` is like `Coe α β`, but only applied once.
Use this for coercions that would cause loops, like `[Ring R] → CoeTail Nat R`.
* `CoeHead α β` is similar to `CoeOut α β`, but only applied once.
Use this for coercions that would cause loops, like `[SetLike S α] → CoeHead S (Set α)`.
* `CoeDep α (x : α) β` allows `β` to depend not only on `α` but on the value
`x : α` itself. This is useful when the coercion function is dependent.
@@ -98,21 +63,32 @@ These classes should be implemented for coercions:
* `CoeFun α (γ : α → Sort v)` is a coercion to a function. `γ a` should be a
(coercion-to-)function type, and this is triggered whenever an element
`f : α` appears in an application like `f x` which would not make sense since
`f` does not have a function type.
`CoeFun` instances apply to `CoeOut` as well.
`f` does not have a function type. This is automatically turned into `CoeFun.coe f x`.
* `CoeSort α β` is a coercion to a sort. `β` must be a universe, and this is
triggered when `a : α` appears in a place where a type is expected, like
`(x : a)` or `a → a`.
`CoeSort` instances apply to `CoeOut` as well.
* `CoeSort α β` is a coercion to a sort. `β` must be a universe, and if
`a : α` appears in a place where a type is expected, like `(x : a)` or `a → a`,
then it will be turned into `(x : CoeSort.coe a)`.
On top of these instances this file defines several auxiliary type classes:
* `CoeTC := Coe*`
* `CoeOTC := CoeOut* Coe*`
* `CoeHTC := CoeHead? CoeOut* Coe*`
* `CoeHTCT := CoeHead? CoeOut* Coe* CoeTail?`
* `CoeDep := CoeHead? CoeOut* Coe* CoeTail? | CoeDep`
* `CoeHead` is like `Coe`, but while `Coe` can be transitively chained in the
`CoeT` class, `CoeHead` can only appear once and only at the start of such a
chain. This is useful when the transitive instances are not well behaved.
* `CoeTail` is similar: it can only appear at the end of a chain of coercions.
* `CoeT α (x : α) β` itself is the combination of all the aforementioned classes
(except `CoeSort` and `CoeFun` which have different triggers). You can
implement `CoeT` if you do not want this coercion to be transitively composed
with any other coercions.
Note that unlike most operators like `+`, `↑` is always eagerly unfolded at
parse time into its definition. So if we look at the definition of `f` from
before, we see no trace of the `CoeT.coe` function:
```
def f (x : Nat) : Int := x
#print f
-- def f : Nat → Int :=
-- fun (x : Nat) => Int.ofNat x
```
-/
universe u v w w'
@@ -123,101 +99,50 @@ chained with other `Coe` instances, and coercion is automatically used when
`x` has type `α` but it is used in a context where `β` is expected.
You can use the `↑x` operator to explicitly trigger coercion.
-/
class Coe (α : semiOutParam (Sort u)) (β : Sort v) where
class Coe (α : Sort u) (β : Sort v) where
/-- Coerces a value of type `α` to type `β`. Accessible by the notation `↑x`,
or by double type ascription `((x : α) : β)`. -/
coe : α β
attribute [coe_decl] Coe.coe
/--
Auxiliary class implementing `Coe*`.
Auxiliary class that contains the transitive closure of `Coe`.
Users should generally not implement this directly.
-/
class CoeTC (α : Sort u) (β : Sort v) where
/-- Coerces a value of type `α` to type `β`. Accessible by the notation `↑x`,
or by double type ascription `((x : α) : β)`. -/
coe : α β
attribute [coe_decl] CoeTC.coe
instance [Coe β γ] [CoeTC α β] : CoeTC α γ where coe a := Coe.coe (CoeTC.coe a : β)
instance [Coe α β] : CoeTC α β where coe a := Coe.coe a
instance : CoeTC α α where coe a := a
/--
`CoeOut α β` is for coercions that are applied from left-to-right.
`CoeHead α β` is for coercions that can only appear at the beginning of a
sequence of coercions. That is, `β` can be further coerced via `Coe β γ` and
`CoeTail γ δ` instances but `α` will only be the inferred type of the input.
-/
class CoeOut (α : Sort u) (β : semiOutParam (Sort v)) where
class CoeHead (α : Sort u) (β : Sort v) where
/-- Coerces a value of type `α` to type `β`. Accessible by the notation `↑x`,
or by double type ascription `((x : α) : β)`. -/
coe : α β
attribute [coe_decl] CoeOut.coe
/--
Auxiliary class implementing `CoeOut* Coe*`.
Users should generally not implement this directly.
-/
class CoeOTC (α : Sort u) (β : Sort v) where
/-- Coerces a value of type `α` to type `β`. Accessible by the notation `↑x`,
or by double type ascription `((x : α) : β)`. -/
coe : α β
attribute [coe_decl] CoeOTC.coe
instance [CoeOut α β] [CoeOTC β γ] : CoeOTC α γ where coe a := CoeOTC.coe (CoeOut.coe a : β)
instance [CoeTC α β] : CoeOTC α β where coe a := CoeTC.coe a
instance : CoeOTC α α where coe a := a
-- Note: ^^ We add reflexivity instances for CoeOTC/etc. so that we avoid going
-- through a user-defined CoeTC/etc. instance. (Instances like
-- `CoeTC F (A →+ B)` apply even when the two sides are defeq.)
/--
`CoeHead α β` is for coercions that are applied from left-to-right at most once
at beginning of the coercion chain.
-/
class CoeHead (α : Sort u) (β : semiOutParam (Sort v)) where
/-- Coerces a value of type `α` to type `β`. Accessible by the notation `↑x`,
or by double type ascription `((x : α) : β)`. -/
coe : α β
attribute [coe_decl] CoeHead.coe
/--
Auxiliary class implementing `CoeHead CoeOut* Coe*`.
Users should generally not implement this directly.
-/
class CoeHTC (α : Sort u) (β : Sort v) where
/-- Coerces a value of type `α` to type `β`. Accessible by the notation `↑x`,
or by double type ascription `((x : α) : β)`. -/
coe : α β
attribute [coe_decl] CoeHTC.coe
instance [CoeHead α β] [CoeOTC β γ] : CoeHTC α γ where coe a := CoeOTC.coe (CoeHead.coe a : β)
instance [CoeOTC α β] : CoeHTC α β where coe a := CoeOTC.coe a
instance : CoeHTC α α where coe a := a
/--
`CoeTail α β` is for coercions that can only appear at the end of a
sequence of coercions. That is, `α` can be further coerced via `Coe σ α` and
`CoeHead τ σ` instances but `β` will only be the expected type of the expression.
-/
class CoeTail (α : semiOutParam (Sort u)) (β : Sort v) where
class CoeTail (α : Sort u) (β : Sort v) where
/-- Coerces a value of type `α` to type `β`. Accessible by the notation `↑x`,
or by double type ascription `((x : α) : β)`. -/
coe : α β
attribute [coe_decl] CoeTail.coe
/--
Auxiliary class implementing `CoeHead* Coe* CoeTail?`.
Users should generally not implement this directly.
Auxiliary class that contains `CoeHead` + `CoeTC` + `CoeTail`.
A `CoeHTCT` chain has the "grammar" `(CoeHead)? (Coe)* (CoeTail)?`, except that
the empty sequence is not allowed.
-/
class CoeHTCT (α : Sort u) (β : Sort v) where
/-- Coerces a value of type `α` to type `β`. Accessible by the notation `↑x`,
or by double type ascription `((x : α) : β)`. -/
coe : α β
attribute [coe_decl] CoeHTCT.coe
instance [CoeTail β γ] [CoeHTC α β] : CoeHTCT α γ where coe a := CoeTail.coe (CoeHTC.coe a : β)
instance [CoeHTC α β] : CoeHTCT α β where coe a := CoeHTC.coe a
instance : CoeHTCT α α where coe a := a
/--
`CoeDep α (x : α) β` is a typeclass for dependent coercions, that is, the type `β`
@@ -232,54 +157,43 @@ class CoeDep (α : Sort u) (_ : α) (β : Sort v) where
the type class, so the value of type `β` may possibly depend on additional
typeclasses on `x`. -/
coe : β
attribute [coe_decl] CoeDep.coe
/--
`CoeT` is the core typeclass which is invoked by Lean to resolve a type error.
It can also be triggered explicitly with the notation `↑x` or by double type
ascription `((x : α) : β)`.
A `CoeT` chain has the grammar `CoeHead? CoeOut* Coe* CoeTail? | CoeDep`.
A `CoeT` chain has the "grammar" `(CoeHead)? (Coe)* (CoeTail)? | CoeDep`,
except that the empty sequence is not allowed (identity coercions don't need
the coercion system at all).
-/
class CoeT (α : Sort u) (_ : α) (β : Sort v) where
/-- The resulting value of type `β`. The input `x : α` is a parameter to
the type class, so the value of type `β` may possibly depend on additional
typeclasses on `x`. -/
coe : β
attribute [coe_decl] CoeT.coe
instance [CoeHTCT α β] : CoeT α a β where coe := CoeHTCT.coe a
instance [CoeDep α a β] : CoeT α a β where coe := CoeDep.coe a
instance : CoeT α a α where coe := a
/--
`CoeFun α (γ : α → Sort v)` is a coercion to a function. `γ a` should be a
(coercion-to-)function type, and this is triggered whenever an element
`f : α` appears in an application like `f x`, which would not make sense since
`f` does not have a function type.
`CoeFun` instances apply to `CoeOut` as well.
`f : α` appears in an application like `f x` which would not make sense since
`f` does not have a function type. This is automatically turned into `CoeFun.coe f x`.
-/
class CoeFun (α : Sort u) (γ : outParam (α Sort v)) where
/-- Coerces a value `f : α` to type `γ f`, which should be either be a
function type or another `CoeFun` type, in order to resolve a mistyped
application `f x`. -/
coe : (f : α) γ f
attribute [coe_decl] CoeFun.coe
instance [CoeFun α fun _ => β] : CoeOut α β where coe a := CoeFun.coe a
/--
`CoeSort α β` is a coercion to a sort. `β` must be a universe, and this is
triggered when `a : α` appears in a place where a type is expected, like
`(x : a)` or `a → a`.
`CoeSort` instances apply to `CoeOut` as well.
`CoeSort α β` is a coercion to a sort. `β` must be a universe, and if
`a : α` appears in a place where a type is expected, like `(x : a)` or `a → a`,
then it will be turned into `(x : CoeSort.coe a)`.
-/
class CoeSort (α : Sort u) (β : outParam (Sort v)) where
/-- Coerces a value of type `α` to `β`, which must be a universe. -/
coe : α β
attribute [coe_decl] CoeSort.coe
instance [CoeSort α β] : CoeOut α β where coe a := CoeSort.coe a
/--
`↑x` represents a coercion, which converts `x` of type `α` to type `β`, using
@@ -290,21 +204,60 @@ between e.g. `↑x + ↑y` and `↑(x + y)`.
-/
syntax:1024 (name := coeNotation) "" term:1024 : term
instance coeTrans {α : Sort u} {β : Sort v} {δ : Sort w} [Coe β δ] [CoeTC α β] : CoeTC α δ where
coe a := Coe.coe (CoeTC.coe a : β)
instance coeBase {α : Sort u} {β : Sort v} [Coe α β] : CoeTC α β where
coe a := Coe.coe a
instance coeOfHeafOfTCOfTail {α : Sort u} {β : Sort v} {δ : Sort w} {γ : Sort w'} [CoeHead α β] [CoeTail δ γ] [CoeTC β δ] : CoeHTCT α γ where
coe a := CoeTail.coe (CoeTC.coe (CoeHead.coe a : β) : δ)
instance coeOfHeadOfTC {α : Sort u} {β : Sort v} {δ : Sort w} [CoeHead α β] [CoeTC β δ] : CoeHTCT α δ where
coe a := CoeTC.coe (CoeHead.coe a : β)
instance coeOfTCOfTail {α : Sort u} {β : Sort v} {δ : Sort w} [CoeTail β δ] [CoeTC α β] : CoeHTCT α δ where
coe a := CoeTail.coe (CoeTC.coe a : β)
instance coeOfHeadOfTail {α : Sort u} {β : Sort v} {γ : Sort w} [CoeHead α β] [CoeTail β γ] : CoeHTCT α γ where
coe a := CoeTail.coe (CoeHead.coe a : β)
instance coeOfHead {α : Sort u} {β : Sort v} [CoeHead α β] : CoeHTCT α β where
coe a := CoeHead.coe a
instance coeOfTail {α : Sort u} {β : Sort v} [CoeTail α β] : CoeHTCT α β where
coe a := CoeTail.coe a
instance coeOfTC {α : Sort u} {β : Sort v} [CoeTC α β] : CoeHTCT α β where
coe a := CoeTC.coe a
instance coeOfHTCT {α : Sort u} {β : Sort v} [CoeHTCT α β] (a : α) : CoeT α a β where
coe := CoeHTCT.coe a
instance coeOfDep {α : Sort u} {β : Sort v} (a : α) [CoeDep α a β] : CoeT α a β where
coe := CoeDep.coe a
instance coeId {α : Sort u} (a : α) : CoeT α a α where
coe := a
instance coeSortToCoeTail [inst : CoeSort α β] : CoeTail α β where
coe := inst.coe
/-! # Basic instances -/
instance boolToProp : Coe Bool Prop where
@[inline] instance boolToProp : Coe Bool Prop where
coe b := Eq b true
instance boolToSort : CoeSort Bool Prop where
coe b := b
coe b := Eq b true
instance decPropToBool (p : Prop) [Decidable p] : CoeDep Prop p Bool where
coe := decide p
instance optionCoe {α : Type u} : Coe α (Option α) where
instance optionCoe {α : Type u} : CoeTail α (Option α) where
coe := some
instance subtypeCoe {α : Sort u} {p : α Prop} : CoeOut (Subtype p) α where
instance subtypeCoe {α : Sort u} {p : α Prop} : CoeHead (Subtype p) α where
coe v := v.val
/-! # Coe bridge -/
@@ -315,7 +268,7 @@ Helper definition used by the elaborator. It is not meant to be used directly by
This is used for coercions between monads, in the case where we want to apply
a monad lift and a coercion on the result type at the same time.
-/
@[inline, coe_decl] def Lean.Internal.liftCoeM {m : Type u Type v} {n : Type u Type w} {α β : Type u}
@[inline] def Lean.Internal.liftCoeM {m : Type u Type v} {n : Type u Type w} {α β : Type u}
[MonadLiftT m n] [ a, CoeT α a β] [Monad n] (x : m α) : n β := do
let a liftM x
pure (CoeT.coe a)
@@ -325,7 +278,16 @@ Helper definition used by the elaborator. It is not meant to be used directly by
This is used for coercing the result type under a monad.
-/
@[inline, coe_decl] def Lean.Internal.coeM {m : Type u Type v} {α β : Type u}
@[inline] def Lean.Internal.coeM {m : Type u Type v} {α β : Type u}
[ a, CoeT α a β] [Monad m] (x : m α) : m β := do
let a x
pure (CoeT.coe a)
instance [CoeFun α β] (a : α) : CoeDep α a (β a) where
coe := CoeFun.coe a
instance [CoeFun α (fun _ => β)] : CoeTail α β where
coe a := CoeFun.coe a
instance [CoeSort α β] : CoeTail α β where
coe a := CoeSort.coe a

View File

@@ -8,14 +8,12 @@ import Init.Core
universe u v w
@[reducible]
def Functor.mapRev {f : Type u Type v} [Functor f] {α β : Type u} : f α (α β) f β :=
@[reducible] def Functor.mapRev {f : Type u Type v} [Functor f] {α β : Type u} : f α (α β) f β :=
fun a f => f <$> a
infixr:100 " <&> " => Functor.mapRev
@[always_inline, inline]
def Functor.discard {f : Type u Type v} {α : Type u} [Functor f] (x : f α) : f PUnit :=
@[inline] def Functor.discard {f : Type u Type v} {α : Type u} [Functor f] (x : f α) : f PUnit :=
Functor.mapConst PUnit.unit x
export Functor (discard)
@@ -30,10 +28,10 @@ variable {f : Type u → Type v} [Alternative f] {α : Type u}
export Alternative (failure)
@[always_inline, inline] def guard {f : Type Type v} [Alternative f] (p : Prop) [Decidable p] : f Unit :=
@[inline] def guard {f : Type Type v} [Alternative f] (p : Prop) [Decidable p] : f Unit :=
if p then pure () else failure
@[always_inline, inline] def optional (x : f α) : f (Option α) :=
@[inline] def optional (x : f α) : f (Option α) :=
some <$> x <|> pure none
class ToBool (α : Type u) where
@@ -44,12 +42,12 @@ export ToBool (toBool)
instance : ToBool Bool where
toBool b := b
@[macro_inline] def bool {β : Type u} {α : Type v} [ToBool β] (f t : α) (b : β) : α :=
@[macroInline] def bool {β : Type u} {α : Type v} [ToBool β] (f t : α) (b : β) : α :=
match toBool b with
| true => t
| false => f
@[macro_inline] def orM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
@[macroInline] def orM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
let b x
match toBool b with
| true => pure b
@@ -57,7 +55,7 @@ instance : ToBool Bool where
infixr:30 " <||> " => orM
@[macro_inline] def andM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
@[macroInline] def andM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
let b x
match toBool b with
| true => y
@@ -65,7 +63,7 @@ infixr:30 " <||> " => orM
infixr:35 " <&&> " => andM
@[macro_inline] def notM {m : Type Type v} [Applicative m] (x : m Bool) : m Bool :=
@[macroInline] def notM {m : Type Type v} [Applicative m] (x : m Bool) : m Bool :=
not <$> x
/-!
@@ -186,7 +184,7 @@ This is the same as [`MonadBaseControl`](https://hackage.haskell.org/package/mon
To learn about `MonadControl`, see the comment above this docstring.
-/
class MonadControl (m : semiOutParam (Type u Type v)) (n : Type u Type w) where
class MonadControl (m : Type u Type v) (n : Type u Type w) where
stM : Type u Type u
liftWith : {α : Type u} (({β : Type u} n β m (stM β)) m α) n α
restoreM : {α : Type u} m (stM α) n α
@@ -199,7 +197,6 @@ class MonadControlT (m : Type u → Type v) (n : Type u → Type w) where
export MonadControlT (stM liftWith restoreM)
@[always_inline]
instance (m n o) [MonadControl n o] [MonadControlT m n] : MonadControlT m o where
stM α := stM m n (MonadControl.stM n o α)
liftWith f := MonadControl.liftWith fun x₂ => liftWith fun x₁ => f (x₁ x₂)
@@ -210,12 +207,12 @@ instance (m : Type u → Type v) [Pure m] : MonadControlT m m where
liftWith f := f fun x => x
restoreM x := pure x
@[always_inline, inline]
@[inline]
def controlAt (m : Type u Type v) {n : Type u Type w} [MonadControlT m n] [Bind n] {α : Type u}
(f : ({β : Type u} n β m (stM m n β)) m (stM m n α)) : n α :=
liftWith f >>= restoreM
@[always_inline, inline]
@[inline]
def control {m : Type u Type v} {n : Type u Type w} [MonadControlT m n] [Bind n] {α : Type u}
(f : ({β : Type u} n β m (stM m n β)) m (stM m n α)) : n α :=
controlAt m f
@@ -231,24 +228,3 @@ class ForM (m : Type u → Type v) (γ : Type w₁) (α : outParam (Type w₂))
forM [Monad m] : γ (α m PUnit) m PUnit
export ForM (forM)
/-- Left-to-right composition of Kleisli arrows. -/
@[always_inline]
def Bind.kleisliRight [Bind m] (f₁ : α m β) (f₂ : β m γ) (a : α) : m γ :=
f₁ a >>= f₂
/-- Right-to-left composition of Kleisli arrows. -/
@[always_inline]
def Bind.kleisliLeft [Bind m] (f₂ : β m γ) (f₁ : α m β) (a : α) : m γ :=
f₁ a >>= f₂
/-- Same as `Bind.bind` but with arguments swapped. -/
@[always_inline]
def Bind.bindLeft [Bind m] (f : α m β) (ma : m α) : m β :=
ma >>= f
-- Precedence choice taken to be the same as in haskell:
-- https://hackage.haskell.org/package/base-4.17.0.0/docs/Control-Monad.html#v:-61--60--60-
@[inherit_doc] infixr:55 " >=> " => Bind.kleisliRight
@[inherit_doc] infixr:55 " <=< " => Bind.kleisliLeft
@[inherit_doc] infixr:55 " =<< " => Bind.bindLeft

View File

@@ -31,8 +31,7 @@ variable {ε σ α β : Type u}
/-- Alternative orElse operator that allows to select which exception should be used.
The default is to use the first exception since the standard `orElse` uses the second. -/
@[always_inline, inline]
protected def orElse' {δ} [Backtrackable δ σ] (x₁ x₂ : EStateM ε σ α) (useFirstEx := true) : EStateM ε σ α := fun s =>
@[inline] protected def orElse' {δ} [Backtrackable δ σ] (x₁ x₂ : EStateM ε σ α) (useFirstEx := true) : EStateM ε σ α := fun s =>
let d := Backtrackable.save s;
match x₁ s with
| Result.error e₁ s₁ =>
@@ -41,7 +40,6 @@ protected def orElse' {δ} [Backtrackable δ σ] (x₁ x₂ : EStateM ε σ α)
| ok => ok
| ok => ok
@[always_inline]
instance : MonadFinally (EStateM ε σ) := {
tryFinally' := fun x h s =>
let r := x s
@@ -54,7 +52,7 @@ instance : MonadFinally (EStateM ε σ) := {
| Result.error e₂ s => Result.error e₂ s
}
@[always_inline, inline] def fromStateM {ε σ α : Type} (x : StateM σ α) : EStateM ε σ α := fun s =>
@[inline] def fromStateM {ε σ α : Type} (x : StateM σ α) : EStateM ε σ α := fun s =>
match x.run s with
| (a, s') => EStateM.Result.ok a s'

View File

@@ -13,12 +13,10 @@ import Init.Coe
namespace Except
variable {ε : Type u}
@[always_inline, inline]
protected def pure (a : α) : Except ε α :=
@[inline] protected def pure (a : α) : Except ε α :=
Except.ok a
@[always_inline, inline]
protected def map (f : α β) : Except ε α Except ε β
@[inline] protected def map (f : α β) : Except ε α Except ε β
| Except.error err => Except.error err
| Except.ok v => Except.ok <| f v
@@ -27,32 +25,27 @@ protected def map (f : α → β) : Except ε α → Except ε β
intro e
simp [Except.map]; cases e <;> rfl
@[always_inline, inline]
protected def mapError (f : ε ε') : Except ε α Except ε' α
@[inline] protected def mapError (f : ε ε') : Except ε α Except ε' α
| Except.error err => Except.error <| f err
| Except.ok v => Except.ok v
@[always_inline, inline]
protected def bind (ma : Except ε α) (f : α Except ε β) : Except ε β :=
@[inline] protected def bind (ma : Except ε α) (f : α Except ε β) : Except ε β :=
match ma with
| Except.error err => Except.error err
| Except.ok v => f v
/-- Returns true if the value is `Except.ok`, false otherwise. -/
@[always_inline, inline]
protected def toBool : Except ε α Bool
@[inline] protected def toBool : Except ε α Bool
| Except.ok _ => true
| Except.error _ => false
abbrev isOk : Except ε α Bool := Except.toBool
@[always_inline, inline]
protected def toOption : Except ε α Option α
@[inline] protected def toOption : Except ε α Option α
| Except.ok a => some a
| Except.error _ => none
@[always_inline, inline]
protected def tryCatch (ma : Except ε α) (handle : ε Except ε α) : Except ε α :=
@[inline] protected def tryCatch (ma : Except ε α) (handle : ε Except ε α) : Except ε α :=
match ma with
| Except.ok a => Except.ok a
| Except.error e => handle e
@@ -62,7 +55,6 @@ def orElseLazy (x : Except ε α) (y : Unit → Except ε α) : Except ε α :=
| Except.ok a => Except.ok a
| Except.error _ => y ()
@[always_inline]
instance : Monad (Except ε) where
pure := Except.pure
bind := Except.bind
@@ -73,69 +65,55 @@ end Except
def ExceptT (ε : Type u) (m : Type u Type v) (α : Type u) : Type v :=
m (Except ε α)
@[always_inline, inline]
def ExceptT.mk {ε : Type u} {m : Type u Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x
@[always_inline, inline]
def ExceptT.run {ε : Type u} {m : Type u Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
@[inline] def ExceptT.mk {ε : Type u} {m : Type u Type v} {α : Type u} (x : m (Except ε α)) : ExceptT ε m α := x
@[inline] def ExceptT.run {ε : Type u} {m : Type u Type v} {α : Type u} (x : ExceptT ε m α) : m (Except ε α) := x
namespace ExceptT
variable {ε : Type u} {m : Type u Type v} [Monad m]
@[always_inline, inline]
protected def pure {α : Type u} (a : α) : ExceptT ε m α :=
@[inline] protected def pure {α : Type u} (a : α) : ExceptT ε m α :=
ExceptT.mk <| pure (Except.ok a)
@[always_inline, inline]
protected def bindCont {α β : Type u} (f : α ExceptT ε m β) : Except ε α m (Except ε β)
@[inline] protected def bindCont {α β : Type u} (f : α ExceptT ε m β) : Except ε α m (Except ε β)
| Except.ok a => f a
| Except.error e => pure (Except.error e)
@[always_inline, inline]
protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α ExceptT ε m β) : ExceptT ε m β :=
@[inline] protected def bind {α β : Type u} (ma : ExceptT ε m α) (f : α ExceptT ε m β) : ExceptT ε m β :=
ExceptT.mk <| ma >>= ExceptT.bindCont f
@[always_inline, inline]
protected def map {α β : Type u} (f : α β) (x : ExceptT ε m α) : ExceptT ε m β :=
@[inline] protected def map {α β : Type u} (f : α β) (x : ExceptT ε m α) : ExceptT ε m β :=
ExceptT.mk <| x >>= fun a => match a with
| (Except.ok a) => pure <| Except.ok (f a)
| (Except.error e) => pure <| Except.error e
@[always_inline, inline]
protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
@[inline] protected def lift {α : Type u} (t : m α) : ExceptT ε m α :=
ExceptT.mk <| Except.ok <$> t
@[always_inline]
instance : MonadLift (Except ε) (ExceptT ε m) := fun e => ExceptT.mk <| pure e
instance : MonadLift m (ExceptT ε m) := ExceptT.lift
@[always_inline, inline]
protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε ExceptT ε m α) : ExceptT ε m α :=
@[inline] protected def tryCatch {α : Type u} (ma : ExceptT ε m α) (handle : ε ExceptT ε m α) : ExceptT ε m α :=
ExceptT.mk <| ma >>= fun res => match res with
| Except.ok a => pure (Except.ok a)
| Except.error e => (handle e)
instance : MonadFunctor m (ExceptT ε m) := fun f x => f x
@[always_inline]
instance : Monad (ExceptT ε m) where
pure := ExceptT.pure
bind := ExceptT.bind
map := ExceptT.map
@[always_inline, inline]
protected def adapt {ε' α : Type u} (f : ε ε') : ExceptT ε m α ExceptT ε' m α := fun x =>
@[inline] protected def adapt {ε' α : Type u} (f : ε ε') : ExceptT ε m α ExceptT ε' m α := fun x =>
ExceptT.mk <| Except.mapError f <$> x
end ExceptT
@[always_inline]
instance (m : Type u Type v) (ε₁ : Type u) (ε₂ : Type u) [Monad m] [MonadExceptOf ε₁ m] : MonadExceptOf ε₁ (ExceptT ε₂ m) where
throw e := ExceptT.mk <| throwThe ε₁ e
tryCatch x handle := ExceptT.mk <| tryCatchThe ε₁ x handle
@[always_inline]
instance (m : Type u Type v) (ε : Type u) [Monad m] : MonadExceptOf ε (ExceptT ε m) where
throw e := ExceptT.mk <| pure (Except.error e)
tryCatch := ExceptT.tryCatch
@@ -152,14 +130,12 @@ variable {ε : Type u} {m : Type v → Type w}
/-- Alternative orelse operator that allows to select which exception should be used.
The default is to use the first exception since the standard `orelse` uses the second. -/
@[always_inline, inline]
def orelse' [MonadExcept ε m] {α : Type v} (t₁ t₂ : m α) (useFirstEx := true) : m α :=
@[inline] def orelse' [MonadExcept ε m] {α : Type v} (t₁ t₂ : m α) (useFirstEx := true) : m α :=
tryCatch t₁ fun e₁ => tryCatch t₂ fun e₂ => throw (if useFirstEx then e₁ else e₂)
end MonadExcept
@[always_inline, inline]
def observing {ε α : Type u} {m : Type u Type v} [Monad m] [MonadExcept ε m] (x : m α) : m (Except ε α) :=
@[inline] def observing {ε α : Type u} {m : Type u Type v} [Monad m] [MonadExcept ε m] (x : m α) : m (Except ε α) :=
tryCatch (do let a x; pure (Except.ok a)) (fun ex => pure (Except.error ex))
def liftExcept [MonadExceptOf ε m] [Pure m] : Except ε α m α
@@ -182,25 +158,22 @@ class MonadFinally (m : Type u → Type v) where
export MonadFinally (tryFinally')
/-- Execute `x` and then execute `finalizer` even if `x` threw an exception -/
@[always_inline, inline]
def tryFinally {m : Type u Type v} {α β : Type u} [MonadFinally m] [Functor m] (x : m α) (finalizer : m β) : m α :=
@[inline] def tryFinally {m : Type u Type v} {α β : Type u} [MonadFinally m] [Functor m] (x : m α) (finalizer : m β) : m α :=
let y := tryFinally' x (fun _ => finalizer)
(·.1) <$> y
@[always_inline]
instance Id.finally : MonadFinally Id where
tryFinally' := fun x h =>
let a := x
let b := h (some x)
pure (a, b)
@[always_inline]
instance ExceptT.finally {m : Type u Type v} {ε : Type u} [MonadFinally m] [Monad m] : MonadFinally (ExceptT ε m) where
tryFinally' := fun x h => ExceptT.mk do
let r tryFinally' x fun e? => match e? with
| some (.ok a) => h (some a)
| _ => h none
| some (Except.ok a) => h (some a)
| _ => h none
match r with
| (.ok a, .ok b) => pure (.ok (a, b))
| (_, .error e) => pure (.error e) -- second error has precedence
| (.error e, _) => pure (.error e)
| (Except.ok a, Except.ok b) => pure (Except.ok (a, b))
| (_, Except.error e) => pure (Except.error e) -- second error has precedence
| (Except.error e, _) => pure (Except.error e)

View File

@@ -14,19 +14,15 @@ def ExceptCpsT (ε : Type u) (m : Type u → Type v) (α : Type u) := (β : Type
namespace ExceptCpsT
@[always_inline, inline]
def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
@[inline] def run {ε α : Type u} [Monad m] (x : ExceptCpsT ε m α) : m (Except ε α) :=
x _ (fun a => pure (Except.ok a)) (fun e => pure (Except.error e))
@[always_inline, inline]
def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α m β) (error : ε m β) : m β :=
@[inline] def runK {ε α : Type u} (x : ExceptCpsT ε m α) (s : ε) (ok : α m β) (error : ε m β) : m β :=
x _ ok error
@[always_inline, inline]
def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
@[inline] def runCatch [Monad m] (x : ExceptCpsT α m α) : m α :=
x α pure pure
@[always_inline]
instance : Monad (ExceptCpsT ε m) where
map f x := fun _ k₁ k₂ => x _ (fun a => k₁ (f a)) k₂
pure a := fun _ k _ => k a
@@ -39,8 +35,7 @@ instance : MonadExceptOf ε (ExceptCpsT ε m) where
throw e := fun _ _ k => k e
tryCatch x handle := fun _ k₁ k₂ => x _ k₁ (fun e => handle e _ k₁ k₂)
@[always_inline, inline]
def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
@[inline] def lift [Monad m] (x : m α) : ExceptCpsT ε m α :=
fun _ k _ => x >>= k
instance [Monad m] : MonadLift m (ExceptCpsT σ m) where

View File

@@ -14,7 +14,6 @@ def Id (type : Type u) : Type u := type
namespace Id
@[always_inline]
instance : Monad Id where
pure x := x
bind x f := f x
@@ -23,8 +22,7 @@ instance : Monad Id where
def hasBind : Bind Id :=
inferInstance
@[always_inline, inline]
protected def run (x : Id α) : α := x
@[inline] protected def run (x : Id α) : α := x
instance [OfNat α n] : OfNat (Id α) n :=
inferInstanceAs (OfNat α n)

View File

@@ -289,14 +289,14 @@ theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT
apply ext; intro s
simp [map_eq_pure_bind, const]
apply bind_congr; intro p; cases p
simp [Prod.eta]
simp [Prod.ext]
theorem seqLeft_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x <* y = const β <$> x <*> y := by
apply ext; intro s
simp [map_eq_pure_bind]
instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
id_map := by intros; apply ext; intros; simp[Prod.eta]
id_map := by intros; apply ext; intros; simp[Prod.ext]
map_const := by intros; rfl
seqLeft_eq := seqLeft_eq
seqRight_eq := seqRight_eq

View File

@@ -10,13 +10,12 @@ import Init.Control.Except
universe u v
instance : ToBool (Option α) := Option.toBool
instance {α} : ToBool (Option α) := Option.toBool
def OptionT (m : Type u Type v) (α : Type u) : Type v :=
m (Option α)
@[always_inline, inline]
def OptionT.run {m : Type u Type v} {α : Type u} (x : OptionT m α) : m (Option α) :=
@[inline] def OptionT.run {m : Type u Type v} {α : Type u} (x : OptionT m α) : m (Option α) :=
x
namespace OptionT
@@ -25,41 +24,38 @@ variable {m : Type u → Type v} [Monad m] {α β : Type u}
protected def mk (x : m (Option α)) : OptionT m α :=
x
@[always_inline, inline]
protected def bind (x : OptionT m α) (f : α OptionT m β) : OptionT m β := OptionT.mk do
@[inline] protected def bind (x : OptionT m α) (f : α OptionT m β) : OptionT m β := OptionT.mk do
match ( x) with
| some a => f a
| none => pure none
@[always_inline, inline]
protected def pure (a : α) : OptionT m α := OptionT.mk do
@[inline] protected def pure (a : α) : OptionT m α := OptionT.mk do
pure (some a)
@[always_inline]
instance : Monad (OptionT m) where
pure := OptionT.pure
bind := OptionT.bind
@[always_inline, inline] protected def orElse (x : OptionT m α) (y : Unit OptionT m α) : OptionT m α := OptionT.mk do
@[inline] protected def orElse (x : OptionT m α) (y : Unit OptionT m α) : OptionT m α := OptionT.mk do
match ( x) with
| some a => pure (some a)
| _ => y ()
@[always_inline, inline] protected def fail : OptionT m α := OptionT.mk do
@[inline] protected def fail : OptionT m α := OptionT.mk do
pure none
instance : Alternative (OptionT m) where
failure := OptionT.fail
orElse := OptionT.orElse
@[always_inline, inline] protected def lift (x : m α) : OptionT m α := OptionT.mk do
@[inline] protected def lift (x : m α) : OptionT m α := OptionT.mk do
return some ( x)
instance : MonadLift m (OptionT m) := OptionT.lift
instance : MonadFunctor m (OptionT m) := fun f x => f x
@[always_inline, inline] protected def tryCatch (x : OptionT m α) (handle : Unit OptionT m α) : OptionT m α := OptionT.mk do
@[inline] protected def tryCatch (x : OptionT m α) (handle : Unit OptionT m α) : OptionT m α := OptionT.mk do
let some a x | handle ()
pure a

View File

@@ -12,12 +12,10 @@ import Init.Control.Except
namespace ReaderT
@[always_inline, inline]
protected def orElse [Alternative m] (x₁ : ReaderT ρ m α) (x₂ : Unit ReaderT ρ m α) : ReaderT ρ m α :=
@[inline] protected def orElse [Alternative m] (x₁ : ReaderT ρ m α) (x₂ : Unit ReaderT ρ m α) : ReaderT ρ m α :=
fun s => x₁ s <|> x₂ () s
@[always_inline, inline]
protected def failure [Alternative m] : ReaderT ρ m α :=
@[inline] protected def failure [Alternative m] : ReaderT ρ m α :=
fun _ => failure
instance [Alternative m] [Monad m] : Alternative (ReaderT ρ m) where
@@ -31,7 +29,6 @@ instance : MonadControl m (ReaderT ρ m) where
liftWith f ctx := f fun x => x ctx
restoreM x _ := x
@[always_inline]
instance ReaderT.tryFinally [MonadFinally m] [Monad m] : MonadFinally (ReaderT ρ m) where
tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx)

View File

@@ -14,16 +14,13 @@ universe u v w
def StateT (σ : Type u) (m : Type u Type v) (α : Type u) : Type (max u v) :=
σ m (α × σ)
@[always_inline, inline]
def StateT.run {σ : Type u} {m : Type u Type v} {α : Type u} (x : StateT σ m α) (s : σ) : m (α × σ) :=
@[inline] def StateT.run {σ : Type u} {m : Type u Type v} {α : Type u} (x : StateT σ m α) (s : σ) : m (α × σ) :=
x s
@[always_inline, inline]
def StateT.run' {σ : Type u} {m : Type u Type v} [Functor m] {α : Type u} (x : StateT σ m α) (s : σ) : m α :=
@[inline] def StateT.run' {σ : Type u} {m : Type u Type v} [Functor m] {α : Type u} (x : StateT σ m α) (s : σ) : m α :=
(·.1) <$> x s
@[reducible]
def StateM (σ α : Type u) : Type u := StateT σ Id α
@[reducible] def StateM (σ α : Type u) : Type u := StateT σ Id α
instance {σ α} [Subsingleton σ] [Subsingleton α] : Subsingleton (StateM σ α) where
allEq x y := by
@@ -38,58 +35,46 @@ section
variable {σ : Type u} {m : Type u Type v}
variable [Monad m] {α β : Type u}
@[always_inline, inline]
protected def pure (a : α) : StateT σ m α :=
@[inline] protected def pure (a : α) : StateT σ m α :=
fun s => pure (a, s)
@[always_inline, inline]
protected def bind (x : StateT σ m α) (f : α StateT σ m β) : StateT σ m β :=
@[inline] protected def bind (x : StateT σ m α) (f : α StateT σ m β) : StateT σ m β :=
fun s => do let (a, s) x s; f a s
@[always_inline, inline]
protected def map (f : α β) (x : StateT σ m α) : StateT σ m β :=
@[inline] protected def map (f : α β) (x : StateT σ m α) : StateT σ m β :=
fun s => do let (a, s) x s; pure (f a, s)
@[always_inline]
instance : Monad (StateT σ m) where
pure := StateT.pure
bind := StateT.bind
map := StateT.map
@[always_inline, inline]
protected def orElse [Alternative m] {α : Type u} (x₁ : StateT σ m α) (x₂ : Unit StateT σ m α) : StateT σ m α :=
@[inline] protected def orElse [Alternative m] {α : Type u} (x₁ : StateT σ m α) (x₂ : Unit StateT σ m α) : StateT σ m α :=
fun s => x₁ s <|> x₂ () s
@[always_inline, inline]
protected def failure [Alternative m] {α : Type u} : StateT σ m α :=
@[inline] protected def failure [Alternative m] {α : Type u} : StateT σ m α :=
fun _ => failure
instance [Alternative m] : Alternative (StateT σ m) where
failure := StateT.failure
orElse := StateT.orElse
@[always_inline, inline]
protected def get : StateT σ m σ :=
@[inline] protected def get : StateT σ m σ :=
fun s => pure (s, s)
@[always_inline, inline]
protected def set : σ StateT σ m PUnit :=
@[inline] protected def set : σ StateT σ m PUnit :=
fun s' _ => pure (, s')
@[always_inline, inline]
protected def modifyGet (f : σ α × σ) : StateT σ m α :=
@[inline] protected def modifyGet (f : σ α × σ) : StateT σ m α :=
fun s => pure (f s)
@[always_inline, inline]
protected def lift {α : Type u} (t : m α) : StateT σ m α :=
@[inline] protected def lift {α : Type u} (t : m α) : StateT σ m α :=
fun s => do let a t; pure (a, s)
instance : MonadLift m (StateT σ m) := StateT.lift
@[always_inline]
instance (σ m) [Monad m] : MonadFunctor m (StateT σ m) := fun f x s => f (x s)
@[always_inline]
instance (ε) [MonadExceptOf ε m] : MonadExceptOf ε (StateT σ m) := {
throw := StateT.lift throwThe ε
tryCatch := fun x c s => tryCatchThe ε (x s) (fun e => c e s)
@@ -98,18 +83,6 @@ instance (ε) [MonadExceptOf ε m] : MonadExceptOf ε (StateT σ m) := {
end
end StateT
/-- Adapter to create a ForIn instance from a ForM instance -/
@[always_inline, inline]
def ForM.forIn [Monad m] [ForM (StateT β (ExceptT β m)) ρ α]
(x : ρ) (b : β) (f : α β m (ForInStep β)) : m β := do
let g a b := .mk do
match f a b with
| .yield b' => pure (.ok (, b'))
| .done b' => pure (.error b')
match forM (m := StateT β (ExceptT β m)) (α := α) x g |>.run b |>.run with
| .ok a => pure a.2
| .error a => pure a
section
variable {σ : Type u} {m : Type u Type v}
@@ -120,13 +93,11 @@ instance [Monad m] : MonadStateOf σ (StateT σ m) where
end
@[always_inline]
instance StateT.monadControl (σ : Type u) (m : Type u Type v) [Monad m] : MonadControl m (StateT σ m) where
stM := fun α => α × σ
liftWith := fun f => do let s get; liftM (f (fun x => x.run s))
restoreM := fun x => do let (a, s) liftM x; set s; pure a
@[always_inline]
instance StateT.tryFinally {m : Type u Type v} {σ : Type u} [MonadFinally m] [Monad m] : MonadFinally (StateT σ m) where
tryFinally' := fun x h s => do
let ((a, _), (b, s'')) tryFinally' (x s) fun

View File

@@ -14,19 +14,15 @@ def StateCpsT (σ : Type u) (m : Type u → Type v) (α : Type u) := (δ : Type
namespace StateCpsT
@[always_inline, inline]
def runK {α σ : Type u} {m : Type u Type v} (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
@[inline] def runK {α σ : Type u} {m : Type u Type v} (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
x _ s k
@[always_inline, inline]
def run {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
@[inline] def run {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
runK x s (fun a s => pure (a, s))
@[always_inline, inline]
def run' {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
@[inline] def run' {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
runK x s (fun a _ => pure a)
@[always_inline]
instance : Monad (StateCpsT σ m) where
map f x := fun δ s k => x δ s fun a s => k (f a) s
pure a := fun _ s k => k a s
@@ -35,14 +31,12 @@ instance : Monad (StateCpsT σ m) where
instance : LawfulMonad (StateCpsT σ m) := by
refine' { .. } <;> intros <;> rfl
@[always_inline]
instance : MonadStateOf σ (StateCpsT σ m) where
get := fun _ s k => k s s
set s := fun _ _ k => k s
modifyGet f := fun _ s k => let (a, s) := f s; k a s
@[always_inline, inline]
protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
@[inline] protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
fun _ s k => x >>= (k . s)
instance [Monad m] : MonadLift m (StateCpsT σ m) where

View File

@@ -13,23 +13,20 @@ def StateRefT' (ω : Type) (σ : Type) (m : Type → Type) (α : Type) : Type :=
/-! Recall that `StateRefT` is a macro that infers `ω` from the `m`. -/
@[always_inline, inline]
def StateRefT'.run {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m (α × σ) := do
@[inline] def StateRefT'.run {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m (α × σ) := do
let ref ST.mkRef s
let a x ref
let s ref.get
pure (a, s)
@[always_inline, inline]
def StateRefT'.run' {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m α := do
@[inline] def StateRefT'.run' {ω σ : Type} {m : Type Type} [Monad m] [MonadLiftT (ST ω) m] {α : Type} (x : StateRefT' ω σ m α) (s : σ) : m α := do
let (a, _) x.run s
pure a
namespace StateRefT'
variable {ω σ : Type} {m : Type Type} {α : Type}
@[always_inline, inline]
protected def lift (x : m α) : StateRefT' ω σ m α :=
@[inline] protected def lift (x : m α) : StateRefT' ω σ m α :=
fun _ => x
instance [Monad m] : Monad (StateRefT' ω σ m) := inferInstanceAs (Monad (ReaderT _ _))
@@ -37,16 +34,13 @@ instance : MonadLift m (StateRefT' ω σ m) := ⟨StateRefT'.lift⟩
instance (σ m) [Monad m] : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _))
@[inline]
protected def get [Monad m] [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
@[inline] protected def get [Monad m] [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
fun ref => ref.get
@[inline]
protected def set [Monad m] [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
@[inline] protected def set [Monad m] [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
fun ref => ref.set s
@[inline]
protected def modifyGet [Monad m] [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α :=
@[inline] protected def modifyGet [Monad m] [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α :=
fun ref => ref.modifyGet f
instance [MonadLiftT (ST ω) m] [Monad m] : MonadStateOf σ (StateRefT' ω σ m) where
@@ -54,7 +48,6 @@ instance [MonadLiftT (ST ω) m] [Monad m] : MonadStateOf σ (StateRefT' ω σ m)
set := StateRefT'.set
modifyGet := StateRefT'.modifyGet
@[always_inline]
instance (ε) [MonadExceptOf ε m] : MonadExceptOf ε (StateRefT' ω σ m) where
throw := StateRefT'.lift throwThe ε
tryCatch := fun x c s => tryCatchThe ε (x s) (fun e => c e s)

View File

@@ -17,31 +17,24 @@ It is mainly used for doing targeted term transformations, for example rewriting
only on the left side of an equality. -/
declare_syntax_cat conv (behavior := both)
syntax convSeq1Indented := sepBy1IndentSemicolon(conv)
syntax convSeqBracketed := "{" withoutPosition(sepByIndentSemicolon(conv)) "}"
syntax convSeq1Indented := withPosition((colGe conv ";"?)+)
syntax convSeqBracketed := "{" (conv ";"?)* "}"
-- Order is important: a missing `conv` proof should not be parsed as `{ <missing> }`,
-- automatically closing goals
syntax convSeq := convSeqBracketed <|> convSeq1Indented
/-- The `*` occurrence list means to apply to all occurrences of the pattern. -/
syntax occsWildcard := "*"
/--
A list `1 2 4` of occurrences means to apply to the first, second, and fourth
occurrence of the pattern.
`conv => ...` allows the user to perform targeted rewriting on a goal or hypothesis,
by focusing on particular subexpressions.
See <https://leanprover.github.io/theorem_proving_in_lean4/conv.html> for more details.
Basic forms:
* `conv => cs` will rewrite the goal with conv tactics `cs`.
* `conv at h => cs` will rewrite hypothesis `h`.
* `conv in pat => cs` will rewrite the first subexpression matching `pat`.
-/
syntax occsIndexed := num+
/-- An occurrence specification, either `*` or a list of numbers. The default is `[1]`. -/
syntax occs := atomic("(" &"occs") " := " (occsWildcard <|> occsIndexed) ") "
/--
`with_annotate_state stx t` annotates the lexical range of `stx : Syntax` with
the initial and final state of running tactic `t`.
-/
scoped syntax (name := withAnnotateState)
"with_annotate_state " rawStx ppSpace conv : conv
syntax (name := conv) "conv " (" at " ident)? (" in " term)? " => " convSeq : tactic
/-- `skip` does nothing. -/
syntax (name := skip) "skip" : conv
@@ -59,10 +52,10 @@ in "head position" until a constructor is exposed. For example, `List.map f [a,
weak head normalizes to `f a :: List.map f [b, c]`. -/
syntax (name := whnf) "whnf" : conv
/-- Expands let-declarations and let-variables. -/
/-- Expand let-declarations and let-variables. -/
syntax (name := zeta) "zeta" : conv
/-- Puts term in normal form, this tactic is meant for debugging purposes only. -/
/-- Put term in normal form, this tactic is ment for debugging purposes only -/
syntax (name := reduce) "reduce" : conv
/-- Performs one step of "congruence", which takes a term and produces
@@ -79,68 +72,33 @@ syntax (name := arg) "arg " "@"? num : conv
/-- `ext x` traverses into a binder (a `fun x => e` or `∀ x, e` expression)
to target `e`, introducing name `x` in the process. -/
syntax (name := ext) "ext" (ppSpace colGt ident)* : conv
syntax (name := ext) "ext " (colGt ident)* : conv
/-- `change t'` replaces the target `t` with `t'`,
assuming `t` and `t'` are definitionally equal. -/
syntax (name := change) "change " term : conv
/-- `delta id1 id2 ...` unfolds all occurrences of `id1`, `id2`, ... in the target.
/-- `delta foo` unfolds all occurrences of `foo` in the target.
Like the `delta` tactic, this ignores any definitional equations and uses
primitive delta-reduction instead, which may result in leaking implementation details.
Users should prefer `unfold` for unfolding definitions. -/
syntax (name := delta) "delta" (ppSpace colGt ident)+ : conv
syntax (name := delta) "delta " ident : conv
/--
* `unfold foo` unfolds all occurrences of `foo` in the target.
* `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`.
/-- `unfold foo` unfolds all occurrences of `foo` in the target.
Like the `unfold` tactic, this uses equational lemmas for the chosen definition
to rewrite the target. For recursive definitions,
only one layer of unfolding is performed. -/
syntax (name := unfold) "unfold" (ppSpace colGt ident)+ : conv
syntax (name := unfold) "unfold " ident : conv
/--
* `pattern pat` traverses to the first subterm of the target that matches `pat`.
* `pattern (occs := *) pat` traverses to every subterm of the target that matches `pat`
which is not contained in another match of `pat`. It generates one subgoal for each matching
subterm.
* `pattern (occs := 1 2 4) pat` matches occurrences `1, 2, 4` of `pat` and produces three subgoals.
Occurrences are numbered left to right from the outside in.
Note that skipping an occurrence of `pat` will traverse inside that subexpression, which means
it may find more matches and this can affect the numbering of subsequent pattern matches.
For example, if we are searching for `f _` in `f (f a) = f b`:
* `occs := 1 2` (and `occs := *`) returns `| f (f a)` and `| f b`
* `occs := 2` returns `| f a`
* `occs := 2 3` returns `| f a` and `| f b`
* `occs := 1 3` is an error, because after skipping `f b` there is no third match.
-/
syntax (name := pattern) "pattern " (occs)? term : conv
/-- `pattern pat` traverses to the first subterm of the target that matches `pat`. -/
syntax (name := pattern) "pattern " term : conv
/-- `rw [thm]` rewrites the target using `thm`. See the `rw` tactic for more information. -/
syntax (name := rewrite) "rewrite" (config)? rwRuleSeq : conv
syntax (name := rewrite) "rewrite " (config)? rwRuleSeq : conv
/-- `simp [thm]` performs simplification using `thm` and marked `@[simp]` lemmas.
See the `simp` tactic for more information. -/
syntax (name := simp) "simp" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpStar <|> simpErase <|> simpLemma),*) "]")? : conv
/--
`dsimp` is the definitional simplifier in `conv`-mode. It differs from `simp` in that it only
applies theorems that hold by reflexivity.
Examples:
```lean
example (a : Nat): (0 + 0) = a - a := by
conv =>
lhs
dsimp
rw [← Nat.sub_self a]
```
-/
syntax (name := dsimp) "dsimp" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpErase <|> simpLemma),*) "]")? : conv
syntax (name := simp) "simp " (config)? (discharger)? (&"only ")? ("[" (simpStar <|> simpErase <|> simpLemma),* "]")? : conv
/-- `simp_match` simplifies match expressions. For example,
```
@@ -152,105 +110,44 @@ simplifies to `a`. -/
syntax (name := simpMatch) "simp_match" : conv
/-- Executes the given tactic block without converting `conv` goal into a regular goal. -/
/-- Execute the given tactic block without converting `conv` goal into a regular goal -/
syntax (name := nestedTacticCore) "tactic'" " => " tacticSeq : conv
/-- Focuses, converts the `conv` goal `⊢ lhs` into a regular goal `⊢ lhs = rhs`, and then executes the given tactic block. -/
/-- Focus, convert the `conv` goal `⊢ lhs` into a regular goal `⊢ lhs = rhs`, and then execute the given tactic block. -/
syntax (name := nestedTactic) "tactic" " => " tacticSeq : conv
/-- Executes the given conv block without converting regular goal into a `conv` goal. -/
syntax (name := convTactic) "conv'" " => " convSeq : tactic
/-- `{ convs }` runs the list of `convs` on the current target, and any subgoals that
remain are trivially closed by `skip`. -/
syntax (name := nestedConv) convSeqBracketed : conv
/-- `(convs)` runs the `convs` in sequence on the current list of targets.
This is pure grouping with no added effects. -/
syntax (name := paren) "(" withoutPosition(convSeq) ")" : conv
/-- `rfl` closes one conv goal "trivially", by using reflexivity
(that is, no rewriting). -/
macro "rfl" : conv => `(conv| tactic => rfl)
/-- `done` succeeds iff there are no goals remaining. -/
macro "done" : conv => `(conv| tactic' => done)
/-- `trace_state` prints the current goal state. -/
macro "trace_state" : conv => `(conv| tactic' => trace_state)
/-- `all_goals tac` runs `tac` on each goal, concatenating the resulting goals, if any. -/
macro (name := allGoals) tk:"all_goals " s:convSeq : conv =>
`(conv| tactic' => all_goals%$tk conv' => $s)
/--
`any_goals tac` applies the tactic `tac` to every goal, and succeeds if at
least one application succeeds.
-/
macro (name := anyGoals) tk:"any_goals " s:convSeq : conv =>
`(conv| tactic' => any_goals%$tk conv' => $s)
/--
* `case tag => tac` focuses on the goal with case name `tag` and solves it using `tac`,
or else fails.
* `case tag x₁ ... xₙ => tac` additionally renames the `n` most recent hypotheses
with inaccessible names to the given names.
* `case tag₁ | tag₂ => tac` is equivalent to `(case tag₁ => tac); (case tag₂ => tac)`.
-/
macro (name := case) tk:"case " args:sepBy1(caseArg, " | ") arr:" => " s:convSeq : conv =>
`(conv| tactic' => case%$tk $args|* =>%$arr conv' => ($s); all_goals rfl)
/--
`case'` is similar to the `case tag => tac` tactic, but does not ensure the goal
has been solved after applying `tac`, nor admits the goal if `tac` failed.
Recall that `case` closes the goal using `sorry` when `tac` fails, and
the tactic execution is not interrupted.
-/
macro (name := case') tk:"case' " args:sepBy1(caseArg, " | ") arr:" => " s:convSeq : conv =>
`(conv| tactic' => case'%$tk $args|* =>%$arr conv' => $s)
/--
`next => tac` focuses on the next goal and solves it using `tac`, or else fails.
`next x₁ ... xₙ => tac` additionally renames the `n` most recent hypotheses with
inaccessible names to the given names.
-/
macro "next" args:(ppSpace binderIdent)* " => " tac:convSeq : conv => `(conv| case _ $args* => $tac)
/--
`focus tac` focuses on the main goal, suppressing all other goals, and runs `tac` on it.
Usually `· tac`, which enforces that the goal is closed by `tac`, should be preferred.
-/
macro (name := focus) tk:"focus " s:convSeq : conv => `(conv| tactic' => focus%$tk conv' => $s)
syntax (name := paren) "(" convSeq ")" : conv
/-- `conv => cs` runs `cs` in sequence on the target `t`,
resulting in `t'`, which becomes the new target subgoal. -/
syntax (name := convConvSeq) "conv" " => " convSeq : conv
syntax (name := convConvSeq) "conv " " => " convSeq : conv
/-- `· conv` focuses on the main conv goal and tries to solve it using `s`. -/
macro dot:patternIgnore("·" <|> ".") s:convSeq : conv => `(conv| {%$dot ($s) })
/-- `fail_if_success t` fails if the tactic `t` succeeds. -/
macro (name := failIfSuccess) tk:"fail_if_success " s:convSeq : conv =>
`(conv| tactic' => fail_if_success%$tk conv' => $s)
/-- `· conv` focuses on the main conv goal and tries to solve it using `s` -/
macro dot:("·" <|> ".") s:convSeq : conv => `({%$dot ($s) })
/-- `rw [rules]` applies the given list of rewrite rules to the target.
See the `rw` tactic for more information. -/
macro "rw" c:(config)? s:rwRuleSeq : conv => `(conv| rewrite $[$c]? $s)
macro "rw " c:(config)? s:rwRuleSeq : conv => `(rewrite $[$c]? $s)
/-- `erw [rules]` is a shorthand for `rw (config := { transparency := .default }) [rules]`.
This does rewriting up to unfolding of regular definitions (by comparison to regular `rw`
which only unfolds `@[reducible]` definitions). -/
macro "erw" s:rwRuleSeq : conv => `(conv| rw (config := { transparency := .default }) $s)
macro "erw " s:rwRuleSeq : conv => `(rw (config := { transparency := .default }) $s)
/-- `args` traverses into all arguments. Synonym for `congr`. -/
macro "args" : conv => `(conv| congr)
macro "args" : conv => `(congr)
/-- `left` traverses into the left argument. Synonym for `lhs`. -/
macro "left" : conv => `(conv| lhs)
macro "left" : conv => `(lhs)
/-- `right` traverses into the right argument. Synonym for `rhs`. -/
macro "right" : conv => `(conv| rhs)
macro "right" : conv => `(rhs)
/-- `intro` traverses into binders. Synonym for `ext`. -/
macro "intro" xs:(ppSpace colGt ident)* : conv => `(conv| ext $xs*)
macro "intro " xs:(colGt ident)* : conv => `(conv| ext $xs*)
syntax enterArg := ident <|> ("@"? num)
@@ -261,46 +158,35 @@ It is a shorthand for other conv tactics as follows:
* `enter [x]` (where `x` is an identifier) is equivalent to `ext x`.
For example, given the target `f (g a (fun x => x b))`, `enter [1, 2, x, 1]`
will traverse to the subterm `b`. -/
syntax "enter" " [" withoutPosition(enterArg,+) "]" : conv
syntax "enter " "[" (colGt enterArg),+ "]": conv
macro_rules
| `(conv| enter [$i:num]) => `(conv| arg $i)
| `(conv| enter [@$i]) => `(conv| arg @$i)
| `(conv| enter [$id:ident]) => `(conv| ext $id)
| `(conv| enter [$arg, $args,*]) => `(conv| (enter [$arg]; enter [$args,*]))
/-- `rfl` closes one conv goal "trivially", by using reflexivity
(that is, no rewriting). -/
macro "rfl" : conv => `(tactic => rfl)
/-- `done` succeeds iff there are no goals remaining. -/
macro "done" : conv => `(tactic' => done)
/-- `trace_state` prints the current goal state. -/
macro "trace_state" : conv => `(tactic' => trace_state)
/-- The `apply thm` conv tactic is the same as `apply thm` the tactic.
There are no restrictions on `thm`, but strange results may occur if `thm`
cannot be reasonably interpreted as proving one equality from a list of others. -/
-- TODO: error if non-conv subgoals?
macro "apply " e:term : conv => `(conv| tactic => apply $e)
macro "apply " e:term : conv => `(tactic => apply $e)
/-- `first | conv | ...` runs each `conv` until one succeeds, or else fails. -/
syntax (name := first) "first " withPosition((ppDedent(ppLine) colGe "| " convSeq)+) : conv
/-- `try tac` runs `tac` and succeeds even if `tac` failed. -/
macro "try " t:convSeq : conv => `(conv| first | $t | skip)
macro:1 x:conv tk:" <;> " y:conv:0 : conv =>
`(conv| tactic' => (conv' => $x:conv) <;>%$tk (conv' => $y:conv))
syntax (name := first) "first " withPosition((colGe "|" convSeq)+) : conv
/-- `repeat convs` runs the sequence `convs` repeatedly until it fails to apply. -/
syntax "repeat " convSeq : conv
macro_rules
| `(conv| repeat $seq) => `(conv| first | ($seq); repeat $seq | rfl)
/--
`conv => ...` allows the user to perform targeted rewriting on a goal or hypothesis,
by focusing on particular subexpressions.
See <https://leanprover.github.io/theorem_proving_in_lean4/conv.html> for more details.
Basic forms:
* `conv => cs` will rewrite the goal with conv tactics `cs`.
* `conv at h => cs` will rewrite hypothesis `h`.
* `conv in pat => cs` will rewrite the first subexpression matching `pat` (see `pattern`).
-/
-- HACK: put this at the end so that references to `conv` above
-- refer to the syntax category instead of this syntax
syntax (name := conv) "conv" (" at " ident)? (" in " (occs)? term)? " => " convSeq : tactic
end Lean.Parser.Tactic.Conv

View File

@@ -13,7 +13,7 @@ set_option linter.missingDocs true -- keep it documented
universe u v w
/--
`inline (f x)` is an indication to the compiler to inline the definition of `f`
`inline f x` is an indication to the compiler to inline the definition of `f`
at the application site itself (by comparison to the `@[inline]` attribute,
which applies to all applications of the function).
-/
@@ -70,10 +70,6 @@ instead of calling the stored getter function.
@[simp] theorem Thunk.sizeOf_eq [SizeOf α] (a : Thunk α) : sizeOf a = 1 + sizeOf a.get := by
cases a; rfl
instance thunkCoe : CoeTail α (Thunk α) where
-- Since coercions are expanded eagerly, `a` is evaluated lazily.
coe a := fun _ => a
/-- A variation on `Eq.ndrec` with the equality argument first. -/
abbrev Eq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : α Sort u1} {b : α} (h : a = b) (m : motive a) : motive b :=
Eq.ndrec m h
@@ -91,8 +87,8 @@ structure Iff (a b : Prop) : Prop where
/-- Modus ponens for if and only if, reversed. If `a ↔ b` and `b`, then `a`. -/
mpr : b a
@[inherit_doc] infix:20 " <-> " => Iff
@[inherit_doc] infix:20 "" => Iff
@[inheritDoc] infix:20 " <-> " => Iff
@[inheritDoc] infix:20 "" => Iff
/--
`Sum α β`, or `α ⊕ β`, is the disjoint union of types `α` and `β`.
@@ -100,12 +96,12 @@ An element of `α ⊕ β` is either of the form `.inl a` where `a : α`,
or `.inr b` where `b : β`.
-/
inductive Sum (α : Type u) (β : Type v) where
/-- Left injection into the sum type `α ⊕ β`. If `a : α` then `.inl a : α ⊕ β`. -/
| inl (val : α) : Sum α β
/-- Right injection into the sum type `α ⊕ β`. If `b : β` then `.inr b : α ⊕ β`. -/
| inr (val : β) : Sum α β
| /-- Left injection into the sum type `α ⊕ β`. If `a : α` then `.inl a : α ⊕ β`. -/
inl (val : α) : Sum α β
| /-- Right injection into the sum type `α ⊕ β`. If `b : β` then `.inr b : α ⊕ β`. -/
inr (val : β) : Sum α β
@[inherit_doc] infixr:30 "" => Sum
@[inheritDoc] infixr:30 "" => Sum
/--
`PSum α β`, or `α ⊕' β`, is the disjoint union of types `α` and `β`.
@@ -119,12 +115,12 @@ because the equation `max 1 u v = ?u + 1` has no solution in level arithmetic.
`PSum` is usually only used in automation that constructs sums of arbitrary types.
-/
inductive PSum (α : Sort u) (β : Sort v) where
/-- Left injection into the sum type `α ⊕' β`. If `a : α` then `.inl a : α ⊕' β`. -/
| inl (val : α) : PSum α β
/-- Right injection into the sum type `α ⊕' β`. If `b : β` then `.inr b : α ⊕' β`. -/
| inr (val : β) : PSum α β
| /-- Left injection into the sum type `α ⊕' β`. If `a : α` then `.inl a : α ⊕' β`. -/
inl (val : α) : PSum α β
| /-- Right injection into the sum type `α ⊕' β`. If `b : β` then `.inr b : α ⊕' β`. -/
inr (val : β) : PSum α β
@[inherit_doc] infixr:30 " ⊕' " => PSum
@[inheritDoc] infixr:30 " ⊕' " => PSum
/--
`Sigma β`, also denoted `Σ a : α, β a` or `(a : α) × β a`, is the type of dependent pairs
@@ -194,9 +190,9 @@ example (h : ∃ x : Nat, x = x) : True :=
```
-/
inductive Exists {α : Sort u} (p : α Prop) : Prop where
/-- Existential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. -/
| intro (w : α) (h : p w) : Exists p
| /-- Existential introduction. If `a : α` and `h : p a`,
then `⟨a, h⟩` is a proof that `∃ x : α, p x`. -/
intro (w : α) (h : p w) : Exists p
/--
Auxiliary type used to compile `for x in xs` notation.
@@ -210,12 +206,12 @@ representing the body of a for loop. It can be:
`.done` is produced by calls to `break` or `return` in the loop,
-/
inductive ForInStep (α : Type u) where
/-- `.done a` means that we should early-exit the loop.
`.done` is produced by calls to `break` or `return` in the loop. -/
| done : α ForInStep α
/-- `.yield a` means that we should continue the loop.
`.yield` is produced by `continue` and reaching the bottom of the loop body. -/
| yield : α ForInStep α
| /-- `.done a` means that we should early-exit the loop.
`.done` is produced by calls to `break` or `return` in the loop. -/
done : α ForInStep α
| /-- `.yield a` means that we should continue the loop.
`.yield` is produced by `continue` and reaching the bottom of the loop body. -/
yield : α ForInStep α
deriving Inhabited
/--
@@ -280,16 +276,16 @@ block can exit:
All cases return a value `s : σ` which bundles all the mutable variables of the do-block.
-/
inductive DoResultPRBC (α β σ : Type u) where
/-- `pure (a : α) s` means that the block exited normally with return value `a` -/
| pure : α σ DoResultPRBC α β σ
/-- `return (b : β) s` means that the block exited via a `return b` early-exit command -/
| return : β σ DoResultPRBC α β σ
/-- `break s` means that `break` was called, meaning that we should exit
from the containing loop -/
| break : σ DoResultPRBC α β σ
/-- `continue s` means that `continue` was called, meaning that we should continue
to the next iteration of the containing loop -/
| continue : σ DoResultPRBC α β σ
| /-- `pure (a : α) s` means that the block exited normally with return value `a` -/
pure : α σ DoResultPRBC α β σ
| /-- `return (b : β) s` means that the block exited via a `return b` early-exit command -/
return : β σ DoResultPRBC α β σ
| /-- `break s` means that `break` was called, meaning that we should exit
from the containing loop -/
break : σ DoResultPRBC α β σ
| /-- `continue s` means that `continue` was called, meaning that we should continue
to the next iteration of the containing loop -/
continue : σ DoResultPRBC α β σ
/--
Auxiliary type used to compile `do` notation. It is the same as
@@ -297,10 +293,10 @@ Auxiliary type used to compile `do` notation. It is the same as
because we are not in a loop context.
-/
inductive DoResultPR (α β σ : Type u) where
/-- `pure (a : α) s` means that the block exited normally with return value `a` -/
| pure : α σ DoResultPR α β σ
/-- `return (b : β) s` means that the block exited via a `return b` early-exit command -/
| return : β σ DoResultPR α β σ
| /-- `pure (a : α) s` means that the block exited normally with return value `a` -/
pure : α σ DoResultPR α β σ
| /-- `return (b : β) s` means that the block exited via a `return b` early-exit command -/
return : β σ DoResultPR α β σ
/--
Auxiliary type used to compile `do` notation. It is an optimization of
@@ -308,12 +304,12 @@ Auxiliary type used to compile `do` notation. It is an optimization of
used when neither `pure` nor `return` are possible exit paths.
-/
inductive DoResultBC (σ : Type u) where
/-- `break s` means that `break` was called, meaning that we should exit
from the containing loop -/
| break : σ DoResultBC σ
/-- `continue s` means that `continue` was called, meaning that we should continue
to the next iteration of the containing loop -/
| continue : σ DoResultBC σ
| /-- `break s` means that `break` was called, meaning that we should exit
from the containing loop -/
break : σ DoResultBC σ
| /-- `continue s` means that `continue` was called, meaning that we should continue
to the next iteration of the containing loop -/
continue : σ DoResultBC σ
/--
Auxiliary type used to compile `do` notation. It is an optimization of
@@ -321,18 +317,18 @@ either `DoResultPRBC α PEmpty σ` or `DoResultPRBC PEmpty α σ` to remove the
impossible case, used when either `pure` or `return` is never used.
-/
inductive DoResultSBC (α σ : Type u) where
/-- This encodes either `pure (a : α)` or `return (a : α)`:
* `pure (a : α) s` means that the block exited normally with return value `a`
* `return (b : β) s` means that the block exited via a `return b` early-exit command
| /-- This encodes either `pure (a : α)` or `return (a : α)`:
* `pure (a : α) s` means that the block exited normally with return value `a`
* `return (b : β) s` means that the block exited via a `return b` early-exit command
The one that is actually encoded depends on the context of use. -/
| pureReturn : α σ DoResultSBC α σ
/-- `break s` means that `break` was called, meaning that we should exit
from the containing loop -/
| break : σ DoResultSBC α σ
/-- `continue s` means that `continue` was called, meaning that we should continue
to the next iteration of the containing loop -/
| continue : σ DoResultSBC α σ
The one that is actually encoded depends on the context of use. -/
pureReturn : α σ DoResultSBC α σ
| /-- `break s` means that `break` was called, meaning that we should exit
from the containing loop -/
break : σ DoResultSBC α σ
| /-- `continue s` means that `continue` was called, meaning that we should continue
to the next iteration of the containing loop -/
continue : σ DoResultSBC α σ
/-- `HasEquiv α` is the typeclass which supports the notation `x ≈ y` where `x y : α`.-/
class HasEquiv (α : Sort u) where
@@ -340,7 +336,7 @@ class HasEquiv (α : Sort u) where
the notion of equivalence is type-dependent. -/
Equiv : α α Sort v
@[inherit_doc] infix:50 "" => HasEquiv.Equiv
@[inheritDoc] infix:50 "" => HasEquiv.Equiv
/-- `EmptyCollection α` is the typeclass which supports the notation `∅`, also written as `{}`. -/
class EmptyCollection (α : Type u) where
@@ -348,8 +344,8 @@ class EmptyCollection (α : Type u) where
It is supported by the `EmptyCollection` typeclass. -/
emptyCollection : α
@[inherit_doc] notation "{" "}" => EmptyCollection.emptyCollection
@[inherit_doc] notation "" => EmptyCollection.emptyCollection
@[inheritDoc] notation "{" "}" => EmptyCollection.emptyCollection
@[inheritDoc] notation "" => EmptyCollection.emptyCollection
/--
`Task α` is a primitive for asynchronous computation.
@@ -365,11 +361,14 @@ structure Task (α : Type u) : Type u where
/-- If `task : Task α` then `task.get : α` blocks the current thread until the
value is available, and then returns the result of the task. -/
get : α
deriving Inhabited, Nonempty
deriving Inhabited
attribute [extern "lean_task_pure"] Task.pure
attribute [extern "lean_task_get_own"] Task.get
instance : [Nonempty α] Nonempty (Task α)
| x => .pure x
namespace Task
/-- Task priority. Tasks with higher priority will always be scheduled before ones with lower priority. -/
abbrev Priority := Nat
@@ -451,10 +450,10 @@ This is the universe-polymorphic version of `PNonScalar`; it is preferred to use
`NonScalar` instead where applicable.
-/
inductive PNonScalar : Type u where
/-- You should not use this function -/
| mk (v : Nat) : PNonScalar
| /-- You should not use this function -/
mk (v : Nat) : PNonScalar
@[simp] protected theorem Nat.add_zero (n : Nat) : n + 0 = n := rfl
@[simp] theorem Nat.add_zero (n : Nat) : n + 0 = n := rfl
theorem optParam_eq (α : Sort u) (default : α) : optParam α default = α := rfl
@@ -482,7 +481,7 @@ Unlike `x ≠ y` (which is notation for `Ne x y`), this is `Bool` valued instead
@[inline] def bne {α : Type u} [BEq α] (a b : α) : Bool :=
!(a == b)
@[inherit_doc] infix:50 " != " => bne
@[inheritDoc] infix:50 " != " => bne
/--
`LawfulBEq α` is a typeclass which asserts that the `BEq α` implementation
@@ -511,7 +510,7 @@ instance : LawfulBEq String := inferInstance
/-! # Logical connectives and equality -/
@[inherit_doc True.intro] def trivial : True :=
@[inheritDoc True.intro] def trivial : True :=
theorem mt {a b : Prop} (h₁ : a b) (h₂ : ¬b) : ¬a :=
fun ha => h₂ (h₁ ha)
@@ -533,7 +532,7 @@ If `h : α = β` is a proof of type equality, then `h.mp : α → β` is the ind
You can prove theorems about the resulting element by induction on `h`, since
`rfl.mp` is definitionally the identity function.
-/
@[macro_inline] def Eq.mp {α β : Sort u} (h : α = β) (a : α) : β :=
@[macroInline] def Eq.mp {α β : Sort u} (h : α = β) (a : α) : β :=
h a
/--
@@ -543,10 +542,9 @@ If `h : α = β` is a proof of type equality, then `h.mpr : β → α` is the in
You can prove theorems about the resulting element by induction on `h`, since
`rfl.mpr` is definitionally the identity function.
-/
@[macro_inline] def Eq.mpr {α β : Sort u} (h : α = β) (b : β) : α :=
@[macroInline] def Eq.mpr {α β : Sort u} (h : α = β) (b : β) : α :=
h b
@[elab_as_elim]
theorem Eq.substr {α : Sort u} {p : α Prop} {a b : α} (h₁ : b = a) (h₂ : p a) : p b :=
h₁ h₂
@@ -560,7 +558,7 @@ and asserts that `a` and `b` are not equal.
@[reducible] def Ne {α : Sort u} (a b : α) :=
¬(a = b)
@[inherit_doc] infix:50 "" => Ne
@[inheritDoc] infix:50 "" => Ne
section Ne
variable {α : Sort u}
@@ -674,9 +672,6 @@ theorem Iff.symm (h : a ↔ b) : b ↔ a :=
theorem Iff.comm : (a b) (b a) :=
Iff.intro Iff.symm Iff.symm
theorem Iff.of_eq (h : a = b) : a b :=
h Iff.refl _
theorem And.comm : a b b a := by
constructor <;> intro h₁, h₂ <;> exact h₂, h₁
@@ -701,7 +696,7 @@ theorem decide_false_eq_false (h : Decidable False) : @decide False h = false :=
/-- Similar to `decide`, but uses an explicit instance -/
@[inline] def toBoolUsing {p : Prop} (d : Decidable p) : Bool :=
decide (h := d)
decide p (h := d)
theorem toBoolUsing_eq_true {p : Prop} (d : Decidable p) (h : p) : toBoolUsing d = true :=
decide_eq_true (inst := d) h
@@ -726,7 +721,7 @@ Synonym for `dite` (dependent if-then-else). We can construct an element `q`
(of any sort, not just a proposition) by cases on whether `p` is true or false,
provided `p` is decidable.
-/
@[macro_inline] def byCases {q : Sort u} [dec : Decidable p] (h1 : p q) (h2 : ¬p q) : q :=
@[macroInline] def byCases {q : Sort u} [dec : Decidable p] (h1 : p q) (h2 : ¬p q) : q :=
match dec with
| isTrue h => h1 h
| isFalse h => h2 h
@@ -767,7 +762,7 @@ variable {p q : Prop}
decidable_of_decidable_of_iff (p := p) (h Iff.rfl)
end
@[macro_inline] instance {p q} [Decidable p] [Decidable q] : Decidable (p q) :=
@[macroInline] instance {p q} [Decidable p] [Decidable q] : Decidable (p q) :=
if hp : p then
if hq : q then isTrue (fun _ => hq)
else isFalse (fun h => absurd (h hp) hq)
@@ -797,13 +792,6 @@ theorem if_neg {c : Prop} {h : Decidable c} (hnc : ¬c) {α : Sort u} {t e : α}
| isTrue hc => absurd hc hnc
| isFalse _ => rfl
/-- Split an if-then-else into cases. The `split` tactic is generally easier to use than this theorem. -/
def iteInduction {c} [inst : Decidable c] {motive : α Sort _} {t e : α}
(hpos : c motive t) (hneg : ¬c motive e) : motive (ite c t e) :=
match inst with
| isTrue h => hpos h
| isFalse h => hneg h
theorem dif_pos {c : Prop} {h : Decidable c} (hc : c) {α : Sort u} {t : c α} {e : ¬ c α} : (dite c t e) = t hc :=
match h with
| isTrue _ => rfl
@@ -944,10 +932,10 @@ transitive and contains `r`. `r⁺ a z` if and only if there exists a sequence
`a r b r ... r z` of length at least 1 connecting `a` to `z`.
-/
inductive TC {α : Sort u} (r : α α Prop) : α α Prop where
/-- If `r a b` then `r⁺ a b`. This is the base case of the transitive closure. -/
| base : a b, r a b TC r a b
/-- The transitive closure is transitive. -/
| trans : a b c, TC r a b TC r b c TC r a c
| /-- If `r a b` then `r⁺ a b`. This is the base case of the transitive closure. -/
base : a b, r a b TC r a b
| /-- The transitive closure is transitive. -/
trans : a b c, TC r a b TC r b c TC r a c
/-! # Subtype -/
@@ -1021,20 +1009,20 @@ instance [DecidableEq α] [DecidableEq β] : DecidableEq (α × β) :=
instance [BEq α] [BEq β] : BEq (α × β) where
beq := fun (a₁, b₁) (a₂, b₂) => a₁ == a₂ && b₁ == b₂
/-- Lexicographical order for products -/
def Prod.lexLt [LT α] [LT β] (s : α × β) (t : α × β) : Prop :=
s.1 < t.1 (s.1 = t.1 s.2 < t.2)
instance [LT α] [LT β] : LT (α × β) where
lt s t := s.1 < t.1 (s.1 = t.1 s.2 < t.2)
instance Prod.lexLtDec
instance prodHasDecidableLt
[LT α] [LT β] [DecidableEq α] [DecidableEq β]
[(a b : α) Decidable (a < b)] [(a b : β) Decidable (a < b)]
: (s t : α × β) Decidable (Prod.lexLt s t) :=
: (s t : α × β) Decidable (s < t) :=
fun _ _ => inferInstanceAs (Decidable (_ _))
theorem Prod.lexLt_def [LT α] [LT β] (s t : α × β) : (Prod.lexLt s t) = (s.1 < t.1 (s.1 = t.1 s.2 < t.2)) :=
theorem Prod.lt_def [LT α] [LT β] (s t : α × β) : (s < t) = (s.1 < t.1 (s.1 = t.1 s.2 < t.2)) :=
rfl
theorem Prod.eta (p : α × β) : (p.1, p.2) = p := rfl
theorem Prod.ext (p : α × β) : (p.1, p.2) = p := by
cases p; rfl
/--
`Prod.map f g : α₁ × β₁ → α₂ × β₂` maps across a pair
@@ -1229,7 +1217,7 @@ protected abbrev liftOn {α : Sort u} {β : Sort v} {r : αα → Prop}
(q : Quot r) (f : α β) (c : (a b : α) r a b f a = f b) : β :=
lift f c q
@[elab_as_elim]
@[elabAsElim]
protected theorem inductionOn {α : Sort u} {r : α α Prop} {motive : Quot r Prop}
(q : Quot r)
(h : (a : α) motive (Quot.mk r a))
@@ -1245,7 +1233,7 @@ variable {r : αα → Prop}
variable {motive : Quot r Sort v}
/-- Auxiliary definition for `Quot.rec`. -/
@[reducible, macro_inline]
@[reducible, macroInline]
protected def indep (f : (a : α) motive (Quot.mk r a)) (a : α) : PSigma motive :=
Quot.mk r a, f a
@@ -1277,7 +1265,7 @@ protected abbrev rec
(q : Quot r) : motive q :=
Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((lift (Quot.indep f) (Quot.indepCoherent f h) q).2)
@[inherit_doc Quot.rec] protected abbrev recOn
@[inheritDoc Quot.rec] protected abbrev recOn
(q : Quot r)
(f : (a : α) motive (Quot.mk r a))
(h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b)
@@ -1351,8 +1339,7 @@ then it lifts to a function on `Quotient s` such that `lift f h (mk a) = f a`.
protected abbrev lift {α : Sort u} {β : Sort v} {s : Setoid α} (f : α β) : ((a b : α) a b f a = f b) Quotient s β :=
Quot.lift f
/-- The analogue of `Quot.ind`: every element of `Quotient s` is of the form `Quotient.mk s a`. -/
protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s Prop} : ((a : α) motive (Quotient.mk s a)) (q : Quotient s) motive q :=
protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s Prop} : ((a : α) motive (Quotient.mk s a)) (q : Quot Setoid.r) motive q :=
Quot.ind
/--
@@ -1362,8 +1349,7 @@ then it lifts to a function on `Quotient s` such that `lift (mk a) f h = f a`.
protected abbrev liftOn {α : Sort u} {β : Sort v} {s : Setoid α} (q : Quotient s) (f : α β) (c : (a b : α) a b f a = f b) : β :=
Quot.liftOn q f c
/-- The analogue of `Quot.inductionOn`: every element of `Quotient s` is of the form `Quotient.mk s a`. -/
@[elab_as_elim]
@[elabAsElim]
protected theorem inductionOn {α : Sort u} {s : Setoid α} {motive : Quotient s Prop}
(q : Quotient s)
(h : (a : α) motive (Quotient.mk s a))
@@ -1379,7 +1365,7 @@ variable {s : Setoid α}
variable {motive : Quotient s Sort v}
/-- The analogue of `Quot.rec` for `Quotient`. See `Quot.rec`. -/
@[inline, elab_as_elim]
@[inline, elabAsElim]
protected def rec
(f : (a : α) motive (Quotient.mk s a))
(h : (a b : α) (p : a b) Eq.ndrec (f a) (Quotient.sound p) = f b)
@@ -1388,7 +1374,7 @@ protected def rec
Quot.rec f h q
/-- The analogue of `Quot.recOn` for `Quotient`. See `Quot.recOn`. -/
@[elab_as_elim]
@[elabAsElim]
protected abbrev recOn
(q : Quotient s)
(f : (a : α) motive (Quotient.mk s a))
@@ -1397,7 +1383,7 @@ protected abbrev recOn
Quot.recOn q f h
/-- The analogue of `Quot.recOnSubsingleton` for `Quotient`. See `Quot.recOnSubsingleton`. -/
@[elab_as_elim]
@[elabAsElim]
protected abbrev recOnSubsingleton
[h : (a : α) Subsingleton (motive (Quotient.mk s a))]
(q : Quotient s)
@@ -1406,7 +1392,7 @@ protected abbrev recOnSubsingleton
Quot.recOnSubsingleton (h := h) q f
/-- The analogue of `Quot.hrecOn` for `Quotient`. See `Quot.hrecOn`. -/
@[elab_as_elim]
@[elabAsElim]
protected abbrev hrecOn
(q : Quotient s)
(f : (a : α) motive (Quotient.mk s a))
@@ -1440,7 +1426,7 @@ protected abbrev liftOn₂
: φ :=
Quotient.lift₂ f c q₁ q₂
@[elab_as_elim]
@[elabAsElim]
protected theorem ind₂
{motive : Quotient s₁ Quotient s₂ Prop}
(h : (a : α) (b : β) motive (Quotient.mk s₁ a) (Quotient.mk s₂ b))
@@ -1451,7 +1437,7 @@ protected theorem ind₂
induction q₂ using Quotient.ind
apply h
@[elab_as_elim]
@[elabAsElim]
protected theorem inductionOn₂
{motive : Quotient s₁ Quotient s₂ Prop}
(q₁ : Quotient s₁)
@@ -1462,7 +1448,7 @@ protected theorem inductionOn₂
induction q₂ using Quotient.ind
apply h
@[elab_as_elim]
@[elabAsElim]
protected theorem inductionOn₃
{s₃ : Setoid φ}
{motive : Quotient s₁ Quotient s₂ Quotient s₃ Prop}
@@ -1507,7 +1493,7 @@ variable {α : Sort uA} {β : Sort uB}
variable {s₁ : Setoid α} {s₂ : Setoid β}
/-- Lift a binary function to a quotient on both arguments. -/
@[elab_as_elim]
@[elabAsElim]
protected abbrev recOnSubsingleton₂
{motive : Quotient s₁ Quotient s₂ Sort uC}
[s : (a : α) (b : β) Subsingleton (motive (Quotient.mk s₁ a) (Quotient.mk s₂ b))]
@@ -1627,7 +1613,7 @@ external type checkers (e.g., Trepplein) that do not implement this feature.
Keep in mind that if you are using Lean as programming language, you are already trusting the Lean compiler and interpreter.
So, you are mainly losing the capability of type checking your development using external checkers.
Recall that the compiler trusts the correctness of all `[implemented_by ...]` and `[extern ...]` annotations.
Recall that the compiler trusts the correctness of all `[implementedBy ...]` and `[extern ...]` annotations.
If an extern function is executed, then the trusted code base will also include the implementation of the associated
foreign function.
-/

View File

@@ -7,7 +7,7 @@ prelude
import Init.WFTactics
import Init.Data.Nat.Basic
import Init.Data.Fin.Basic
import Init.Data.UInt.Basic
import Init.Data.UInt
import Init.Data.Repr
import Init.Data.ToString.Basic
import Init.Util
@@ -123,7 +123,7 @@ unsafe def modifyMUnsafe [Monad m] (a : Array α) (i : Nat) (f : α → m α) :
else
pure a
@[implemented_by modifyMUnsafe]
@[implementedBy modifyMUnsafe]
def modifyM [Monad m] (a : Array α) (i : Nat) (f : α m α) : m (Array α) := do
if h : i < a.size then
let idx := i, h
@@ -158,7 +158,7 @@ def modifyOp (self : Array α) (idx : Nat) (f : αα) : Array α :=
loop 0 b
/-- Reference implementation for `forIn` -/
@[implemented_by Array.forInUnsafe]
@[implementedBy Array.forInUnsafe]
protected def forIn {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Array α) (b : β) (f : α β m (ForInStep β)) : m β :=
let rec loop (i : Nat) (h : i as.size) (b : β) : m β := do
match i, h with
@@ -192,7 +192,7 @@ unsafe def foldlMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Mon
pure init
/-- Reference implementation for `foldlM` -/
@[implemented_by foldlMUnsafe]
@[implementedBy foldlMUnsafe]
def foldlM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : β α m β) (init : β) (as : Array α) (start := 0) (stop := as.size) : m β :=
let fold (stop : Nat) (h : stop as.size) :=
let rec loop (i : Nat) (j : Nat) (b : β) : m β := do
@@ -229,7 +229,7 @@ unsafe def foldrMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Mon
pure init
/-- Reference implementation for `foldrM` -/
@[implemented_by foldrMUnsafe]
@[implementedBy foldrMUnsafe]
def foldrM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α β m β) (init : β) (as : Array α) (start := as.size) (stop := 0) : m β :=
let rec fold (i : Nat) (h : i as.size) (b : β) : m β := do
if i == stop then
@@ -267,17 +267,9 @@ unsafe def mapMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad
unsafeCast <| map 0 (unsafeCast as)
/-- Reference implementation for `mapM` -/
@[implemented_by mapMUnsafe]
@[implementedBy mapMUnsafe]
def mapM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (f : α m β) (as : Array α) : m (Array β) :=
-- Note: we cannot use `foldlM` here for the reference implementation because this calls
-- `bind` and `pure` too many times. (We are not assuming `m` is a `LawfulMonad`)
let rec map (i : Nat) (r : Array β) : m (Array β) := do
if hlt : i < as.size then
map (i+1) (r.push ( f as[i]))
else
pure r
map 0 (mkEmpty as.size)
termination_by map => as.size - i
as.foldlM (fun bs a => do let b f a; pure (bs.push b)) (mkEmpty as.size)
@[inline]
def mapIdxM {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Array α) (f : Fin as.size α m β) : m (Array β) :=
@@ -335,7 +327,7 @@ unsafe def anyMUnsafe {α : Type u} {m : Type → Type w} [Monad m] (p : α
else
pure false
@[implemented_by anyMUnsafe]
@[implementedBy anyMUnsafe]
def anyM {α : Type u} {m : Type Type w} [Monad m] (p : α m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool :=
let any (stop : Nat) (h : stop as.size) :=
let rec loop (j : Nat) : m Bool := do
@@ -460,6 +452,17 @@ def contains [BEq α] (as : Array α) (a : α) : Bool :=
def elem [BEq α] (a : α) (as : Array α) : Bool :=
as.contains a
def reverse (as : Array α) : Array α :=
let n := as.size
let mid := n / 2
let rec rev (as : Array α) (i : Nat) :=
if h : i < mid then
rev (as.swap! i (n - i - 1)) (i+1)
else
as
rev as 0
termination_by _ => mid - i
@[inline] def getEvenElems (as : Array α) : Array α :=
(·.2) <| as.foldl (init := (true, Array.empty)) fun (even, r) a =>
if even then
@@ -504,7 +507,7 @@ end Array
export Array (mkArray)
syntax "#[" withoutPosition(sepBy(term, ", ")) "]" : term
syntax "#[" sepBy(term, ", ") "]" : term
macro_rules
| `(#[ $elems,* ]) => `(List.toArray [ $elems,* ])
@@ -591,7 +594,7 @@ theorem ext (a b : Array α)
have hz₁ : 0 < (a::as).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
have hz₂ : 0 < (b::bs).length := by rw [List.length_cons]; apply Nat.zero_lt_succ
have headEq : a = b := h₂ 0 hz₁ hz₂
have h₁' : as.length = bs.length := by rw [List.length_cons, List.length_cons] at h₁; injection h₁
have h₁' : as.length = bs.length := by rw [List.length_cons, List.length_cons] at h₁; injection h₁; assumption
have h₂' : (i : Nat) (hi₁ : i < as.length) (hi₂ : i < bs.length) as.get i, hi₁ = bs.get i, hi₂ := by
intro i hi₁ hi₂
have hi₁' : i+1 < (a::as).length := by rw [List.length_cons]; apply Nat.succ_lt_succ; assumption
@@ -635,27 +638,7 @@ def indexOf? [BEq α] (a : Array α) (v : α) : Option (Fin a.size) :=
@[simp] theorem size_pop (a : Array α) : a.pop.size = a.size - 1 := by
match a with
| [] => rfl
| a::as => simp [pop, Nat.succ_sub_succ_eq_sub, size]
theorem reverse.termination {i j : Nat} (h : i < j) : j - 1 - (i + 1) < j - i := by
rw [Nat.sub_sub, Nat.add_comm]
exact Nat.lt_of_le_of_lt (Nat.pred_le _) (Nat.sub_succ_lt_self _ _ h)
def reverse (as : Array α) : Array α :=
if h : as.size 1 then
as
else
loop as 0 as.size - 1, Nat.pred_lt (mt (fun h : as.size = 0 => h by decide) h)
where
loop (as : Array α) (i : Nat) (j : Fin as.size) :=
if h : i < j then
have := reverse.termination h
let as := as.swap i, Nat.lt_trans h j.2 j
have : j-1 < as.size := by rw [size_swap]; exact Nat.lt_of_le_of_lt (Nat.pred_le _) j.2
loop as (i+1) j-1, this
else
as
termination_by _ => j - i
| a::as => simp [pop, Nat.succ_sub_succ_eq_sub]
def popWhile (p : α Bool) (as : Array α) : Array α :=
if h : as.size > 0 then
@@ -713,25 +696,21 @@ def erase [BEq α] (as : Array α) (a : α) : Array α :=
| none => as
| some i => as.feraseIdx i
/-- Insert element `a` at position `i`. -/
@[inline] def insertAt (as : Array α) (i : Fin (as.size + 1)) (a : α) : Array α :=
let rec loop (as : Array α) (j : Fin as.size) :=
if i.1 < j then
let j' := j-1, Nat.lt_of_le_of_lt (Nat.pred_le _) j.2
let as := as.swap j' j
loop as j', by rw [size_swap]; exact j'.2
else
as
let j := as.size
let as := as.push a
loop as j, size_push .. j.lt_succ_self
termination_by loop j => j.1
def insertAtAux (i : Nat) (as : Array α) (j : Nat) : Array α :=
if h : i < j then
let as := as.swap! (j-1) j;
insertAtAux i as (j-1)
else
as
/-- Insert element `a` at position `i`. Panics if `i` is not `i ≤ as.size`. -/
def insertAt! (as : Array α) (i : Nat) (a : α) : Array α :=
if h : i as.size then
insertAt as i, Nat.lt_succ_of_le h a
else panic! "invalid index"
/--
Insert element `a` at position `i`.
Pre: `i < as.size` -/
def insertAt (as : Array α) (i : Nat) (a : α) : Array α :=
if i > as.size then panic! "invalid index"
else
let as := as.push a;
as.insertAtAux i as.size
def toListLitAux (a : Array α) (n : Nat) (hsz : a.size = n) : (i : Nat), i a.size List α List α
| 0, _, acc => acc

View File

@@ -67,7 +67,7 @@ where
Monomorphic `Array.mapM`. The internal implementation uses pointer equality, and does not allocate a new array
if the result of each `f a` is a pointer equal value `a`.
-/
@[implemented_by mapMonoMImp] def Array.mapMonoM [Monad m] (as : Array α) (f : α m α) : m (Array α) :=
@[implementedBy mapMonoMImp] def Array.mapMonoM [Monad m] (as : Array α) (f : α m α) : m (Array α) :=
as.mapM f
@[inline] def Array.mapMono (as : Array α) (f : α α) : Array α :=

View File

@@ -13,10 +13,9 @@ namespace Array
-- TODO: remove the [Inhabited α] parameters as soon as we have the tactic framework for automating proof generation and using Array.fget
-- TODO: remove `partial` using well-founded recursion
@[specialize] partial def binSearchAux {α : Type u} {β : Type v} [Inhabited β] (lt : α α Bool) (found : Option α β) (as : Array α) (k : α) : Nat Nat β
@[specialize] partial def binSearchAux {α : Type u} {β : Type v} [Inhabited α] [Inhabited β] (lt : α α Bool) (found : Option α β) (as : Array α) (k : α) : Nat Nat β
| lo, hi =>
if lo <= hi then
let _ := Inhabited.mk k
let m := (lo + hi)/2
let a := as.get! m
if lt a k then binSearchAux lt found as k (m+1) hi
@@ -26,54 +25,52 @@ namespace Array
else found (some a)
else found none
@[inline] def binSearch {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Option α :=
@[inline] def binSearch {α : Type} [Inhabited α] (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Option α :=
if lo < as.size then
let hi := if hi < as.size then hi else as.size - 1
binSearchAux lt id as k lo hi
else
none
@[inline] def binSearchContains {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Bool :=
@[inline] def binSearchContains {α : Type} [Inhabited α] (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Bool :=
if lo < as.size then
let hi := if hi < as.size then hi else as.size - 1
binSearchAux lt Option.isSome as k lo hi
else
false
@[specialize] private partial def binInsertAux {α : Type u} {m : Type u Type v} [Monad m]
@[specialize] private partial def binInsertAux {α : Type u} {m : Type u Type v} [Monad m] [Inhabited α]
(lt : α α Bool)
(merge : α m α)
(add : Unit m α)
(as : Array α)
(k : α) : Nat Nat m (Array α)
| lo, hi =>
let _ := Inhabited.mk k
-- as[lo] < k < as[hi]
let mid := (lo + hi)/2
let midVal := as.get! mid
if lt midVal k then
if mid == lo then do let v add (); pure <| as.insertAt! (lo+1) v
if mid == lo then do let v add (); pure <| as.insertAt (lo+1) v
else binInsertAux lt merge add as k mid hi
else if lt k midVal then
binInsertAux lt merge add as k lo mid
else do
as.modifyM mid <| fun v => merge v
@[specialize] def binInsertM {α : Type u} {m : Type u Type v} [Monad m]
@[specialize] def binInsertM {α : Type u} {m : Type u Type v} [Monad m] [Inhabited α]
(lt : α α Bool)
(merge : α m α)
(add : Unit m α)
(as : Array α)
(k : α) : m (Array α) :=
let _ := Inhabited.mk k
if as.isEmpty then do let v add (); pure <| as.push v
else if lt k (as.get! 0) then do let v add (); pure <| as.insertAt! 0 v
else if lt k (as.get! 0) then do let v add (); pure <| as.insertAt 0 v
else if !lt (as.get! 0) k then as.modifyM 0 <| merge
else if lt as.back k then do let v add (); pure <| as.push v
else if !lt k as.back then as.modifyM (as.size - 1) <| merge
else binInsertAux lt merge add as k 0 (as.size - 1)
@[inline] def binInsert {α : Type u} (lt : α α Bool) (as : Array α) (k : α) : Array α :=
@[inline] def binInsert {α : Type u} [Inhabited α] (lt : α α Bool) (as : Array α) (k : α) : Array α :=
Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k
end Array

View File

@@ -23,6 +23,6 @@ where
| j'+1 =>
have h' : j' < a.size := by subst j; exact Nat.lt_trans (Nat.lt_succ_self _) h
if lt a[j] a[j'] then
swapLoop (a.swap j, h j', h') j' (by rw [size_swap]; assumption; done)
swapLoop (a.swap j, h j', h') j' (by rw [size_swap]; assumption done)
else
a

View File

@@ -51,7 +51,7 @@ termination_by aux j _ => as.size - j
`sizeOf arr[i] < sizeOf arr`, which is useful for well founded recursions
over a nested inductive like `inductive T | mk : Array T → T`. -/
macro "array_get_dec" : tactic =>
`(tactic| first
`(first
| apply sizeOf_get
| apply Nat.lt_trans (sizeOf_get ..); simp_arith)

View File

@@ -8,9 +8,9 @@ import Init.Data.Array.Basic
namespace Array
-- TODO: remove the [Inhabited α] parameters as soon as we have the tactic framework for automating proof generation and using Array.fget
-- TODO: remove `partial` using well-founded recursion
def qpartition (as : Array α) (lt : α α Bool) (lo hi : Nat) : Nat × Array α :=
if h : as.size = 0 then (0, as) else have : Inhabited α := as[0]'(by revert h; cases as.size <;> simp [Nat.zero_lt_succ]) -- TODO: remove
@[inline] def qpartition {α : Type} [Inhabited α] (as : Array α) (lt : α α Bool) (lo hi : Nat) : Nat × Array α :=
let mid := (lo + hi) / 2
let as := if lt (as.get! mid) (as.get! lo) then as.swap! lo mid else as
let as := if lt (as.get! hi) (as.get! lo) then as.swap! lo hi else as
@@ -29,7 +29,7 @@ def qpartition (as : Array α) (lt : αα → Bool) (lo hi : Nat) : Nat ×
loop as lo lo
termination_by _ => hi - j
@[inline] partial def qsort (as : Array α) (lt : α α Bool) (low := 0) (high := as.size - 1) : Array α :=
@[inline] partial def qsort {α : Type} [Inhabited α] (as : Array α) (lt : α α Bool) (low := 0) (high := as.size - 1) : Array α :=
let rec @[specialize] sort (as : Array α) (low high : Nat) :=
if low < high then
let p := qpartition as lt low high;

View File

@@ -57,7 +57,7 @@ def popFront (s : Subarray α) : Subarray α :=
loop (USize.ofNat s.start) b
-- TODO: provide reference implementation
@[implemented_by Subarray.forInUnsafe]
@[implementedBy Subarray.forInUnsafe]
protected opaque forIn {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (s : Subarray α) (b : β) (f : α β m (ForInStep β)) : m β :=
pure b
@@ -104,28 +104,6 @@ def any {α : Type u} (p : α → Bool) (as : Subarray α) : Bool :=
def all {α : Type u} (p : α Bool) (as : Subarray α) : Bool :=
Id.run <| as.allM p
@[inline]
def findSomeRevM? {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Subarray α) (f : α m (Option β)) : m (Option β) :=
let rec @[specialize] find : (i : Nat) i as.size m (Option β)
| 0, _ => pure none
| i+1, h => do
have : i < as.size := Nat.lt_of_lt_of_le (Nat.lt_succ_self _) h
let r f as[i]
match r with
| some _ => pure r
| none =>
have : i as.size := Nat.le_of_lt this
find i this
find as.size (Nat.le_refl _)
@[inline]
def findRevM? {α : Type} {m : Type Type w} [Monad m] (as : Subarray α) (p : α m Bool) : m (Option α) :=
as.findSomeRevM? fun a => return if ( p a) then some a else none
@[inline]
def findRev? {α : Type} (as : Subarray α) (p : α Bool) : Option α :=
Id.run <| as.findRevM? p
end Subarray
namespace Array
@@ -149,11 +127,14 @@ def ofSubarray (s : Subarray α) : Array α := Id.run do
as := as.push a
return as
def extract (as : Array α) (start stop : Nat) : Array α :=
ofSubarray (as.toSubarray start stop)
instance : Coe (Subarray α) (Array α) := ofSubarray
syntax:max term noWs "[" withoutPosition(term ":" term) "]" : term
syntax:max term noWs "[" withoutPosition(term ":") "]" : term
syntax:max term noWs "[" withoutPosition(":" term) "]" : term
syntax:max term noWs "[" term ":" term "]" : term
syntax:max term noWs "[" term ":" "]" : term
syntax:max term noWs "[" ":" term "]" : term
macro_rules
| `($a[$start : $stop]) => `(Array.toSubarray $a $start $stop)

View File

@@ -6,7 +6,7 @@ Author: Leonardo de Moura
prelude
import Init.Data.Array.Basic
import Init.Data.Array.Subarray
import Init.Data.UInt.Basic
import Init.Data.UInt
import Init.Data.Option.Basic
universe u
@@ -67,12 +67,6 @@ def set : (a : ByteArray) → (@& Fin a.size) → UInt8 → ByteArray
def uset : (a : ByteArray) (i : USize) UInt8 i.toNat < a.size ByteArray
| bs, i, v, h => bs.uset i v h
@[extern "lean_byte_array_hash"]
protected opaque hash (a : @& ByteArray) : UInt64
instance : Hashable ByteArray where
hash := ByteArray.hash
def isEmpty (s : ByteArray) : Bool :=
s.size == 0
@@ -127,7 +121,7 @@ partial def toList (bs : ByteArray) : List UInt8 :=
loop 0 b
/-- Reference implementation for `forIn` -/
@[implemented_by ByteArray.forInUnsafe]
@[implementedBy ByteArray.forInUnsafe]
protected def forIn {β : Type v} {m : Type v Type w} [Monad m] (as : ByteArray) (b : β) (f : UInt8 β m (ForInStep β)) : m β :=
let rec loop (i : Nat) (h : i as.size) (b : β) : m β := do
match i, h with
@@ -162,7 +156,7 @@ unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β
pure init
/-- Reference implementation for `foldlM` -/
@[implemented_by foldlMUnsafe]
@[implementedBy foldlMUnsafe]
def foldlM {β : Type v} {m : Type v Type w} [Monad m] (f : β UInt8 m β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : m β :=
let fold (stop : Nat) (h : stop as.size) :=
let rec loop (i : Nat) (j : Nat) (b : β) : m β := do

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