mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-18 10:54:09 +00:00
Compare commits
2 Commits
ptrset
...
parser_wit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4187edf5e7 | ||
|
|
7bc9c12462 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,4 +1,3 @@
|
||||
*.lean text eol=lf
|
||||
*.expected.out -text
|
||||
RELEASES.md merge=union
|
||||
stage0/** binary linguist-generated
|
||||
|
||||
33
.github/workflows/changelog.yml
vendored
33
.github/workflows/changelog.yml
vendored
@@ -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
|
||||
91
.github/workflows/ci.yml
vendored
91
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
||||
19
.github/workflows/nix-ci.yml
vendored
19
.github/workflows/nix-ci.yml
vendored
@@ -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
|
||||
|
||||
6
.github/workflows/pr.yml
vendored
6
.github/workflows/pr.yml
vendored
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
98
RELEASES.md
98
RELEASES.md
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
...
|
||||
-/
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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⟩
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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))
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 [*]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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! [*]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
-/
|
||||
@@ -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
|
||||
-/
|
||||
@@ -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
|
||||
@@ -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 [*])
|
||||
|
||||
|
||||
/-!
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
68
doc/flake.lock
generated
@@ -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": {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -201,7 +201,6 @@ literate=
|
||||
|
||||
{ₙ}{{\ensuremath{_n}}}1
|
||||
{ₘ}{{\ensuremath{_m}}}1
|
||||
{ₚ}{{\ensuremath{_p}}}1
|
||||
{↑}{{\ensuremath{\uparrow}}}1
|
||||
{↓}{{\ensuremath{\downarrow}}}1
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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&
|
||||
```
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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).
|
||||
|
||||
-/
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
-/
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
-/
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
97
doc/setup.md
97
doc/setup.md
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
22
flake.lock
generated
@@ -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"
|
||||
|
||||
32
flake.nix
32
flake.nix
@@ -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;
|
||||
|
||||
@@ -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; };
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
-/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 α :=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user