mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-19 19:34:13 +00:00
Compare commits
3 Commits
parser_wit
...
save
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8149b1aa2 | ||
|
|
00b0c6a758 | ||
|
|
f2669aff78 |
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -76,7 +76,6 @@ jobs:
|
||||
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
|
||||
- name: macOS aarch64
|
||||
os: macos-latest
|
||||
release: true
|
||||
@@ -86,7 +85,6 @@ jobs:
|
||||
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
|
||||
- name: Windows
|
||||
os: windows-2022
|
||||
release: true
|
||||
@@ -173,23 +171,27 @@ jobs:
|
||||
- name: List Install Tree
|
||||
run: |
|
||||
# omit contents of Init/, ...
|
||||
tree --du -h lean-* | grep -E ' (Init|Lean|Lake|LICENSE|[a-z])'
|
||||
tree --du -h lean-* | grep -E ' (Init|Std|Lean|Lake|LICENSE|[a-z])'
|
||||
- name: Pack
|
||||
run: |
|
||||
dir=$(echo lean-*)
|
||||
mkdir pack
|
||||
# high-compression tar.zst + zip for release, fast tar.zst otherwise
|
||||
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
|
||||
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
|
||||
tar cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
|
||||
fi
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: matrix.release
|
||||
with:
|
||||
name: build-${{ matrix.name }}
|
||||
path: pack/*
|
||||
- name: Trigger TPIL build
|
||||
run: |
|
||||
curl -u '${{ github.repository_owner }}:${{ secrets.PUSH_NIGHTLY_TOKEN }}' 'https://api.github.com/repos/leanprover/theorem_proving_in_lean4/dispatches' -X POST -d '{ "event_type": "build_event" }'
|
||||
if: matrix.name == 'Linux release' && github.event_name == 'push' && github.repository == 'leanprover/lean4'
|
||||
- name: Lean stats
|
||||
run: |
|
||||
build/stage1/bin/lean --stats src/Lean.lean
|
||||
|
||||
2
.github/workflows/nix-ci.yml
vendored
2
.github/workflows/nix-ci.yml
vendored
@@ -103,6 +103,6 @@ jobs:
|
||||
if: matrix.name == 'Nix Linux' && github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
- name: Fixup CCache Cache
|
||||
run: |
|
||||
sudo chown -R $USER /nix/var/cache
|
||||
sudo chown -R $USER /nix/var/cache/ccache
|
||||
- name: CCache stats
|
||||
run: CCACHE_DIR=/nix/var/cache/ccache nix run .#nixpkgs.ccache -- -s
|
||||
|
||||
153
RELEASES.md
153
RELEASES.md
@@ -1,143 +1,6 @@
|
||||
v4.0.0-m5 (07 August 2022)
|
||||
Unreleased
|
||||
---------
|
||||
|
||||
* Update Lake to v4.0.0. See the [v4.0.0 release notes](https://github.com/leanprover/lake/releases/tag/v4.0.0) for detailed changes.
|
||||
|
||||
* Mutual declarations in different namespaces are now supported. Example:
|
||||
```lean
|
||||
mutual
|
||||
def Foo.boo (x : Nat) :=
|
||||
match x with
|
||||
| 0 => 1
|
||||
| x + 1 => 2*Boo.bla x
|
||||
|
||||
def Boo.bla (x : Nat) :=
|
||||
match x with
|
||||
| 0 => 2
|
||||
| x+1 => 3*Foo.boo x
|
||||
end
|
||||
```
|
||||
A `namespace` is automatically created for the common prefix. Example:
|
||||
```lean
|
||||
mutual
|
||||
def Tst.Foo.boo (x : Nat) := ...
|
||||
def Tst.Boo.bla (x : Nat) := ...
|
||||
end
|
||||
```
|
||||
expands to
|
||||
```lean
|
||||
namespace Tst
|
||||
mutual
|
||||
def Foo.boo (x : Nat) := ...
|
||||
def Boo.bla (x : Nat) := ...
|
||||
end
|
||||
end Tst
|
||||
```
|
||||
|
||||
* Allow users to install their own `deriving` handlers for existing type classes.
|
||||
See example at [Simple.lean](https://github.com/leanprover/lean4/blob/master/tests/pkg/deriving/UserDeriving/Simple.lean).
|
||||
|
||||
* Add tactic `congr (num)?`. See doc string for additional details.
|
||||
|
||||
* [Missing doc linter](https://github.com/leanprover/lean4/pull/1390)
|
||||
|
||||
* `match`-syntax notation now checks for unused alternatives. See issue [#1371](https://github.com/leanprover/lean4/issues/1371).
|
||||
|
||||
* Auto-completion for structure instance fields. Example:
|
||||
```lean
|
||||
example : Nat × Nat := {
|
||||
f -- HERE
|
||||
}
|
||||
```
|
||||
`fst` now appears in the list of auto-completion suggestions.
|
||||
|
||||
* Auto-completion for dotted identifier notation. Example:
|
||||
```lean
|
||||
example : Nat :=
|
||||
.su -- HERE
|
||||
```
|
||||
`succ` now appears in the list of auto-completion suggestions.
|
||||
|
||||
* `nat_lit` is not needed anymore when declaring `OfNat` instances. See issues [#1389](https://github.com/leanprover/lean4/issues/1389) and [#875](https://github.com/leanprover/lean4/issues/875). Example:
|
||||
```lean
|
||||
inductive Bit where
|
||||
| zero
|
||||
| one
|
||||
|
||||
instance inst0 : OfNat Bit 0 where
|
||||
ofNat := Bit.zero
|
||||
|
||||
instance : OfNat Bit 1 where
|
||||
ofNat := Bit.one
|
||||
|
||||
example : Bit := 0
|
||||
example : Bit := 1
|
||||
```
|
||||
|
||||
* Add `[elabAsElim]` attribute (it is called `elab_as_eliminator` in Lean 3). Motivation: simplify the Mathlib port to Lean 4.
|
||||
|
||||
* `Trans` type class now accepts relations in `Type u`. See this [Zulip issue](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Calc.20mode/near/291214574).
|
||||
|
||||
* Accept unescaped keywords as inductive constructor names. Escaping can often be avoided at use sites via dot notation.
|
||||
```lean
|
||||
inductive MyExpr
|
||||
| let : ...
|
||||
|
||||
def f : MyExpr → MyExpr
|
||||
| .let ... => .let ...
|
||||
```
|
||||
|
||||
* Throw an error message at parametric local instances such as `[Nat -> Decidable p]`. The type class resolution procedure
|
||||
cannot use this kind of local instance because the parameter does not have a forward dependency.
|
||||
This check can be disabled using `set_option checkBinderAnnotations false`.
|
||||
|
||||
* Add option `pp.showLetValues`. When set to `false`, the info view hides the value of `let`-variables in a goal.
|
||||
By default, it is `true` when visualizing tactic goals, and `false` otherwise.
|
||||
See [issue #1345](https://github.com/leanprover/lean4/issues/1345) for additional details.
|
||||
|
||||
* Add option `warningAsError`. When set to true, warning messages are treated as errors.
|
||||
|
||||
* Support dotted notation and named arguments in patterns. Example:
|
||||
```lean
|
||||
def getForallBinderType (e : Expr) : Expr :=
|
||||
match e with
|
||||
| .forallE (binderType := type) .. => type
|
||||
| _ => panic! "forall expected"
|
||||
```
|
||||
|
||||
* "jump-to-definition" now works for function names embedded in the following attributes
|
||||
`@[implementedBy funName]`, `@[tactic parserName]`, `@[termElab parserName]`, `@[commandElab parserName]`,
|
||||
`@[builtinTactic parserName]`, `@[builtinTermElab parserName]`, and `@[builtinCommandElab parserName]`.
|
||||
See [issue #1350](https://github.com/leanprover/lean4/issues/1350).
|
||||
|
||||
* Improve `MVarId` methods discoverability. See [issue #1346](https://github.com/leanprover/lean4/issues/1346).
|
||||
We still have to add similar methods for `FVarId`, `LVarId`, `Expr`, and other objects.
|
||||
Many existing methods have been marked as deprecated.
|
||||
|
||||
* Add attribute `[deprecated]` for marking deprecated declarations. Examples:
|
||||
```lean
|
||||
def g (x : Nat) := x + 1
|
||||
|
||||
-- Whenever `f` is used, a warning message is generated suggesting to use `g` instead.
|
||||
@[deprecated g]
|
||||
def f (x : Nat) := x + 1
|
||||
|
||||
#check f 0 -- warning: `f` has been deprecated, use `g` instead
|
||||
|
||||
-- Whenever `h` is used, a warning message is generated.
|
||||
@[deprecated]
|
||||
def h (x : Nat) := x + 1
|
||||
|
||||
#check h 0 -- warning: `h` has been deprecated
|
||||
```
|
||||
|
||||
* Add type `LevelMVarId` (and abbreviation `LMVarId`) for universe level metavariable ids.
|
||||
Motivation: prevent meta-programmers from mixing up universe and expression metavariable ids.
|
||||
|
||||
* Improve `calc` term and tactic. See [issue #1342](https://github.com/leanprover/lean4/issues/1342).
|
||||
|
||||
* [Relaxed antiquotation parsing](https://github.com/leanprover/lean4/pull/1272) further reduces the need for explicit `$x:p` antiquotation kind annotations.
|
||||
|
||||
* Add support for computed fields in inductives. Example:
|
||||
```lean
|
||||
inductive Exp
|
||||
@@ -153,8 +16,8 @@ v4.0.0-m5 (07 August 2022)
|
||||
|
||||
* Update `a[i]` notation. It is now based on the typeclass
|
||||
```lean
|
||||
class GetElem (cont : Type u) (idx : Type v) (elem : outParam (Type w)) (dom : outParam (cont → idx → Prop)) where
|
||||
getElem (xs : cont) (i : idx) (h : dom xs i) : Elem
|
||||
class GetElem (Cont : Type u) (Idx : Type v) (Elem : outParam (Type w)) (Dom : outParam (Cont → Idx → Prop)) where
|
||||
getElem (xs : Cont) (i : Idx) (h : Dom xs i) : Elem
|
||||
```
|
||||
The notation `a[i]` is now defined as follows
|
||||
```lean
|
||||
@@ -172,13 +35,13 @@ v4.0.0-m5 (07 August 2022)
|
||||
index `i` is not valid.
|
||||
The three new notations are defined as follows:
|
||||
```lean
|
||||
@[inline] def getElem' [GetElem cont idx elem dom] (xs : cont) (i : idx) (h : dom xs i) : elem :=
|
||||
@[inline] def getElem' [GetElem Cont Idx Elem Dom] (xs : Cont) (i : Idx) (h : Dom xs i) : Elem :=
|
||||
getElem xs i h
|
||||
|
||||
@[inline] def getElem! [GetElem cont idx elem dom] [Inhabited elem] (xs : cont) (i : idx) [Decidable (dom xs i)] : elem :=
|
||||
@[inline] def getElem! [GetElem Cont Idx Elem Dom] [Inhabited Elem] (xs : Cont) (i : Idx) [Decidable (Dom xs i)] : Elem :=
|
||||
if h : _ then getElem xs i h else panic! "index out of bounds"
|
||||
|
||||
@[inline] def getElem? [GetElem cont idx elem dom] (xs : cont) (i : idx) [Decidable (dom xs i)] : Option elem :=
|
||||
@[inline] def getElem? [GetElem Cont Idx Elem Dom] (xs : Cont) (i : Idx) [Decidable (Dom xs i)] : Option Elem :=
|
||||
if h : _ then some (getElem xs i h) else none
|
||||
|
||||
macro:max x:term noWs "[" i:term "]" noWs "?" : term => `(getElem? $x $i)
|
||||
@@ -227,7 +90,7 @@ v4.0.0-m5 (07 August 2022)
|
||||
```
|
||||
Note that `Idx`'s type in `GetElem` does not depend on `Cont`. So, you cannot write the instance `instance : GetElem (Array α) (Fin ??) α fun xs i => ...`, but the Lean library comes equipped with the following auxiliary instance:
|
||||
```lean
|
||||
instance [GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i where
|
||||
instance [GetElem Cont Nat Elem Dom] : GetElem Cont (Fin n) Elem fun xs i => Dom xs i where
|
||||
getElem xs i h := getElem xs i.1 h
|
||||
```
|
||||
and helper tactic
|
||||
@@ -253,6 +116,8 @@ v4.0.0-m5 (07 August 2022)
|
||||
end Nat
|
||||
```
|
||||
|
||||
* Update Lake to v3.2.1. See the [v3.2.1 release notes](https://github.com/leanprover/lake/releases/tag/v3.2.1) for detailed changes.
|
||||
|
||||
* Add support for `CommandElabM` monad at `#eval`. Example:
|
||||
```lean
|
||||
import Lean
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Std
|
||||
open Std
|
||||
open Lean
|
||||
|
||||
@@ -103,9 +104,9 @@ syntax entry := ident " ↦ " term:max
|
||||
syntax entry,* "⊢" term : term
|
||||
|
||||
macro_rules
|
||||
| `( $[$xs ↦ $vs],* ⊢ $p) =>
|
||||
| `( $[$xs:ident ↦ $vs:term],* ⊢ $p:term ) =>
|
||||
let xs := xs.map fun x => quote x.getId.toString
|
||||
`(denote (List.toAssocList [$[($xs, $vs)],*]) `[BExpr| $p])
|
||||
`(denote (List.toAssocList [$[( $xs , $vs )],*]) `[BExpr| $p])
|
||||
|
||||
#check b ↦ true ⊢ b ∨ b
|
||||
#eval a ↦ false, b ↦ false ⊢ b ∨ a
|
||||
|
||||
@@ -59,22 +59,12 @@
|
||||
- [Thunk](./thunk.md)
|
||||
- [Task and Thread](./task.md)
|
||||
- [Functions](./functions.md)
|
||||
- [Monads](./monads/intro.md)
|
||||
- [Functor](./monads/functors.lean.md)
|
||||
- [Applicative](./monads/applicatives.lean.md)
|
||||
- [Monad](./monads/monads.lean.md)
|
||||
- [Reader](./monads/readers.lean.md)
|
||||
- [State](./monads/states.lean.md)
|
||||
- [Except](./monads/except.lean.md)
|
||||
- [Transformers](./monads/transformers.lean.md)
|
||||
- [Laws](./monads/laws.lean.md)
|
||||
|
||||
# Other
|
||||
|
||||
- [Frequently Asked Questions](./faq.md)
|
||||
- [Significant Changes from Lean 3](./lean3changes.md)
|
||||
- [Syntax Highlighting Lean in LaTeX](./syntax_highlight_in_latex.md)
|
||||
- [User Widgets](examples/widgets.lean.md)
|
||||
|
||||
# Development
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ Lean provides ways of adding new objects to the environment. The following provi
|
||||
* ``axiom c : α`` : declare a constant named ``c`` of type ``α``, it is postulating that `α` is not an empty type.
|
||||
* ``def c : α := v`` : defines ``c`` to denote ``v``, which should have type ``α``.
|
||||
* ``theorem c : p := v`` : similar to ``def``, but intended to be used when ``p`` is a proposition.
|
||||
* ``opaque c : α (:= v)?`` : declares a opaque constant named ``c`` of type ``α``, the optional value `v` is must have type `α`
|
||||
* ``constant 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 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.
|
||||
@@ -89,8 +89,8 @@ Any of ``def``, ``theorem``, ``axiom``, or ``example`` can take a list of argume
|
||||
is interpreted as ``def foo : (a : α) → β := fun a : α => t``. Similarly, a theorem ``theorem foo (a : α) : p := t`` is interpreted as ``theorem foo : ∀ a : α, p := fun a : α => t``.
|
||||
|
||||
```lean
|
||||
opaque c : Nat
|
||||
opaque d : Nat
|
||||
constant c : Nat
|
||||
constant d : Nat
|
||||
axiom cd_eq : c = d
|
||||
|
||||
def foo : Nat := 5
|
||||
@@ -165,70 +165,70 @@ Below are some common examples of inductive types, many of which are defined in
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace Hide
|
||||
universe u v
|
||||
namespace hide
|
||||
universes u v
|
||||
|
||||
-- BEGIN
|
||||
inductive Empty : Type
|
||||
inductive empty : Type
|
||||
|
||||
inductive Unit : Type
|
||||
| unit : Unit
|
||||
inductive unit : Type
|
||||
| star : unit
|
||||
|
||||
inductive Bool : Type
|
||||
| false : Bool
|
||||
| true : Bool
|
||||
inductive bool : Type
|
||||
| ff : bool
|
||||
| tt : bool
|
||||
|
||||
inductive Prod (α : Type u) (β : Type v) : Type (max u v)
|
||||
| mk : α → β → Prod α β
|
||||
inductive prod (α : Type u) (β : Type v) : Type (max u v)
|
||||
| mk : α → β → prod
|
||||
|
||||
inductive Sum (α : Type u) (β : Type v)
|
||||
| inl : α → Sum α β
|
||||
| inr : β → Sum α β
|
||||
inductive sum (α : Type u) (β : Type v)
|
||||
| inl : α → sum
|
||||
| inr : β → sum
|
||||
|
||||
inductive Sigma (α : Type u) (β : α → Type v)
|
||||
| mk : (a : α) → β a → Sigma α β
|
||||
inductive sigma (α : Type u) (β : α → Type v)
|
||||
| mk : Π a : α, β a → sigma
|
||||
|
||||
inductive false : Prop
|
||||
|
||||
inductive True : Prop
|
||||
| trivial : True
|
||||
inductive true : Prop
|
||||
| trivial : true
|
||||
|
||||
inductive And (p q : Prop) : Prop
|
||||
| intro : p → q → And p q
|
||||
inductive and (p q : Prop) : Prop
|
||||
| intro : p → q → and
|
||||
|
||||
inductive Or (p q : Prop) : Prop
|
||||
| inl : p → Or p q
|
||||
| inr : q → Or p q
|
||||
inductive or (p q : Prop) : Prop
|
||||
| inl : p → or
|
||||
| inr : q → or
|
||||
|
||||
inductive Exists (α : Type u) (p : α → Prop) : Prop
|
||||
| intro : ∀ x : α, p x → Exists α p
|
||||
| intro : ∀ x : α, p x → Exists
|
||||
|
||||
inductive Subtype (α : Type u) (p : α → Prop) : Type u
|
||||
| intro : ∀ x : α, p x → Subtype α p
|
||||
inductive subtype (α : Type u) (p : α → Prop) : Type u
|
||||
| intro : ∀ x : α, p x → subtype
|
||||
|
||||
inductive Nat : Type
|
||||
| zero : Nat
|
||||
| succ : Nat → Nat
|
||||
inductive nat : Type
|
||||
| zero : nat
|
||||
| succ : nat → nat
|
||||
|
||||
inductive List (α : Type u)
|
||||
| nil : List α
|
||||
| cons : α → List α → List α
|
||||
inductive list (α : Type u)
|
||||
| nil : list
|
||||
| cons : α → list → list
|
||||
|
||||
-- full binary tree with nodes and leaves labeled from α
|
||||
inductive BinTree (α : Type u)
|
||||
| leaf : α → BinTree α
|
||||
| node : BinTree α → α → BinTree α → BinTree α
|
||||
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 α
|
||||
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``.
|
||||
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 ``[matchPattern]`` [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 ``[pattern]`` [attribute](TODO: missing link) to ensure that these will be used appropriately by the [Equation Compiler](#the-equation-compiler).
|
||||
|
||||
There are restrictions on the universe ``u`` in the return type ``Sort u`` of the type former. There are also restrictions on the universe ``u`` in the return type ``Sort u`` of the motive of the eliminator. These will be discussed in the next section in the more general setting of inductive families.
|
||||
|
||||
@@ -236,69 +236,69 @@ Lean allows some additional syntactic conveniences. You can omit the return type
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace Hide
|
||||
namespace hide
|
||||
universe u
|
||||
|
||||
-- BEGIN
|
||||
inductive Weekday
|
||||
inductive weekday
|
||||
| sunday | monday | tuesday | wednesday
|
||||
| thursday | friday | saturday
|
||||
|
||||
inductive Nat
|
||||
inductive nat
|
||||
| zero
|
||||
| succ (n : Nat) : Nat
|
||||
| succ (n : nat) : nat
|
||||
|
||||
inductive List (α : Type u)
|
||||
| nil : List α
|
||||
| cons (a : α) (l : List α) : List α
|
||||
inductive list (α : Type u)
|
||||
| nil {} : list
|
||||
| cons (a : α) (l : list) : list
|
||||
|
||||
@[matchPattern]
|
||||
def List.nil' (α : Type u) : List α := List.nil
|
||||
@[pattern]
|
||||
def list.nil' (α : Type u) : list α := list.nil
|
||||
|
||||
def length {α : Type u} : List α → Nat
|
||||
| (List.nil' _) => 0
|
||||
| (List.cons a l) => 1 + length l
|
||||
def length {α : Type u} : list α → Nat
|
||||
| (list.nil' .(α)) := 0
|
||||
| (list.cons a l) := 1 + length l
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
end hide
|
||||
|
||||
The type former, constructors, and eliminator are all part of Lean's axiomatic foundation, which is to say, they are part of the trusted kernel. In addition to these axiomatically declared constants, Lean automatically defines some additional objects in terms of these, and adds them to the environment. These include the following:
|
||||
|
||||
- ``Foo.recOn`` : a variant of the eliminator, in which the major premise comes first
|
||||
- ``Foo.casesOn`` : a restricted version of the eliminator which omits any recursive calls
|
||||
- ``Foo.noConfusionType``, ``Foo.noConfusion`` : functions which witness the fact that the inductive type is freely generated, i.e. that the constructors are injective and that distinct constructors produce distinct objects
|
||||
- ``Foo.below``, ``Foo.ibelow`` : functions used by the equation compiler to implement structural recursion
|
||||
- ``instance : SizeOf Foo`` : a measure which can be used for well-founded recursion
|
||||
- ``foo.rec_on`` : a variant of the eliminator, in which the major premise comes first
|
||||
- ``foo.cases_on`` : a restricted version of the eliminator which omits any recursive calls
|
||||
- ``foo.no_confusion_type``, ``foo.no_confusion`` : functions which witness the fact that the inductive type is freely generated, i.e. that the constructors are injective and that distinct constructors produce distinct objects
|
||||
- ``foo.below``, ``foo.ibelow`` : functions used by the equation compiler to implement structural recursion
|
||||
- ``foo.sizeof`` : a measure which can be used for well-founded recursion
|
||||
|
||||
Note that it is common to put definitions and theorems related to a datatype ``foo`` in a namespace of the same name. This makes it possible to use projection notation described in [Structures](struct.md#structures) and [Namespaces](namespaces.md#namespaces).
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace Hide
|
||||
namespace hide
|
||||
universe u
|
||||
|
||||
-- BEGIN
|
||||
inductive Nat
|
||||
inductive nat
|
||||
| zero
|
||||
| succ (n : Nat) : Nat
|
||||
| succ (n : nat) : nat
|
||||
|
||||
#check Nat
|
||||
#check @Nat.rec
|
||||
#check Nat.zero
|
||||
#check Nat.succ
|
||||
#check nat
|
||||
#check nat.rec
|
||||
#check nat.zero
|
||||
#check nat.succ
|
||||
|
||||
#check @Nat.recOn
|
||||
#check @Nat.casesOn
|
||||
#check @Nat.noConfusionType
|
||||
#check @Nat.noConfusion
|
||||
#check @Nat.brecOn
|
||||
#check Nat.below
|
||||
#check Nat.ibelow
|
||||
#check Nat._sizeOf_1
|
||||
#check nat.rec_on
|
||||
#check nat.cases_on
|
||||
#check nat.no_confusion_type
|
||||
#check @nat.no_confusion
|
||||
#check nat.brec_on
|
||||
#check nat.below
|
||||
#check nat.ibelow
|
||||
#check nat.sizeof
|
||||
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
end hide
|
||||
|
||||
.. _inductive_families:
|
||||
|
||||
@@ -309,39 +309,39 @@ In fact, Lean implements a slight generalization of the inductive types describe
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
inductive Foo (a : α) : Π (c : γ), Sort u
|
||||
| constructor₁ : Π (b : β₁), Foo t₁
|
||||
| constructor₂ : Π (b : β₂), Foo t₂
|
||||
inductive foo (a : α) : Π (c : γ), Sort u
|
||||
| constructor₁ : Π (b : β₁), foo t₁
|
||||
| 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ᵢ``.
|
||||
Here ``(a : α)`` is a context, ``(c : γ)`` is a telescope in context ``(a : α)``, each ``(b : βᵢ)`` is a telescope in the context ``(a : α)`` together with ``(foo : Π (c : γ), Sort u)`` subject to the constraints below, and each ``tᵢ`` is a tuple of terms in the context ``(a : α) (b : βᵢ)`` having the types ``γ``. Instead of defining a single inductive type ``foo a``, we are now defining a family of types ``foo a c`` indexed by elements ``c : γ``. Each constructor, ``constructorᵢ``, places its result in the type ``foo a tᵢ``, the member of the family with index ``tᵢ``.
|
||||
|
||||
The modifications to the scheme in the previous section are straightforward. Suppose the telescope ``(b : βᵢ)`` is ``(b₁ : βᵢ₁) ... (bᵤ : βᵢᵤ)``.
|
||||
|
||||
- As before, an argument ``(bⱼ : βᵢⱼ)`` is *nonrecursive* if ``βᵢⱼ`` does not refer to ``Foo,`` the inductive type being defined. In that case, ``βᵢⱼ`` can be any type, so long as it does not refer to any nonrecursive arguments.
|
||||
- As before, an argument ``(bⱼ : βᵢⱼ)`` is *nonrecursive* if ``βᵢⱼ`` does not refer to ``foo,`` the inductive type being defined. In that case, ``βᵢⱼ`` can be any type, so long as it does not refer to any nonrecursive arguments.
|
||||
|
||||
- An argument ``(bⱼ : βᵢⱼ)`` is *recursive* if ``βᵢⱼ`` is of the form ``Π (d : δ), Foo s`` where ``(d : δ)`` is a telescope which does not refer to ``Foo`` or any nonrecursive arguments and ``s`` is a tuple of terms in context ``(a : α)`` and the previous nonrecursive ``bⱼ``'s with types ``γ``.
|
||||
- An argument ``(bⱼ : βᵢⱼ)`` is *recursive* if ``βᵢⱼ`` is of the form ``Π (d : δ), foo s`` where ``(d : δ)`` is a telescope which does not refer to ``foo`` or any nonrecursive arguments and ``s`` is a tuple of terms in context ``(a : α)`` and the previous nonrecursive ``bⱼ``'s with types ``γ``.
|
||||
|
||||
The declaration of the type ``Foo`` as above results in the addition of the following constants to the environment:
|
||||
The declaration of the type ``foo`` as above results in the addition of the following constants to the environment:
|
||||
|
||||
- the *type former* ``Foo : Π (a : α) (c : γ), Sort u``
|
||||
- for each ``i``, the *constructor* ``Foo.constructorᵢ : Π (a : α) (b : βᵢ), Foo a tᵢ``
|
||||
- the *eliminator* ``Foo.rec``, which takes arguments
|
||||
- the *type former* ``foo : Π (a : α) (c : γ), Sort u``
|
||||
- for each ``i``, the *constructor* ``foo.constructorᵢ : Π (a : α) (b : βᵢ), foo a tᵢ``
|
||||
- the *eliminator* ``foo.rec``, which takes arguments
|
||||
|
||||
+ ``(a : α)`` (the parameters)
|
||||
+ ``{C : Π (c : γ), Foo a c → Type u}`` (the motive of the elimination)
|
||||
+ ``{C : Π (c : γ), foo a c → Type u}`` (the motive of the elimination)
|
||||
+ for each ``i``, the minor premise corresponding to ``constructorᵢ``
|
||||
+ ``(x : Foo a)`` (the major premise)
|
||||
+ ``(x : foo a)`` (the major premise)
|
||||
|
||||
and returns an element of ``C x``. Here, The ith minor premise is a function which takes
|
||||
|
||||
+ ``(b : βᵢ)`` (the arguments to the constructor)
|
||||
+ an argument of type ``Π (d : δ), C s (bⱼ d)`` corresponding to each recursive argument ``(bⱼ : βᵢⱼ)``, where ``βᵢⱼ`` is of the form ``Π (d : δ), Foo s``
|
||||
+ an argument of type ``Π (d : δ), C s (bⱼ d)`` corresponding to each recursive argument ``(bⱼ : βᵢⱼ)``, where ``βᵢⱼ`` is of the form ``Π (d : δ), foo s``
|
||||
|
||||
and returns an element of ``C tᵢ (constructorᵢ a b)``.
|
||||
|
||||
Suppose we set ``F := Foo.rec a C f₁ ... fₙ``. Then for each constructor, we have the definitional reduction, as before:
|
||||
Suppose we set ``F := foo.rec a C f₁ ... fₙ``. Then for each constructor, we have the definitional reduction, as before:
|
||||
|
||||
.. code-block :: text
|
||||
|
||||
@@ -353,24 +353,24 @@ The following are examples of inductive families.
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace Hide
|
||||
namespace hide
|
||||
universe u
|
||||
|
||||
-- BEGIN
|
||||
inductive Vector (α : Type u) : Nat → Type u
|
||||
| nil : Vector 0
|
||||
| succ : Π n, Vector n → Vector (n + 1)
|
||||
inductive vector (α : Type u) : Nat → Type u
|
||||
| nil : vector 0
|
||||
| succ : Π n, vector n → vector (n + 1)
|
||||
|
||||
-- 'IsProd s n' means n is a product of elements of s
|
||||
inductive IsProd (s : Set Nat) : Nat → Prop
|
||||
| base : ∀ n ∈ s, IsProd n
|
||||
| step : ∀ m n, IsProd m → IsProd n → IsProd (m * n)
|
||||
-- 'is_prod s n' means n is a product of elements of s
|
||||
inductive is_prod (s : set Nat) : Nat → Prop
|
||||
| base : ∀ n ∈ s, is_prod n
|
||||
| step : ∀ m n, is_prod m → is_prod n → is_prod (m * n)
|
||||
|
||||
inductive Eq {α : Sort u} (a : α) : α → Prop
|
||||
| refl : Eq a
|
||||
inductive eq {α : Sort u} (a : α) : α → Prop
|
||||
| refl : eq a
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
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.
|
||||
|
||||
@@ -387,47 +387,42 @@ The first generalization allows for multiple inductive types to be defined simul
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
mutual
|
||||
|
||||
inductive Foo (a : α) : Π (c : γ₁), Sort u
|
||||
| constructor₁₁ : Π (b : β₁₁), Foo a t₁₁
|
||||
| constructor₁₂ : Π (b : β₁₂), Foo a t₁₂
|
||||
mutual inductive foo, bar (a : α)
|
||||
with foo : Π (c : γ), Sort u
|
||||
| constructor₁₁ : Π (b : β₁₁), foo t₁₁
|
||||
| constructor₁₂ : Π (b : β₁₂), foo t₁₂
|
||||
...
|
||||
| constructor₁ₙ : Π (b : β₁ₙ), Foo a t₁ₙ
|
||||
|
||||
inductive Bar (a : α) : Π (c : γ₂), Sort u
|
||||
| constructor₂₁ : Π (b : β₂₁), Bar a t₂₁
|
||||
| constructor₂₂ : Π (b : β₂₂), Bar a t₂₂
|
||||
| constructor₁ₙ : Π (b : β₁ₙ), foo t₁ₙ
|
||||
with bar :
|
||||
| constructor₂₁ : Π (b : β₂₁), bar t₂₁
|
||||
| constructor₂₂ : Π (b : β₂₂), bar t₂₂
|
||||
...
|
||||
| constructor₂ₘ : Π (b : β₂ₘ), Bar a t₂ₘ
|
||||
| constructor₂ₘ : Π (b : β₂ₘ), bar 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.
|
||||
|
||||
Here the syntax is shown for defining two inductive families, ``Foo`` and ``Bar``, but any number is allowed. The restrictions are almost the same as for ordinary inductive families. For example, each ``(b : βᵢⱼ)`` is a telescope relative to the context ``(a : α)``. The difference is that the constructors can now have recursive arguments whose return types are any of the inductive families currently being defined, in this case ``Foo`` and ``Bar``. Note that all of the inductive definitions share the same parameters ``(a : α)``, though they may have different indices.
|
||||
A mutual inductive definition is compiled down to an ordinary inductive definition using an extra finite-valued index to distinguish the components. The details of the internal construction are meant to be hidden from most users. Lean defines the expected type formers ``foo`` and ``bar`` and constructors ``constructorᵢⱼ`` from the internal inductive definition. There is no straightforward elimination principle, however. Instead, Lean defines an appropriate ``sizeof`` measure, meant for use with well-founded recursion, with the property that the recursive arguments to a constructor are smaller than the constructed value.
|
||||
|
||||
A mutual inductive definition is compiled down to an ordinary inductive definition using an extra finite-valued index to distinguish the components. The details of the internal construction are meant to be hidden from most users. Lean defines the expected type formers ``Foo`` and ``Bar`` and constructors ``constructorᵢⱼ`` from the internal inductive definition. There is no straightforward elimination principle, however. Instead, Lean defines an appropriate ``sizeOf`` measure, meant for use with well-founded recursion, with the property that the recursive arguments to a constructor are smaller than the constructed value.
|
||||
The second generalization relaxes the restriction that in the recursive definition of ``foo``, ``foo`` can only occur strictly positively in the type of any of its recursive arguments. Specifically, in a nested inductive definition, ``foo`` can appear as an argument to another inductive type constructor, so long as the corresponding parameter occurs strictly positively in the constructors for *that* inductive type. This process can be iterated, so that additional type constructors can be applied to those, and so on.
|
||||
|
||||
The second generalization relaxes the restriction that in the recursive definition of ``Foo``, ``Foo`` can only occur strictly positively in the type of any of its recursive arguments. Specifically, in a nested inductive definition, ``Foo`` can appear as an argument to another inductive type constructor, so long as the corresponding parameter occurs strictly positively in the constructors for *that* inductive type. This process can be iterated, so that additional type constructors can be applied to those, and so on.
|
||||
|
||||
A nested inductive definition is compiled down to an ordinary inductive definition using a mutual inductive definition to define copies of all the nested types simultaneously. Lean then constructs isomorphisms between the mutually defined nested types and their independently defined counterparts. Once again, the internal details are not meant to be manipulated by users. Rather, the type former and constructors are made available and work as expected, while an appropriate ``sizeOf`` measure is generated for use with well-founded recursion.
|
||||
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.
|
||||
|
||||
.. code-block:: 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
|
||||
mutual inductive even, odd
|
||||
with even : Nat → Prop
|
||||
| even_zero : even 0
|
||||
| even_succ : ∀ n, odd n → even (n + 1)
|
||||
with odd : Nat → Prop
|
||||
| odd_succ : ∀ n, even n → odd (n + 1)
|
||||
|
||||
inductive Tree (α : Type u)
|
||||
| mk : α → List (Tree α) → Tree α
|
||||
inductive tree (α : Type u)
|
||||
| mk : α → list tree → tree
|
||||
|
||||
inductive DoubleTree (α : Type u)
|
||||
| mk : α → List (DoubleTree α) × List (DoubleTree α) → DoubleTree α
|
||||
inductive double_tree (α : Type u)
|
||||
| mk : α → list double_tree × list double_tree → double_tree
|
||||
-- END
|
||||
|
||||
.. _the_equation_compiler:
|
||||
@@ -440,9 +435,9 @@ The equation compiler takes an equational description of a function or proof and
|
||||
.. code-block:: text
|
||||
|
||||
def foo (a : α) : Π (b : β), γ
|
||||
| [patterns₁] => t₁
|
||||
| [patterns₁] := t₁
|
||||
...
|
||||
| [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``.
|
||||
|
||||
@@ -455,78 +450,78 @@ Each ``patternsᵢ`` is a sequence of patterns of the same length as ``(b : β)`
|
||||
|
||||
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.
|
||||
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 ``[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.
|
||||
When identifiers are marked with the ``[pattern]`` attribute, the equation compiler unfolds them in the hopes of exposing a constructor. For example, this makes it possible to write ``n+1`` and ``0`` instead of ``nat.succ n`` and ``nat.zero`` in patterns.
|
||||
|
||||
For a nonrecursive definition involving case splits, the defining equations will hold definitionally. With inductive types like ``Char``, ``String``, and ``Fin n``, a case split would produce definitions with an inordinate number of cases. To avoid this, the equation compiler uses ``if ... then ... else`` instead of ``casesOn`` when defining the function. In this case, the defining equations hold definitionally as well.
|
||||
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 ``cases_on`` when defining the function. In this case, the defining equations hold definitionally as well.
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
open Nat
|
||||
open nat
|
||||
|
||||
def sub2 : Nat → Nat
|
||||
| zero => 0
|
||||
| succ zero => 0
|
||||
| succ (succ a) => a
|
||||
| zero := 0
|
||||
| (succ zero) := 0
|
||||
| (succ (succ a)) := a
|
||||
|
||||
def bar : Nat → List Nat → Bool → Nat
|
||||
| 0, _, false => 0
|
||||
| 0, b :: _, _ => b
|
||||
| 0, [], true => 7
|
||||
| a+1, [], false => a
|
||||
| a+1, [], true => a + 1
|
||||
| a+1, b :: _, _ => a + b
|
||||
def bar : Nat → list Nat → bool → Nat
|
||||
| 0 _ ff := 0
|
||||
| 0 (b :: _) _ := b
|
||||
| 0 [] tt := 7
|
||||
| (a+1) [] ff := a
|
||||
| (a+1) [] tt := a + 1
|
||||
| (a+1) (b :: _) _ := a + b
|
||||
|
||||
def baz : Char → Nat
|
||||
| 'A' => 1
|
||||
| 'B' => 2
|
||||
| _ => 3
|
||||
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.
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace Hide
|
||||
namespace hide
|
||||
|
||||
-- BEGIN
|
||||
def fib : Nat → Nat
|
||||
| 0 => 1
|
||||
| 1 => 1
|
||||
| (n+2) => fib (n+1) + fib n
|
||||
def fib : nat → nat
|
||||
| 0 := 1
|
||||
| 1 := 1
|
||||
| (n+2) := fib (n+1) + fib n
|
||||
|
||||
def append {α : Type} : List α → List α → List α
|
||||
| [], l => l
|
||||
| h::t, l => h :: append t l
|
||||
def append {α : Type} : list α → list α → list α
|
||||
| [] l := l
|
||||
| (h::t) l := h :: append t l
|
||||
|
||||
example : append [(1 : Nat), 2, 3] [4, 5] = [1, 2, 3, 4, 5] => rfl
|
||||
example : append [(1 : Nat), 2, 3] [4, 5] = [1, 2, 3, 4, 5] := rfl
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
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``.
|
||||
If structural recursion fails, the equation compiler falls back on well-founded recursion. It tries to infer an instance of ``has_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``.
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
namespace Hide
|
||||
open Nat
|
||||
namespace hide
|
||||
open nat
|
||||
|
||||
-- BEGIN
|
||||
def div : Nat → Nat → Nat
|
||||
| x, y =>
|
||||
| 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
|
||||
have x - y < x,
|
||||
from sub_lt (lt_of_lt_of_le h.left h.right) h.left,
|
||||
div (x - y) y + 1
|
||||
else
|
||||
0
|
||||
|
||||
example (x y : Nat) :
|
||||
div x y = if 0 < y ∧ y ≤ x then div (x - y) y + 1 else 0 :=
|
||||
by rw [div]; rfl
|
||||
by rw [div]
|
||||
-- END
|
||||
|
||||
end Hide
|
||||
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.
|
||||
|
||||
@@ -534,14 +529,13 @@ The equation compiler also allows mutual recursive definitions, with a syntax si
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
mutual
|
||||
def even : Nat → Bool
|
||||
| 0 => true
|
||||
| a+1 => odd a
|
||||
def odd : Nat → Bool
|
||||
| 0 => false
|
||||
| a+1 => even a
|
||||
end
|
||||
mutual def even, odd
|
||||
with even : Nat → bool
|
||||
| 0 := tt
|
||||
| (a+1) := odd a
|
||||
with odd : Nat → bool
|
||||
| 0 := ff
|
||||
| (a+1) := even a
|
||||
|
||||
example (a : Nat) : even (a + 1) = odd a :=
|
||||
by simp [even]
|
||||
@@ -553,39 +547,36 @@ Well-founded recursion is especially useful with [Mutual and Nested Inductive De
|
||||
|
||||
.. code-block:: 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
|
||||
mutual inductive even, odd
|
||||
with even : Nat → Prop
|
||||
| even_zero : even 0
|
||||
| even_succ : ∀ n, odd n → even (n + 1)
|
||||
with odd : Nat → Prop
|
||||
| odd_succ : ∀ n, even n → odd (n + 1)
|
||||
|
||||
open Even Odd
|
||||
open even odd
|
||||
|
||||
theorem not_odd_zero : ¬ Odd 0 := fun x => nomatch x
|
||||
theorem not_odd_zero : ¬ odd 0.
|
||||
|
||||
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
|
||||
mutual theorem even_of_odd_succ, odd_of_even_succ
|
||||
with even_of_odd_succ : ∀ n, odd (n + 1) → even n
|
||||
| _ (odd_succ n h) := h
|
||||
with odd_of_even_succ : ∀ n, even (n + 1) → odd n
|
||||
| _ (even_succ n h) := h
|
||||
|
||||
inductive Term
|
||||
| const : String → Term
|
||||
| app : String → List Term → Term
|
||||
inductive term
|
||||
| const : string → term
|
||||
| app : string → list term → term
|
||||
|
||||
open Term
|
||||
open term
|
||||
|
||||
mutual
|
||||
def num_consts : Term → Nat
|
||||
| .const n => 1
|
||||
| .app n ts => num_consts_lst ts
|
||||
def num_consts_lst : List Term → Nat
|
||||
| [] => 0
|
||||
| t::ts => num_consts t + num_consts_lst ts
|
||||
end
|
||||
mutual def num_consts, num_consts_lst
|
||||
with num_consts : term → nat
|
||||
| (term.const n) := 1
|
||||
| (term.app n ts) := num_consts_lst ts
|
||||
with num_consts_lst : list term → nat
|
||||
| [] := 0
|
||||
| (t::ts) := num_consts t + num_consts_lst ts
|
||||
|
||||
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.
|
||||
|
||||
@@ -593,24 +584,52 @@ The case where patterns are matched against an argument whose type is an inducti
|
||||
|
||||
universe u
|
||||
|
||||
inductive Vector (α : Type u) : Nat → Type u
|
||||
| nil : Vector α 0
|
||||
| cons : α → Vector α n → Vector α (n+1)
|
||||
inductive vector (α : Type u) : Nat → Type u
|
||||
| nil {} : vector 0
|
||||
| cons : Π {n}, α → vector n → vector (n+1)
|
||||
|
||||
namespace Vector
|
||||
namespace vector
|
||||
|
||||
def head {α : Type} : Vector α (n+1) → α
|
||||
| cons h t => h
|
||||
def head {α : Type} : Π {n}, vector α (n+1) → α
|
||||
| n (cons h t) := h
|
||||
|
||||
def tail {α : Type} : Vector α (n+1) → Vector α n
|
||||
| cons h t => t
|
||||
def tail {α : Type} : Π {n}, vector α (n+1) → vector α n
|
||||
| n (cons h t) := t
|
||||
|
||||
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)
|
||||
Π {n}, vector α n → vector β n → vector γ n
|
||||
| 0 nil nil := nil
|
||||
| (n+1) (cons a va) (cons b vb) := cons (f a b) (map va vb)
|
||||
|
||||
end Vector
|
||||
end vector
|
||||
|
||||
An expression of the form ``.(t)`` in a pattern is known as an *inaccessible term*. It is not viewed as part of the pattern; rather, it is explicit information that is used by the elaborator and equation compiler when interpreting the definition. Inaccessible terms do not participate in pattern matching. They are sometimes needed for a pattern to make sense, for example, when a constructor depends on a parameter that is not a pattern-matching variable. In other cases, they can be used to inform the equation compiler that certain arguments do not require a case split, and they can be used to make a definition more readable.
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
universe u
|
||||
|
||||
inductive vector (α : Type u) : Nat → Type u
|
||||
| nil {} : vector 0
|
||||
| cons : Π {n}, α → vector n → vector (n+1)
|
||||
|
||||
namespace vector
|
||||
|
||||
-- BEGIN
|
||||
variable {α : Type u}
|
||||
|
||||
def add [has_add α] :
|
||||
Π {n : Nat}, vector α n → vector α n → vector α n
|
||||
| ._ nil nil := nil
|
||||
| ._ (cons a v) (cons b w) := cons (a + b) (add v w)
|
||||
|
||||
def add' [has_add α] :
|
||||
Π {n : Nat}, vector α n → vector α n → vector α n
|
||||
| .(0) nil nil := nil
|
||||
| .(n+1) (@cons .(α) n a v) (cons b w) := cons (a + b) (add' v w)
|
||||
-- END
|
||||
|
||||
end vector
|
||||
|
||||
.. _match_expressions:
|
||||
|
||||
@@ -622,9 +641,9 @@ Lean supports a ``match ... with ...`` construct similar to ones found in most f
|
||||
.. code-block:: text
|
||||
|
||||
match t₁, ..., tₙ with
|
||||
| p₁₁, ..., p₁ₙ => s₁
|
||||
| p₁₁, ..., p₁ₙ := s₁
|
||||
...
|
||||
| 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.
|
||||
|
||||
@@ -632,28 +651,29 @@ Any ``match`` expression is interpreted using the equation compiler, which gener
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
def foo (n : Nat) (b c : Bool) :=
|
||||
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
|
||||
| 0, tt := 0
|
||||
| m+1, tt := m + 7
|
||||
| 0, ff := 5
|
||||
| m+1, ff := m + 3
|
||||
end
|
||||
|
||||
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.
|
||||
When a ``match`` has only one line, the vertical bar may be left out. In that case, 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.
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
def bar₁ : Nat × Nat → Nat
|
||||
| (m, n) => m + n
|
||||
| (m, n) := m + n
|
||||
|
||||
def bar₂ (p : Nat × Nat) : Nat :=
|
||||
match p with | (m, n) => m + n
|
||||
match p with (m, n) := m + n end
|
||||
|
||||
def bar₃ : Nat × Nat → Nat :=
|
||||
fun ⟨m, n⟩ => m + n
|
||||
|
||||
def bar₄ (p : Nat × Nat) : Nat :=
|
||||
let ⟨m, n⟩ := p; m + n
|
||||
let ⟨m, n⟩ := p in 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:
|
||||
|
||||
@@ -678,113 +698,114 @@ The ``structure`` command in Lean is used to define an inductive data type with
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
structure Foo (a : α) extends Bar, Baz : Sort u :=
|
||||
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``.
|
||||
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``.
|
||||
|
||||
The declaration above is syntactic sugar for an inductive type declaration, and so results in the addition of the following constants to the environment:
|
||||
|
||||
- the type former : ``Foo : Π (a : α), Sort u``
|
||||
- the type former : ``foo : Π (a : α), Sort u``
|
||||
- the single constructor :
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Foo.constructor : Π (a : α) (toBar : Bar) (toBaz : Baz)
|
||||
(field₁ : β₁) ... (fieldₙ : βₙ), Foo a
|
||||
foo.constructor : Π (a : α) (_to_foo : foo) (_to_bar : bar)
|
||||
(field₁ : β₁) ... (fieldₙ : βₙ), foo a
|
||||
|
||||
- the eliminator ``Foo.rec`` for the inductive type with that constructor
|
||||
- the eliminator ``foo.rec`` for the inductive type with that constructor
|
||||
|
||||
In addition, Lean defines
|
||||
|
||||
- the projections : ``fieldᵢ : Π (a : α) (c : Foo) : βᵢ`` for each ``i``
|
||||
- the projections : ``fieldᵢ : Π (a : α) (c : foo) : βᵢ`` for each ``i``
|
||||
|
||||
where any other fields mentioned in ``βᵢ`` are replaced by the relevant projections from ``c``.
|
||||
|
||||
Given ``c : Foo``, Lean offers the following convenient syntax for the projection ``Foo.fieldᵢ c``:
|
||||
Given ``c : foo``, Lean offers the following convenient syntax for the projection ``foo.fieldᵢ c``:
|
||||
|
||||
- *anonymous projections* : ``c.fieldᵢ``
|
||||
- *numbered projections* : ``c.i``
|
||||
|
||||
These can be used in any situation where Lean can infer that the type of ``c`` is of the form ``Foo a``. The convention for anonymous projections is extended to any function ``f`` defined in the namespace ``Foo``, as described in [Namespaces](namespaces.md).
|
||||
These can be used in any situation where Lean can infer that the type of ``c`` is of the form ``foo a``. The convention for anonymous projections is extended to any function ``f`` defined in the namespace ``foo``, as described in [Namespaces](namespaces.md).
|
||||
|
||||
Similarly, Lean offers the following convenient syntax for constructing elements of ``Foo``. They are equivalent to ``Foo.constructor b₁ b₂ f₁ f₁ ... fₙ``, where ``b₁ : Bar``, ``b₂ : Baz``, and each ``fᵢ : βᵢ`` :
|
||||
Similarly, Lean offers the following convenient syntax for constructing elements of ``foo``. They are equivalent to ``foo.constructor b₁ b₂ f₁ f₁ ... fₙ``, where ``b₁ : foo``, ``b₂ : bar``, and each ``fᵢ : βᵢ`` :
|
||||
|
||||
- *anonymous constructor*: ``⟨ b₁, b₂, f₁, ..., fₙ ⟩``
|
||||
- *record notation*:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
{ toBar := b₁, toBaz := b₂, field₁ := f₁, ...,
|
||||
fieldₙ := fₙ : Foo a }
|
||||
{ foo . to_bar := b₁, to_baz := b₂, field₁ := f₁, ...,
|
||||
fieldₙ := fₙ }
|
||||
|
||||
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.
|
||||
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. The tokens ``(|`` and ``|)`` are ascii equivalents.
|
||||
|
||||
When using record notation, you can omit the annotation ``: Foo a`` when Lean can infer that the expression should have a type of the form ``Foo a``. You can replace either ``toBar`` or ``toBaz`` by assignments to *their* fields as well, essentially acting as though the fields of ``Bar`` and ``Baz`` are simply imported into ``Foo``. Finally, record notation also supports
|
||||
When using record notation, you can omit the annotation ``foo .`` when Lean can infer that the expression should have a type of the form ``foo a``. You can replace either ``to_bar`` or ``to_baz`` by assignments to *their* fields as well, essentially acting as though the fields of ``bar`` and ``baz`` are simply imported into ``foo``. Finally, record notation also supports
|
||||
|
||||
- *record updates*: ``{ t with ... fieldᵢ := fᵢ ...}``
|
||||
|
||||
Here ``t`` is a term of type ``Foo a`` for some ``a``. The notation instructs Lean to take values from ``t`` for any field assignment that is omitted from the list.
|
||||
Here ``t`` is a term of type ``foo a`` for some ``a``. The notation instructs Lean to take values from ``t`` for any field assignment that is omitted from the list.
|
||||
|
||||
Lean also allows you to specify a default value for any field in a structure by writing ``(fieldᵢ : βᵢ := t)``. Here ``t`` specifies the value to use when the field ``fieldᵢ`` is left unspecified in an instance of record notation.
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
universe u v
|
||||
universes u v
|
||||
|
||||
structure Vec (α : Type u) (n : Nat) :=
|
||||
(l : List α) (h : l.length = n)
|
||||
structure vec (α : Type u) (n : Nat) :=
|
||||
(l : list α) (h : l.length = n)
|
||||
|
||||
structure Foo (α : Type u) (β : Nat → Type v) : Type (max u v) :=
|
||||
structure foo (α : Type u) (β : Nat → Type v) : Type (max u v) :=
|
||||
(a : α) (n : Nat) (b : β n)
|
||||
|
||||
structure Bar :=
|
||||
structure bar :=
|
||||
(c : Nat := 8) (d : Nat)
|
||||
|
||||
structure Baz extends Foo Nat (Vec Nat), Bar :=
|
||||
(v : Vec Nat n)
|
||||
structure baz extends foo Nat (vec Nat), bar :=
|
||||
(v : vec Nat n)
|
||||
|
||||
#check Foo
|
||||
#check @Foo.mk
|
||||
#check @Foo.rec
|
||||
#check foo
|
||||
#check @foo.mk
|
||||
#check @foo.rec
|
||||
|
||||
#check Foo.a
|
||||
#check Foo.n
|
||||
#check Foo.b
|
||||
#check foo.a
|
||||
#check foo.n
|
||||
#check foo.b
|
||||
|
||||
#check Baz
|
||||
#check @Baz.mk
|
||||
#check @Baz.rec
|
||||
#check baz
|
||||
#check @baz.mk
|
||||
#check @baz.rec
|
||||
|
||||
#check Baz.toFoo
|
||||
#check Baz.toBar
|
||||
#check Baz.v
|
||||
#check baz.to_foo
|
||||
#check baz.to_bar
|
||||
#check baz.v
|
||||
|
||||
def bzz := Vec.mk [1, 2, 3] rfl
|
||||
def bzz := vec.mk [1, 2, 3] rfl
|
||||
|
||||
#check Vec.l bzz
|
||||
#check Vec.h bzz
|
||||
#check vec.l bzz
|
||||
#check vec.h bzz
|
||||
#check bzz.l
|
||||
#check bzz.h
|
||||
#check bzz.1
|
||||
#check bzz.2
|
||||
|
||||
example : Vec Nat 3 := Vec.mk [1, 2, 3] rfl
|
||||
example : Vec Nat 3 := ⟨[1, 2, 3], rfl⟩
|
||||
example : Vec Nat 3 := { l := [1, 2, 3], h := rfl : Vec Nat 3 }
|
||||
example : Vec Nat 3 := { l := [1, 2, 3], h := rfl }
|
||||
example : vec Nat 3 := vec.mk [1, 2, 3] rfl
|
||||
example : vec Nat 3 := ⟨[1, 2, 3], rfl⟩
|
||||
example : vec Nat 3 := (| [1, 2, 3], rfl |)
|
||||
example : vec Nat 3 := { vec . l := [1, 2, 3], h := rfl }
|
||||
example : vec Nat 3 := { l := [1, 2, 3], h := rfl }
|
||||
|
||||
example : Foo Nat (Vec Nat) := ⟨1, 3, bzz⟩
|
||||
example : foo Nat (vec Nat) := ⟨1, 3, bzz⟩
|
||||
|
||||
example : Baz := ⟨⟨1, 3, bzz⟩, ⟨5, 7⟩, bzz⟩
|
||||
example : Baz := { a := 1, n := 3, b := bzz, c := 5, d := 7, v := bzz}
|
||||
def fzz : Foo Nat (Vec Nat) := {a := 1, n := 3, b := bzz}
|
||||
example : baz := ⟨⟨1, 3, bzz⟩, ⟨5, 7⟩, bzz⟩
|
||||
example : baz := { a := 1, n := 3, b := bzz, c := 5, d := 7, v := bzz}
|
||||
def fzz : foo Nat (vec Nat) := {a := 1, n := 3, b := bzz}
|
||||
|
||||
example : Foo Nat (Vec Nat) := { fzz with a := 7 }
|
||||
example : Baz := { fzz with c := 5, d := 7, v := bzz }
|
||||
example : 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
|
||||
example : bar := { c := 8, d := 9 }
|
||||
example : bar := { d := 9 } -- uses the default value for c
|
||||
|
||||
.. _type_classes:
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ stage1/
|
||||
lib/
|
||||
lean/**/*.olean # the Lean library (incl. the compiler) compiled by the previous stage's `lean`
|
||||
temp/**/*.{c,o} # the library extracted to C and compiled by `leanc`
|
||||
libInit.a libLean.a # static libraries of the Lean library
|
||||
libInit.a libStd.a libLean.a # static libraries of the Lean library
|
||||
libleancpp.a # a static library of the C++ sources of Lean
|
||||
libleanshared.so # a dynamic library including the static libraries above
|
||||
bin/
|
||||
|
||||
@@ -43,7 +43,6 @@ In the case of `@[extern]` all *irrelevant* types are removed first; see next se
|
||||
* it is none of the types described above
|
||||
* it is not marked `unsafe`
|
||||
* it has a single constructor with a single parameter of *relevant* type
|
||||
|
||||
is represented by the representation of that parameter's type.
|
||||
|
||||
For example, `{ x : α // p }`, the `Subtype` structure of a value of type `α` and an irrelevant proof, is represented by the representation of `α`.
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
# 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
|
||||
preprocessed using a tool called [LeanInk](https://github.com/leanprover/leanink) and
|
||||
[Alectryon](https://github.com/Kha/alectryon) which produces a generated markdown file. We then run
|
||||
`mdbook` on the result to generate the html pages.
|
||||
|
||||
|
||||
## Settings
|
||||
|
||||
We are using the following settings while editing the markdown docs.
|
||||
@@ -23,84 +14,30 @@ We are using the following settings while editing the markdown docs.
|
||||
|
||||
## Build
|
||||
|
||||
### Using Nix
|
||||
|
||||
Building the manual using Nix (which is what the CI does) is as easy as
|
||||
```bash
|
||||
$ nix build --update-input lean ./doc
|
||||
```
|
||||
You can also open a shell with `mdbook` for running the commands mentioned below with
|
||||
`nix develop ./doc#book`. Otherwise, read on.
|
||||
|
||||
### Manually
|
||||
|
||||
To build and test the book you have to preprocess the .lean files with Alectryon then use our own
|
||||
fork of the Rust tool named [mdbook](https://github.com/leanprover/mdbook). We have our own fork of
|
||||
mdBook with the following additional features:
|
||||
This manual is generated by
|
||||
[mdBook](https://github.com/rust-lang/mdBook). We are currently using
|
||||
a [fork](https://github.com/leanprover/mdBook) of it for the following
|
||||
additional features:
|
||||
|
||||
* Add support for hiding lines in other languages
|
||||
[#1339](https://github.com/rust-lang/mdBook/pull/1339)
|
||||
* Make `mdbook test` call the `lean` compiler to test the snippets.
|
||||
* Ability to test a single chapter at a time which is handy when you
|
||||
are working on that chapter. See the `--chapter` option.
|
||||
* Replace calling `rustdoc --test` from `mdbook test` with `./test`
|
||||
|
||||
So you need to setup these tools before you can run `mdBook`.
|
||||
To build this manual, first install the fork via
|
||||
```bash
|
||||
cargo install --git https://github.com/leanprover/mdBook mdbook
|
||||
```
|
||||
Then use e.g. [`mdbook watch`](https://rust-lang.github.io/mdBook/cli/watch.html) in the `doc/` folder:
|
||||
|
||||
1. install [Rust](https://www.rust-lang.org/tools/install)
|
||||
which provides you with the `cargo` tool for building rust packages.
|
||||
Then run the following:
|
||||
```bash
|
||||
cargo install --git https://github.com/leanprover/mdBook mdbook
|
||||
```
|
||||
```bash
|
||||
cd doc
|
||||
mdbook watch --open # opens the output in `out/` in your default browser
|
||||
```
|
||||
|
||||
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.
|
||||
Run `mdbook test` to test all `lean` code blocks.
|
||||
|
||||
1. Create a Python 3.10 environment.
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
cd lean4/doc
|
||||
alectryon --frontend lean4+markup examples\palindromes.lean --backend webpage -o palindromes.lean.md
|
||||
```
|
||||
|
||||
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:
|
||||
```
|
||||
cd lean4/doc
|
||||
mdbook build
|
||||
```
|
||||
|
||||
This will put the HTML in a `out` folder so you can load `out/index.html` in your web browser and
|
||||
it should look like https://leanprover.github.io/lean4/doc/.
|
||||
|
||||
1. It is also handy to use e.g. [`mdbook watch`](https://rust-lang.github.io/mdBook/cli/watch.html)
|
||||
in the `doc/` folder so that it keeps the html up to date while you are editing.
|
||||
|
||||
```bash
|
||||
mdbook watch --open # opens the output in `out/` in your default browser
|
||||
```
|
||||
|
||||
## Testing Lean Snippets
|
||||
|
||||
You can run the following in the `doc/` folder to test all the lean code snippets.
|
||||
|
||||
```bash
|
||||
mdbook test
|
||||
```
|
||||
|
||||
and you can use the `--chapter` option to test a specific chapter that you are working on:
|
||||
|
||||
```bash
|
||||
mdbook test --chapter Array
|
||||
```
|
||||
|
||||
Use chapter name `?` to get a list of all the chapter names.
|
||||
Using the [Nix setup](make/nix.md), you can instead open a shell with
|
||||
the mdBook fork downloaded from our binary cache:
|
||||
```bash
|
||||
nix develop .#doc
|
||||
```
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import Lean
|
||||
|
||||
open Lean Meta
|
||||
|
||||
def ctor (mvarId : MVarId) (idx : Nat) : MetaM (List MVarId) := do
|
||||
/- Set `MetaM` context using `mvarId` -/
|
||||
withMVarContext mvarId do
|
||||
/- Fail if the metavariable is already assigned. -/
|
||||
checkNotAssigned mvarId `ctor
|
||||
/- Retrieve the target type, instantiateMVars, and use `whnf`. -/
|
||||
let target ← getMVarType' mvarId
|
||||
let .const declName us := target.getAppFn
|
||||
| throwTacticEx `ctor mvarId "target is not an inductive datatype"
|
||||
let .inductInfo { ctors, .. } ← getConstInfo declName
|
||||
| throwTacticEx `ctor mvarId "target is not an inductive datatype"
|
||||
if idx = 0 then
|
||||
throwTacticEx `ctor mvarId "invalid index, it must be > 0"
|
||||
else if h : idx - 1 < ctors.length then
|
||||
apply mvarId (.const ctors[idx - 1] us)
|
||||
else
|
||||
throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} contructors"
|
||||
|
||||
open Elab Tactic
|
||||
|
||||
elab "ctor" idx:num : tactic =>
|
||||
liftMetaTactic (ctor · idx.getNat)
|
||||
|
||||
example (p : Prop) : p := by
|
||||
ctor 1 -- Error
|
||||
|
||||
example (h : q) : p ∨ q := by
|
||||
ctor 0 -- Error
|
||||
exact h
|
||||
|
||||
example (h : q) : p ∨ q := by
|
||||
ctor 3 -- Error
|
||||
exact h
|
||||
|
||||
example (h : q) : p ∨ q := by
|
||||
ctor 2
|
||||
exact h
|
||||
|
||||
example (h : q) : p ∨ q := by
|
||||
ctor 1
|
||||
exact h -- Error
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import Lean
|
||||
|
||||
open Lean Meta
|
||||
|
||||
def ex1 (declName : Name) : MetaM Unit := do
|
||||
let info ← getConstInfo declName
|
||||
IO.println s!"{declName} : {← ppExpr info.type}"
|
||||
if let some val := info.value? then
|
||||
IO.println s!"{declName} : {← ppExpr val}"
|
||||
|
||||
#eval ex1 ``Nat
|
||||
|
||||
def ex2 (declName : Name) : MetaM Unit := do
|
||||
let info ← getConstInfo declName
|
||||
trace[Meta.debug] "{declName} : {info.type}"
|
||||
if let some val := info.value? then
|
||||
trace[Meta.debug] "{declName} : {val}"
|
||||
|
||||
#eval ex2 ``Add.add
|
||||
|
||||
set_option trace.Meta.debug true in
|
||||
#eval ex2 ``Add.add
|
||||
|
||||
def ex3 (declName : Name) : MetaM Unit := do
|
||||
let info ← getConstInfo declName
|
||||
forallTelescope info.type fun xs type => do
|
||||
trace[Meta.debug] "hypotheses : {xs}"
|
||||
trace[Meta.debug] "resultType : {type}"
|
||||
for x in xs do
|
||||
trace[Meta.debug] "{x} : {← inferType x}"
|
||||
|
||||
def myMin [LT α] [DecidableRel (α := α) (·<·)] (a b : α) : α :=
|
||||
if a < b then
|
||||
a
|
||||
else
|
||||
b
|
||||
|
||||
set_option trace.Meta.debug true in
|
||||
#eval ex3 ``myMin
|
||||
|
||||
def ex4 : MetaM Unit := do
|
||||
let nat := mkConst ``Nat
|
||||
withLocalDeclD `a nat fun a =>
|
||||
withLocalDeclD `b nat fun b => do
|
||||
let e ← mkAppM ``HAdd.hAdd #[a, b]
|
||||
trace[Meta.debug] "{e} : {← inferType e}"
|
||||
let e ← mkAdd a (mkNatLit 5)
|
||||
trace[Meta.debug] "added 5: {e}"
|
||||
let e ← whnf e
|
||||
trace[Meta.debug] "whnf: {e}"
|
||||
let e ← reduce e
|
||||
trace[Meta.debug] "reduced: {e}"
|
||||
let a_plus_1 ← mkAdd a (mkNatLit 1)
|
||||
let succ_a := mkApp (mkConst ``Nat.succ) a
|
||||
trace[Meta.debug] "({a_plus_1} =?= {succ_a}) == {← isDefEq a_plus_1 succ_a}"
|
||||
let m ← mkFreshExprMVar nat
|
||||
let m_plus_1 ← mkAdd m (mkNatLit 1)
|
||||
trace[Meta.debug] "m_plus_1: {m_plus_1}"
|
||||
unless (← isDefEq m_plus_1 succ_a) do throwError "isDefEq failed"
|
||||
trace[Meta.debug] "m_plus_1: {m_plus_1}"
|
||||
|
||||
set_option trace.Meta.debug true in
|
||||
#eval ex4
|
||||
|
||||
open Elab Term
|
||||
|
||||
def ex5 : TermElabM Unit := do
|
||||
let nat := Lean.mkConst ``Nat
|
||||
withLocalDeclD `a nat fun a => do
|
||||
withLocalDeclD `b nat fun b => do
|
||||
let ab ← mkAppM ``HAdd.hAdd #[a, b]
|
||||
let stx ← `(fun x => if x < 10 then $(← exprToSyntax ab) + x else x + $(← exprToSyntax a))
|
||||
let e ← elabTerm stx none
|
||||
trace[Meta.debug] "{e} : {← inferType e}"
|
||||
let e := mkApp e (mkNatLit 5)
|
||||
let e ← whnf e
|
||||
trace[Meta.debug] "{e}"
|
||||
|
||||
set_option trace.Meta.debug true in
|
||||
#eval ex5
|
||||
@@ -1,49 +0,0 @@
|
||||
import Lean
|
||||
|
||||
def f (x y : Nat) := x * y + 1
|
||||
|
||||
infixl:65 " *' " => f
|
||||
|
||||
#check 2 *' 3
|
||||
|
||||
notation "unitTest " x => Prod.mk x ()
|
||||
|
||||
#check unitTest 42
|
||||
|
||||
notation "parenthesisTest " x => Nat.sub (x)
|
||||
#check parenthesisTest 12
|
||||
|
||||
def Set (α : Type u) := α → Prop
|
||||
def setOf {α : Type} (p : α → Prop) : Set α := p
|
||||
notation "{ " x " | " p " }" => setOf (fun x => p)
|
||||
|
||||
#check { x | x ≤ 1 }
|
||||
|
||||
notation "cdotTest " "(" x ", " y ")" => Prod.map (· + 1) (1 + ·) (x, y)
|
||||
|
||||
#check cdotTest (13, 12)
|
||||
|
||||
notation "tupleFunctionTest " "(" x ", " y ")"=> Prod.map (Nat.add 1) (Nat.add 2) (x, y)
|
||||
|
||||
#check tupleFunctionTest (15, 12)
|
||||
|
||||
notation "diag " x => Prod.mk x x
|
||||
|
||||
#check diag 12
|
||||
|
||||
open Lean Meta PrettyPrinter Delaborator SubExpr in
|
||||
@[delab app.Prod.mk] def delabDoubleRhsTest : Delab := do
|
||||
let e ← getExpr
|
||||
let #[_, _, x, y] := e.getAppArgs | failure
|
||||
guard (← isDefEq x y)
|
||||
let stx ← withAppArg delab
|
||||
`(diag $stx)
|
||||
|
||||
#check diag 3
|
||||
#check (3, 3)
|
||||
#check (3, 4)
|
||||
#check (2+1, 3)
|
||||
#check (true, true)
|
||||
|
||||
|
||||
|
||||
@@ -176,10 +176,10 @@ 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 =>
|
||||
`((have h : $lhs = $rhs :=
|
||||
`((have h : $lhs:term = $rhs:term :=
|
||||
-- TODO: replace with linarith
|
||||
by simp_arith at *; apply Nat.le_antisymm <;> assumption
|
||||
try subst $lhs))
|
||||
try subst $lhs:term))
|
||||
|
||||
/-!
|
||||
The `by_cases' e` is just the regular `by_cases` followed by `simp` using all
|
||||
@@ -191,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 =>
|
||||
`(by_cases $e <;> simp [*])
|
||||
`(by_cases $e:term <;> simp [*])
|
||||
|
||||
|
||||
/-!
|
||||
|
||||
@@ -48,7 +48,7 @@ inductive Term' (rep : Ty → Type) : Ty → Type
|
||||
| plus : Term' rep .nat → Term' rep .nat → Term' rep .nat
|
||||
| lam : (rep dom → Term' rep ran) → Term' rep (.fn dom ran)
|
||||
| app : Term' rep (.fn dom ran) → Term' rep dom → Term' rep ran
|
||||
| let : Term' rep ty₁ → (rep ty₁ → Term' rep ty₂) → Term' rep ty₂
|
||||
| «let» : Term' rep ty₁ → (rep ty₁ → Term' rep ty₂) → Term' rep ty₂
|
||||
|
||||
/-!
|
||||
Lean accepts this definition because our embedded functions now merely take variables as
|
||||
@@ -62,6 +62,7 @@ choices of `rep` type family
|
||||
-/
|
||||
|
||||
open Ty (nat fn)
|
||||
open Term'
|
||||
|
||||
namespace FirstTry
|
||||
|
||||
@@ -71,11 +72,11 @@ def Term (ty : Ty) := (rep : Ty → Type) → Term' rep ty
|
||||
In the next two example, note how each is written as a function over a `rep` choice,
|
||||
such that the specific choice has no impact on the structure of the term.
|
||||
-/
|
||||
def add : Term (fn nat (fn nat nat)) := fun _rep =>
|
||||
.lam fun x => .lam fun y => .plus (.var x) (.var y)
|
||||
def add : Term (fn nat (fn nat nat)) := fun rep =>
|
||||
lam fun x => lam fun y => plus (var x) (var y)
|
||||
|
||||
def three_the_hard_way : Term nat := fun rep =>
|
||||
.app (.app (add rep) (.const 1)) (.const 2)
|
||||
app (app (add rep) (const 1)) (const 2)
|
||||
|
||||
end FirstTry
|
||||
|
||||
@@ -89,10 +90,10 @@ we can completely hide `rep` in these examples.
|
||||
def Term (ty : Ty) := {rep : Ty → Type} → Term' rep ty
|
||||
|
||||
def add : Term (fn nat (fn nat nat)) :=
|
||||
.lam fun x => .lam fun y => .plus (.var x) (.var y)
|
||||
lam fun x => lam fun y => plus (var x) (var y)
|
||||
|
||||
def three_the_hard_way : Term nat :=
|
||||
.app (.app add (.const 1)) (.const 2)
|
||||
app (app add (const 1)) (const 2)
|
||||
|
||||
/-!
|
||||
It may not be at all obvious that the PHOAS representation admits the crucial computable
|
||||
@@ -106,12 +107,12 @@ pass beneath. For our current choice of `Unit` data, we always pass `()`.
|
||||
-/
|
||||
|
||||
def countVars : Term' (fun _ => Unit) ty → Nat
|
||||
| .var _ => 1
|
||||
| .const _ => 0
|
||||
| .plus a b => countVars a + countVars b
|
||||
| .app f a => countVars f + countVars a
|
||||
| .lam b => countVars (b ())
|
||||
| .let a b => countVars a + countVars (b ())
|
||||
| var h => 1
|
||||
| const n => 0
|
||||
| plus a b => countVars a + countVars b
|
||||
| app f a => countVars f + countVars a
|
||||
| lam b => countVars (b ())
|
||||
| «let» a b => countVars a + countVars (b ())
|
||||
|
||||
/-! We can now easily prove that `add` has two variables by using reflexivity -/
|
||||
|
||||
@@ -127,14 +128,14 @@ We also use the string interpolation available in Lean. For example, `s!"x_{i}"`
|
||||
-/
|
||||
def pretty (e : Term' (fun _ => String) ty) (i : Nat := 1) : String :=
|
||||
match e with
|
||||
| .var s => s
|
||||
| .const n => toString n
|
||||
| .app f a => s!"({pretty f i} {pretty a i})"
|
||||
| .plus a b => s!"({pretty a i} + {pretty b i})"
|
||||
| .lam f =>
|
||||
| var s => s
|
||||
| const n => toString n
|
||||
| app f a => s!"({pretty f i} {pretty a i})"
|
||||
| plus a b => s!"({pretty a i} + {pretty b i})"
|
||||
| lam f =>
|
||||
let x := s!"x_{i}"
|
||||
s!"(fun {x} => {pretty (f x) (i+1)})"
|
||||
| .let a b =>
|
||||
| «let» a b =>
|
||||
let x := s!"x_{i}"
|
||||
s!"(let {x} := {pretty a i}; => {pretty (b x) (i+1)}"
|
||||
|
||||
@@ -150,12 +151,12 @@ new variables are added, but they are only tagged with their own term equivalent
|
||||
that this function squash is parameterized over a specific `rep` choice.
|
||||
-/
|
||||
def squash : Term' (Term' rep) ty → Term' rep ty
|
||||
| .var e => e
|
||||
| .const n => .const n
|
||||
| .plus a b => .plus (squash a) (squash b)
|
||||
| .lam f => .lam fun x => squash (f (.var x))
|
||||
| .app f a => .app (squash f) (squash a)
|
||||
| .let a b => .let (squash a) fun x => squash (b (.var x))
|
||||
| var e => e
|
||||
| const n => const n
|
||||
| plus a b => plus (squash a) (squash b)
|
||||
| lam f => lam fun x => squash (f (.var x))
|
||||
| app f a => app (squash f) (squash a)
|
||||
| «let» a b => «let» (squash a) fun x => squash (b (.var x))
|
||||
|
||||
/-!
|
||||
To define the final substitution function over terms with single free variables, we define
|
||||
@@ -180,7 +181,7 @@ We can view `Term1` as a term with hole. In the following example,
|
||||
the hole `_` is instantiated by `subst` with `three_the_hard_way`
|
||||
-/
|
||||
|
||||
#eval pretty <| subst (fun x => .plus (.var x) (.const 5)) three_the_hard_way
|
||||
#eval pretty <| subst (fun x => plus (var x) (const 5)) three_the_hard_way
|
||||
|
||||
/-!
|
||||
One further development, which may seem surprising at first,
|
||||
@@ -191,12 +192,12 @@ The attribute `[simp]` instructs Lean to always try to unfold `denote` applicati
|
||||
the `simp` tactic. We also say this is a hint for the Lean term simplifier.
|
||||
-/
|
||||
@[simp] def denote : Term' Ty.denote ty → ty.denote
|
||||
| .var x => x
|
||||
| .const n => n
|
||||
| .plus a b => denote a + denote b
|
||||
| .app f a => denote f (denote a)
|
||||
| .lam f => fun x => denote (f x)
|
||||
| .let a b => denote (b (denote a))
|
||||
| var x => x
|
||||
| const n => n
|
||||
| plus a b => denote a + denote b
|
||||
| app f a => denote f (denote a)
|
||||
| lam f => fun x => denote (f x)
|
||||
| «let» a b => denote (b (denote a))
|
||||
|
||||
example : denote three_the_hard_way = 3 :=
|
||||
rfl
|
||||
@@ -212,15 +213,15 @@ We now define the constant folding optimization that traverses a term if replace
|
||||
`plus (const m) (const n)` with `const (n+m)`.
|
||||
-/
|
||||
@[simp] def constFold : Term' rep ty → Term' rep ty
|
||||
| .var x => .var x
|
||||
| .const n => .const n
|
||||
| .app f a => .app (constFold f) (constFold a)
|
||||
| .lam f => .lam fun x => constFold (f x)
|
||||
| .let a b => .let (constFold a) fun x => constFold (b x)
|
||||
| .plus a b =>
|
||||
| var x => var x
|
||||
| const n => const n
|
||||
| app f a => app (constFold f) (constFold a)
|
||||
| lam f => lam fun x => constFold (f x)
|
||||
| «let» a b => «let» (constFold a) fun x => constFold (b x)
|
||||
| plus a b =>
|
||||
match constFold a, constFold b with
|
||||
| .const n, .const m => .const (n+m)
|
||||
| a', b' => .plus a' b'
|
||||
| const n, const m => const (n+m)
|
||||
| a', b' => plus a' b'
|
||||
|
||||
/-!
|
||||
The correctness of the `constFold` is proved using induction, case-analysis, and the term simplifier.
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
import Lean
|
||||
open Lean Widget
|
||||
|
||||
/-!
|
||||
# The user-widgets system
|
||||
|
||||
Proving and programming are inherently interactive tasks. Lots of mathematical objects and data
|
||||
structures are visual in nature. *User widgets* let you associate custom interactive UIs with
|
||||
sections of a Lean document. User widgets are rendered in the Lean infoview.
|
||||
|
||||

|
||||
|
||||
## Trying it out
|
||||
|
||||
To try it out, simply type in the following code and place your cursor over the `#widget` command.
|
||||
-/
|
||||
|
||||
@[widget]
|
||||
def helloWidget : UserWidgetDefinition where
|
||||
name := "Hello"
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
export default function(props) {
|
||||
const name = props.name || 'world'
|
||||
return React.createElement('p', {}, name + '!')
|
||||
}"
|
||||
|
||||
#widget helloWidget .null
|
||||
|
||||
/-!
|
||||
If you want to dive into a full sample right away, check out
|
||||
[`RubiksCube`](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/).
|
||||
Below, we'll explain the system piece by piece.
|
||||
|
||||
⚠️ WARNING: All of the user widget APIs are **unstable** and subject to breaking changes.
|
||||
|
||||
## Widget sources and instances
|
||||
|
||||
A *widget source* is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
|
||||
which exports a [React component](https://reactjs.org/docs/components-and-props.html). To access
|
||||
React, the module must use `import * as React from 'react'`. Our first example of a widget source
|
||||
is of course the value of `helloWidget.javascript`.
|
||||
|
||||
We can register a widget source with the `@[widget]` attribute, giving it a friendlier name
|
||||
in the `name` field. This is bundled together in a `UserWidgetDefinition`.
|
||||
|
||||
A *widget instance* is then the identifier of a `UserWidgetDefinition` (so `` `helloWidget ``,
|
||||
not `"Hello"`) associated with a range of positions in the Lean source code. Widget instances
|
||||
are stored in the *infotree* in the same manner as other information about the source file
|
||||
such as the type of every expression. In our example, the `#widget` command stores a widget instance
|
||||
with the entire line as its range. We can think of a widget instance as an instruction for the
|
||||
infoview: "when the user places their cursor here, please render the following widget".
|
||||
|
||||
Every widget instance also contains a `props : Json` value. This value is passed as an argument
|
||||
to the React component. In our first invocation of `#widget`, we set it to `.null`. Try out what
|
||||
happens when you type in:
|
||||
-/
|
||||
|
||||
#widget helloWidget (Json.mkObj [("name", "<your name here>")])
|
||||
|
||||
/-!
|
||||
💡 NOTE: The RPC system presented below does not depend on JavaScript. However the primary use case
|
||||
is the web-based infoview in VSCode.
|
||||
|
||||
## Querying the Lean server
|
||||
|
||||
Besides enabling us to create cool client-side visualizations, user widgets come with the ability
|
||||
to communicate with the Lean server. Thanks to this, they have the same metaprogramming capabilities
|
||||
as custom elaborators or the tactic framework. To see this in action, let's implement a `#check`
|
||||
command as a web input form. This example assumes some familiarity with React.
|
||||
|
||||
The first thing we'll need is to create an *RPC method*. Meaning "Remote Procedure Call", this
|
||||
is basically a Lean function callable from widget code (possibly remotely over the internet).
|
||||
Our method will take in the `name : Name` of a constant in the environment and return its type.
|
||||
By convention, we represent the input data as a `structure`. Since it will be sent over from JavaScript,
|
||||
we need `FromJson` and `ToJson`. We'll see below why the position field is needed.
|
||||
-/
|
||||
|
||||
structure GetTypeParams where
|
||||
/-- Name of a constant to get the type of. -/
|
||||
name : Name
|
||||
/-- Position of our widget instance in the Lean file. -/
|
||||
pos : Lsp.Position
|
||||
deriving FromJson, ToJson
|
||||
|
||||
/-!
|
||||
After its arguments, we define the `getType` method. Every RPC method executes in the `RequestM`
|
||||
monad and must return a `RequestTask α` where `α` is its "actual" return type. The `Task` is so
|
||||
that requests can be handled concurrently. A first guess for `α` might be `Expr`. However,
|
||||
expressions in general can be large objects which depend on an `Environment` and `LocalContext`.
|
||||
Thus we cannot directly serialize an `Expr` and send it to the widget. Instead, there are two
|
||||
options:
|
||||
- One is to send a *reference* which points to an object residing on the server. From JavaScript's
|
||||
point of view, references are entirely opaque, but they can be sent back to other RPC methods for
|
||||
further processing.
|
||||
- Two is to pretty-print the expression and send its textual representation called `CodeWithInfos`.
|
||||
This representation contains extra data which the infoview uses for interactivity. We take this
|
||||
strategy here.
|
||||
|
||||
RPC methods execute in the context of a file, but not any particular `Environment` so they don't
|
||||
know about the available `def`initions and `theorem`s. Thus, we need to pass in a position at which
|
||||
we want to use the local `Environment`. This is why we store it in `GetTypeParams`. The `withWaitFindSnapAtPos`
|
||||
method launches a concurrent computation whose job is to find such an `Environment` and a bit
|
||||
more information for us, in the form of a `snap : Snapshot`. With this in hand, we can call
|
||||
`MetaM` procedures to find out the type of `name` and pretty-print it.
|
||||
-/
|
||||
|
||||
open Server RequestM in
|
||||
@[serverRpcMethod]
|
||||
def getType (params : GetTypeParams) : RequestM (RequestTask CodeWithInfos) :=
|
||||
withWaitFindSnapAtPos params.pos fun snap => do
|
||||
runTermElabM snap do
|
||||
let name ← resolveGlobalConstNoOverloadCore params.name
|
||||
let some c ← Meta.getConst? name
|
||||
| throwThe RequestError ⟨.invalidParams, s!"no constant named '{name}'"⟩
|
||||
Widget.ppExprTagged c.type
|
||||
|
||||
/-!
|
||||
## Using infoview components
|
||||
|
||||
Now that we have all we need on the server side, let's write the widget source. By importing
|
||||
`@leanprover/infoview`, widgets can render UI components used to implement the infoview itself.
|
||||
For example, the `<InteractiveCode>` component displays expressions with `term : type` tooltips
|
||||
as seen in the goal view. We will use it to implement our custom `#check` display.
|
||||
|
||||
⚠️ 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 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]
|
||||
def checkWidget : UserWidgetDefinition where
|
||||
name := "#check as a service"
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
const e = React.createElement;
|
||||
import { RpcContext, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview';
|
||||
|
||||
export default function(props) {
|
||||
const rs = React.useContext(RpcContext)
|
||||
const [name, setName] = React.useState('getType')
|
||||
|
||||
const [status, val, err] = useAsync(() =>
|
||||
rs.call('getType', { name, pos: props.pos }), [name, rs, props.pos])
|
||||
|
||||
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)
|
||||
}
|
||||
"
|
||||
|
||||
/-!
|
||||
Finally we can try out the widget.
|
||||
-/
|
||||
|
||||
#widget checkWidget .null
|
||||
|
||||
/-!
|
||||

|
||||
|
||||
## Building widget sources
|
||||
|
||||
While typing JavaScript inline is fine for a simple example, for real developments we want to use
|
||||
packages from NPM, a proper build system, and JSX. Thus, most actual widget sources are built with
|
||||
Lake and NPM. They consist of multiple files and may import libraries which don't work as ESModules
|
||||
by default. On the other hand a widget source must be a single, self-contained ESModule in the form
|
||||
of a string. Readers familiar with web development may already have guessed that to obtain such a
|
||||
string, we need a *bundler*. Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/)
|
||||
and [`esbuild`](https://esbuild.github.io/). If we go with `rollup.js`, to make a widget work with
|
||||
the infoview we need to:
|
||||
- Set [`output.format`](https://rollupjs.org/guide/en/#outputformat) to `'es'`.
|
||||
- [Externalize](https://rollupjs.org/guide/en/#external) `react`, `react-dom`, `@leanprover/infoview`.
|
||||
These libraries are already loaded by the infoview so they should not be bundled.
|
||||
|
||||
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).
|
||||
-/
|
||||
@@ -1,5 +0,0 @@
|
||||
(this chapter is rendered by Alectryon in the CI)
|
||||
|
||||
```lean
|
||||
{{#include widgets.lean}}
|
||||
```
|
||||
@@ -222,7 +222,7 @@ def f2 (a b c : Bool) : Bool :=
|
||||
def p : Nat × Bool := (1, false)
|
||||
|
||||
section
|
||||
variable (a b c : Nat) (p : Nat × bool)
|
||||
variables (a b c : Nat) (p : Nat × bool)
|
||||
|
||||
#check (1, 2)
|
||||
#check p.1 * 2
|
||||
@@ -232,8 +232,8 @@ end
|
||||
|
||||
/- lists -/
|
||||
section
|
||||
variable x y z : Nat
|
||||
variable xs ys zs : list Nat
|
||||
variables x y z : Nat
|
||||
variables xs ys zs : list Nat
|
||||
open list
|
||||
|
||||
#check (1 :: xs) ++ (y :: zs) ++ [1,2,3]
|
||||
@@ -243,7 +243,7 @@ end
|
||||
|
||||
/- sets -/
|
||||
section
|
||||
variable s t u : set Nat
|
||||
variables s t u : set Nat
|
||||
|
||||
#check ({1, 2, 3} ∩ s) ∪ ({x | x < 7} ∩ t)
|
||||
end
|
||||
@@ -276,7 +276,7 @@ Finally, for data types with one constructor, one destruct an element by pattern
|
||||
.. code-block:: lean
|
||||
|
||||
universes u v
|
||||
variable {α : Type u} {β : Type v}
|
||||
variables {α : Type u} {β : Type v}
|
||||
|
||||
def p : Nat × ℤ := ⟨1, 2⟩
|
||||
#check p.fst
|
||||
@@ -369,7 +369,7 @@ Here is an example:
|
||||
|
||||
.. code-block:: lean
|
||||
|
||||
variable (a b c d e : Nat)
|
||||
variables (a b c d e : Nat)
|
||||
variable h1 : a = b
|
||||
variable h2 : b = c + 1
|
||||
variable h3 : c = d
|
||||
|
||||
6
doc/flake.lock
generated
6
doc/flake.lock
generated
@@ -117,11 +117,11 @@
|
||||
"mdBook": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1660074464,
|
||||
"narHash": "sha256-W30G7AeWBjdJE/CQZJU5vJjaDGZtpmxEKNMEvaYtuF8=",
|
||||
"lastModified": 1644567966,
|
||||
"narHash": "sha256-fqdb2AUAMmi54vfa2qOF7VMFvN3rNku8/kUh1YEW86g=",
|
||||
"owner": "leanprover",
|
||||
"repo": "mdBook",
|
||||
"rev": "9321c10c502cd59eea8afc4325a84eab3ddf9391",
|
||||
"rev": "b7a9bc48e9881087cac684d0c6c2c0a3583417e8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
# necessary for `additional-css`...?
|
||||
cp -r --no-preserve=mode $src/doc/* .
|
||||
# overwrite stub .lean.md files
|
||||
cp -r ${inked}/* .
|
||||
cp -r ${examples}/* examples/
|
||||
mdbook build -d $out
|
||||
'';
|
||||
};
|
||||
@@ -79,21 +79,16 @@
|
||||
doCheck = false;
|
||||
};
|
||||
renderLean = name: file: runCommandNoCC "${name}.md" { buildInputs = [ alectryon ]; } ''
|
||||
mkdir -p $(basename $out/${name})
|
||||
mkdir -p $out/examples
|
||||
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}");
|
||||
renderDir = name: dir: let
|
||||
ents = builtins.readDir dir;
|
||||
inputs = lib.filterAttrs (n: t: builtins.match ".*\.lean" n != null && t == "regular") ents;
|
||||
outputs = lib.mapAttrs (n: _: renderLean n "${dir}/${n}") inputs;
|
||||
in
|
||||
outputs // symlinkJoin { inherit name; paths = lib.attrValues outputs; };
|
||||
inked = renderDir ./.;
|
||||
examples = renderDir "examples" ./examples;
|
||||
doc = book;
|
||||
};
|
||||
defaultPackage = self.packages.${system}.doc;
|
||||
|
||||
@@ -1111,9 +1111,9 @@ hljs.registerLanguage("lean", function(hljs) {
|
||||
'axiom constant ' +
|
||||
'partial unsafe private protected ' +
|
||||
'if then else ' +
|
||||
'universe variable ' +
|
||||
'import open export prelude renaming hiding ' +
|
||||
'calc match with do by let extends ' +
|
||||
'universe variable variables ' +
|
||||
'import open export theory prelude renaming hiding exposing ' +
|
||||
'calc match with do by let extends ' +
|
||||
'for in unless try catch finally mutual mut return continue break where rec ' +
|
||||
'syntax macro_rules macro deriving ' +
|
||||
'fun ' +
|
||||
@@ -1123,7 +1123,7 @@ hljs.registerLanguage("lean", function(hljs) {
|
||||
'Type Prop|10 Sort rw|10 rewrite rwa erw subst substs ' +
|
||||
'simp dsimp simpa simp_intros finish using generalizing ' +
|
||||
'unfold unfold1 dunfold unfold_projs unfold_coes ' +
|
||||
'delta cc ac_rfl ' +
|
||||
'delta cc ac_reflexivity ac_refl ' +
|
||||
'existsi|10 cases rcases intro intros introv by_cases ' +
|
||||
'refl rfl funext case focus propext exact exacts ' +
|
||||
'refine apply eapply fapply apply_with apply_instance ' +
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<DirectedGraph GraphDirection="TopToBottom" Layout="Sugiyama" Offset="-1264.63833333333,-729.52" ZoomLevel="1" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
|
||||
<Nodes>
|
||||
<Node Id="Applicative" Bounds="-839,-412,78.3066666666667,25.96" UseManualLocation="True" />
|
||||
<Node Id="Bind" Bounds="-703,-412.990048779297,50,25.96" UseManualLocation="True" />
|
||||
<Node Id="Except" Category="concrete" Bounds="-631,-238,54.5166666666667,25.96" UseManualLocation="True" />
|
||||
<Node Id="Functor" Bounds="-890.021657986111,-509,60.2533333333333,25.96" UseManualLocation="True" />
|
||||
<Node Id="List" Category="concrete" Bounds="-922,-412,50,25.96" UseManualLocation="True" />
|
||||
<Node Id="Monad" Bounds="-765,-339,57.77,25.96" UseManualLocation="True" />
|
||||
<Node Id="Option" Category="concrete" Bounds="-903,-238,56.8933333333333,25.96" UseManualLocation="True" />
|
||||
<Node Id="Pure" Bounds="-799,-509,50,25.96" UseManualLocation="True" />
|
||||
<Node Id="ReaderM" Category="concrete" Bounds="-816,-238,67.5033333333333,25.96" UseManualLocation="True" />
|
||||
<Node Id="Seq" Bounds="-719,-509,50,25.96" UseManualLocation="True" />
|
||||
<Node Id="SeqLeft" Bounds="-639,-509,59.4666666666667,25.96" UseManualLocation="True" />
|
||||
<Node Id="SeqRight" Bounds="-544,-509,67.7233333333333,25.96" UseManualLocation="True" />
|
||||
<Node Id="StateM" Category="concrete" Bounds="-718.358888888889,-238,57.28,25.96" UseManualLocation="True" />
|
||||
</Nodes>
|
||||
<Links>
|
||||
<Link Source="Applicative" Target="Functor" Bounds="-847.12242702921,-475.387638592472,39.2404278471513,63.3876385924717" />
|
||||
<Link Source="Applicative" Target="Pure" Bounds="-796.388009621993,-474.343439288465,16.6120628262117,62.3434392884653" />
|
||||
<Link Source="Applicative" Target="Seq" Bounds="-785.682854982818,-476.959367842732,70.8838407724442,64.9593678427323" />
|
||||
<Link Source="Applicative" Target="SeqLeft" Bounds="-774.344312027491,-478.957606209781,131.554439087217,66.9576062097807" />
|
||||
<Link Source="Applicative" Target="SeqRight" Bounds="-762.225406557428,-481.91850535998,209.970366938847,70.3021737715571" />
|
||||
<Link Source="Except" Target="Monad" Bounds="-711.95378637201,-307.579654923495,91.1658051838909,69.5796549234947" />
|
||||
<Link Source="List" Target="Functor" Bounds="-892.034814302334,-474.634018472681,23.9591319497622,62.6340184726814" />
|
||||
<Link Source="Monad" Target="Applicative" Bounds="-782.595654197137,-379.260216894652,35.1486400418858,40.2602168946518" />
|
||||
<Link Source="Monad" Target="Bind" Bounds="-725.919943874952,-379.952252619104,32.1656790368972,40.9522526191042" />
|
||||
<Link Source="Option" Target="Monad" Bounds="-856.761951485149,-307.735551794831,95.5848867777901,69.7355517948313" />
|
||||
<Link Source="ReaderM" Target="Monad" Bounds="-776.319514851485,-304.853562563497,30.5364127352738,66.8535625634966" />
|
||||
<Link Source="StateM" Target="Monad" Bounds="-726.395530552052,-304.861622913356,30.7140523342301,66.8616229133556" />
|
||||
</Links>
|
||||
<Categories>
|
||||
<Category Id="concrete" />
|
||||
</Categories>
|
||||
<Properties>
|
||||
<Property Id="Bounds" DataType="System.Windows.Rect" />
|
||||
<Property Id="Expression" DataType="System.String" />
|
||||
<Property Id="GraphDirection" DataType="Microsoft.VisualStudio.Diagrams.Layout.LayoutOrientation" />
|
||||
<Property Id="GroupLabel" DataType="System.String" />
|
||||
<Property Id="IsEnabled" DataType="System.Boolean" />
|
||||
<Property Id="Layout" DataType="System.String" />
|
||||
<Property Id="Offset" DataType="System.String" />
|
||||
<Property Id="TargetType" DataType="System.Type" />
|
||||
<Property Id="UseManualLocation" DataType="System.Boolean" />
|
||||
<Property Id="Value" DataType="System.String" />
|
||||
<Property Id="ValueLabel" DataType="System.String" />
|
||||
<Property Id="ZoomLevel" DataType="System.String" />
|
||||
</Properties>
|
||||
<Styles>
|
||||
<Style TargetType="Node" GroupLabel="concrete" ValueLabel="True">
|
||||
<Condition Expression="HasCategory('concrete')" />
|
||||
<Setter Property="Background" Value="#FF91E7ED" />
|
||||
</Style>
|
||||
</Styles>
|
||||
</DirectedGraph>
|
||||
@@ -1,106 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg stroke-linecap="round" font-size="12" font-family="Segoe UI" width="485.7233333333333" height="336.96000000000004" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs />
|
||||
<g transform="translate(942,529)">
|
||||
<g id="Applicative->Functor">
|
||||
<path d="M -807.881999182059,-412 L -847.12242702921,-475.387638592472" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -851.859658804052,-483.04 L -849.997117457574,-472.431939869483 C -848.256110200696,-474.68582647768 -845.988743857725,-476.089450707263 -843.19501842866,-476.642812558231 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="Applicative->Pure">
|
||||
<path d="M -796.388009621993,-412 L -779.775946795781,-474.343439288465" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -777.458657044673,-483.04 L -783.898561528809,-474.407061321009 C -781.064326160453,-474.686741473815 -778.487567431109,-474.000137103116 -776.168285340778,-472.347248208913 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="Applicative->Seq">
|
||||
<path d="M -785.682854982818,-412 L -714.799014210374,-476.959367842732" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -708.163811683849,-483.04 L -718.238762116551,-479.232720948158 C -715.699848604043,-477.942360809625 -713.898179816704,-475.97637487584 -712.833755754535,-473.334763146803 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="Applicative->SeqLeft">
|
||||
<path d="M -774.344312027491,-412 L -642.789872940275,-478.957606209781" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -634.769021305842,-483.04 L -645.495475917531,-482.068829848393 C -643.394672020307,-480.145880525993 -642.185073860242,-477.769331893569 -641.866681437336,-474.93918395112 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="Applicative->SeqRight">
|
||||
<path d="M -762.225406557428,-411.616331588423 L -552.255039618581,-481.91850535998" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -543.720702607113,-484.775967831244 L -554.473282607084,-485.394048201604 C -552.678367392102,-483.182851583901 -551.831711845061,-480.654159136059 -551.93331596596,-477.807970858076 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="Except->Monad">
|
||||
<path d="M -620.787981188119,-238 L -711.95378637201,-307.579654923495" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -719.108129922992,-313.04 L -713.585679344792,-303.793241670113 C -712.762726383344,-306.519752175201 -711.144846360676,-308.639557671788 -708.732039276787,-310.152658159875 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="List->Functor">
|
||||
<path d="M -892.034814302334,-412 L -868.075682352572,-474.634018472681" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -864.86017701711,-483.04 L -872.168952513098,-475.129134007629 C -869.321012949211,-475.110389633491 -866.830351755932,-474.157647311872 -864.696968933259,-472.270907042774 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="Monad->Applicative">
|
||||
<path d="M -747.447014155251,-339 L -782.595654197137,-379.260216894652" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -788.514652511416,-386.04 L -784.951224653483,-375.876241743267 C -783.60006650904,-378.383328255499 -781.591241885233,-380.137105533804 -778.924750782062,-381.137573578181 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="Monad->Bind">
|
||||
<path d="M -725.919943874952,-339 L -693.754264838054,-379.952252619104" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -688.195056125048,-387.030048779297 L -697.517641877363,-381.63659025153 C -694.802827232157,-380.775839095105 -692.705702443952,-379.128666143103 -691.226267512747,-376.695071395525 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="Option->Monad">
|
||||
<path d="M -856.761951485149,-238 L -761.177064707358,-307.735551794831" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -753.906381848185,-313.04 L -764.342450894008,-310.377583265001 C -761.962908885902,-308.81268999619 -760.391220528815,-306.658413593472 -759.627385822747,-303.914754056846 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="ReaderM->Monad">
|
||||
<path d="M -776.319514851485,-238 L -745.783102116211,-304.853562563497" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -742.043818481848,-313.04 L -749.836994714031,-305.60586224138 C -746.99590766236,-305.407530509328 -744.570296570063,-304.299594617665 -742.560161437139,-302.28205456639 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="StateM->Monad">
|
||||
<path d="M -695.681478217822,-238 L -726.395530552052,-304.861622913356" fill="none" stroke="#A0A0A0" stroke-width="1" />
|
||||
<path d="M -730.152410671067,-313.04 L -729.612933688448,-302.283189850833 C -727.607141972296,-304.305048080909 -725.183919131808,-305.418197745802 -722.343265166986,-305.622638845513 z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1" stroke-linejoin="round" />
|
||||
</g>
|
||||
<g id="Applicative">
|
||||
<rect x="-839" y="-412" rx="3" ry="3" width="78.3066666666667" height="25.96" fill="#FFFFFF" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-829" y="-394.05" fill="#3D3D3D">Applicative</text>
|
||||
</g>
|
||||
<g id="Bind">
|
||||
<rect x="-703" y="-412.990048779297" rx="3" ry="3" width="50" height="25.96" fill="#FFFFFF" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-690" y="-395.040048779297" fill="#3D3D3D">Bind</text>
|
||||
</g>
|
||||
<g id="Except">
|
||||
<rect x="-631" y="-238" rx="3" ry="3" width="54.5166666666667" height="25.96" fill="#91E7ED" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-621" y="-220.05" fill="#3D3D3D">Except</text>
|
||||
</g>
|
||||
<g id="Functor">
|
||||
<rect x="-890.021657986111" y="-509" rx="3" ry="3" width="60.2533333333333" height="25.96" fill="#FFFFFF" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-880.021657986111" y="-491.05" fill="#3D3D3D">Functor</text>
|
||||
</g>
|
||||
<g id="List">
|
||||
<rect x="-922" y="-412" rx="3" ry="3" width="50" height="25.96" fill="#91E7ED" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-906" y="-394.05" fill="#3D3D3D">List</text>
|
||||
</g>
|
||||
<g id="Monad">
|
||||
<rect x="-765" y="-339" rx="3" ry="3" width="57.77" height="25.96" fill="#FFFFFF" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-755" y="-321.05" fill="#3D3D3D">Monad</text>
|
||||
</g>
|
||||
<g id="Option">
|
||||
<rect x="-903" y="-238" rx="3" ry="3" width="56.8933333333333" height="25.96" fill="#91E7ED" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-893" y="-220.05" fill="#3D3D3D">Option</text>
|
||||
</g>
|
||||
<g id="Pure">
|
||||
<rect x="-799" y="-509" rx="3" ry="3" width="50" height="25.96" fill="#FFFFFF" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-786" y="-491.05" fill="#3D3D3D">Pure</text>
|
||||
</g>
|
||||
<g id="ReaderM">
|
||||
<rect x="-816" y="-238" rx="3" ry="3" width="67.5033333333333" height="25.96" fill="#91E7ED" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-806" y="-220.05" fill="#3D3D3D">ReaderM</text>
|
||||
</g>
|
||||
<g id="Seq">
|
||||
<rect x="-719" y="-509" rx="3" ry="3" width="50" height="25.96" fill="#FFFFFF" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-704" y="-491.05" fill="#3D3D3D">Seq</text>
|
||||
</g>
|
||||
<g id="SeqLeft">
|
||||
<rect x="-639" y="-509" rx="3" ry="3" width="59.4666666666667" height="25.96" fill="#FFFFFF" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-629" y="-491.05" fill="#3D3D3D">SeqLeft</text>
|
||||
</g>
|
||||
<g id="SeqRight">
|
||||
<rect x="-544" y="-509" rx="3" ry="3" width="67.7233333333333" height="25.96" fill="#FFFFFF" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-534" y="-491.05" fill="#3D3D3D">SeqRight</text>
|
||||
</g>
|
||||
<g id="StateM">
|
||||
<rect x="-718.358888888889" y="-238" rx="3" ry="3" width="57.28" height="25.96" fill="#91E7ED" stroke="#A5A6A9" stroke-width="1" />
|
||||
<text x="-708.358888888889" y="-220.05" fill="#3D3D3D">StateM</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB |
@@ -64,7 +64,7 @@ It makes these three arguments implicit. Notationally, this hides the specificat
|
||||
making it look as though ``compose`` simply takes 3 arguments.
|
||||
|
||||
Variables can also be specified as implicit when they are declared with
|
||||
the ``variable`` command:
|
||||
the ``variables`` command:
|
||||
|
||||
```lean
|
||||
universe u
|
||||
|
||||
@@ -33,13 +33,14 @@ class Lean4Lexer(RegexLexer):
|
||||
|
||||
keywords1 = (
|
||||
'import', 'abbreviation', 'opaque_hint', 'tactic_hint', 'definition',
|
||||
'renaming', 'inline', 'hiding', 'parameter', 'lemma', 'variable',
|
||||
'renaming', 'inline', 'hiding', 'exposing', 'parameter', 'parameters',
|
||||
'conjecture', 'hypothesis', 'lemma', 'corollary', 'variable', 'variables',
|
||||
'theorem', 'axiom', 'inductive', 'structure', 'universe', 'alias',
|
||||
'help', 'options', 'precedence', 'postfix', 'prefix',
|
||||
'infix', 'infixl', 'infixr', 'notation', '#eval',
|
||||
'help', 'options', 'precedence', 'postfix', 'prefix', 'calc_trans',
|
||||
'calc_subst', 'calc_refl', 'infix', 'infixl', 'infixr', 'notation', '#eval',
|
||||
'#check', '#reduce', '#exit', 'coercion', 'end', 'private', 'using', 'namespace',
|
||||
'including', 'instance', 'section', 'context', 'protected', 'expose',
|
||||
'export', 'set_option', 'extends', 'open', 'example',
|
||||
'export', 'set_option', 'add_rewrite', 'extends', 'open', 'example',
|
||||
'constant', 'constants', 'print', 'opaque', 'reducible', 'irreducible',
|
||||
'def', 'macro', 'elab', 'syntax', 'macro_rules', 'reduce', 'where',
|
||||
'abbrev', 'noncomputable', 'class', 'attribute', 'synth', 'mutual',
|
||||
|
||||
@@ -12,22 +12,21 @@ texcl=false,
|
||||
% keywords, list taken from lean-syntax.el
|
||||
morekeywords=[1]{
|
||||
import, prelude, protected, private, noncomputable, definition, meta, renaming,
|
||||
hiding, parameter, parameters, begin, constant, constants,
|
||||
lemma, variable, variables, theory,
|
||||
print, theorem, example,
|
||||
open, as, export, override, axiom, axioms, inductive, with,
|
||||
hiding, exposing, parameter, parameters, begin, conjecture, constant, constants,
|
||||
hypothesis, lemma, corollary, variable, variables, premise, premises, theory,
|
||||
print, theorem, proposition, example,
|
||||
open, as, export, override, axiom, axioms, inductive, with, without,
|
||||
structure, record, universe, universes,
|
||||
alias, help, precedence, reserve, declare_trace, add_key_equivalence,
|
||||
match, infix, infixl, infixr, notation, postfix, prefix, instance,
|
||||
eval, reduce, check, end, this,
|
||||
using, using_well_founded, namespace, section,
|
||||
attribute, local, set_option, extends, include, omit, class,
|
||||
raw, replacing,
|
||||
eval, vm_eval, check, coercion, end, this, suppose,
|
||||
using, using_well_founded, namespace, section, fields,
|
||||
attribute, local, set_option, extends, include, omit, classes, class,
|
||||
instances, coercions, attributes, raw, replacing,
|
||||
calc, have, show, suffices, by, in, at, let, forall, Pi, fun,
|
||||
exists, if, dif, then, else, assume, obtain, from, register_simp_ext, unless, break, continue,
|
||||
mutual, do, def, run_cmd, const,
|
||||
partial, mut, where, macro, syntax, deriving,
|
||||
return, try, catch, for, macro_rules, declare_syntax_cat, abbrev},
|
||||
exists, if, dif, then, else, assume, obtain, from, aliases, register_simp_ext, unless, break, continue,
|
||||
mutual, do, def, run_cmd, const
|
||||
partial,mut,where,macro,syntax,deriving, return, try, catch, for, macro_rules,declare_syntax_cat,abbrev},
|
||||
|
||||
% Sorts
|
||||
morekeywords=[2]{Sort, Type, Prop},
|
||||
|
||||
@@ -18,9 +18,9 @@ syntax "`[Arith| " arith "]" : term
|
||||
macro_rules
|
||||
| `(`[Arith| $s:str]) => `(Arith.symbol $s)
|
||||
| `(`[Arith| $num:num]) => `(Arith.int $num)
|
||||
| `(`[Arith| $x + $y]) => `(Arith.add `[Arith| $x] `[Arith| $y])
|
||||
| `(`[Arith| $x * $y]) => `(Arith.mul `[Arith| $x] `[Arith| $y])
|
||||
| `(`[Arith| ($x)]) => `(`[Arith| $x])
|
||||
| `(`[Arith| $x:arith + $y:arith]) => `(Arith.add `[Arith| $x] `[Arith| $y])
|
||||
| `(`[Arith| $x:arith * $y:arith]) => `(Arith.mul `[Arith| $x] `[Arith| $y])
|
||||
| `(`[Arith| ($x:arith)]) => `(`[Arith| $x])
|
||||
|
||||
#check `[Arith| "x" * "y"] -- mul
|
||||
-- Arith.mul (Arith.symbol "x") (Arith.symbol "y")
|
||||
|
||||
1
doc/monads/.gitignore
vendored
1
doc/monads/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
*.lean.md
|
||||
@@ -1,326 +0,0 @@
|
||||
/-!
|
||||
# Applicative Functors
|
||||
|
||||
Building on [Functors](functors.lean.md) is the [Applicative
|
||||
Functor](https://en.wikipedia.org/wiki/Applicative_functor). For simplicity, you can refer to these
|
||||
simply as "Applicatives". These are a little tricker than functors, but still simpler than monads.
|
||||
Let's see how they work!
|
||||
|
||||
## What is an Applicative Functor?
|
||||
|
||||
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.
|
||||
|
||||
## How are Applicatives represented in Lean?
|
||||
|
||||
An [applicative functor](https://en.wikipedia.org/wiki/Applicative_functor) is an intermediate
|
||||
structure between `Functor` and `Monad`. It mainly consists of two operations:
|
||||
|
||||
* `pure : α → F α`
|
||||
* `seq : F (α → β) → F α → F β` (written as `<*>`)
|
||||
|
||||
The `pure` operator specifies how you can wrap a normal object `α` into an instance of this structure `F α`.
|
||||
This is the "default" mechanism mentioned above.
|
||||
|
||||
The `seq` operator allows you to chain operations by wrapping a function in a structure. The name
|
||||
"applicative" comes from the fact that you "apply" functions from within the structure, rather than
|
||||
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`:
|
||||
|
||||
```lean,ignore
|
||||
class Applicative (f : Type u → Type v) extends Functor f, Pure f, Seq f, SeqLeft f, SeqRight f where
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
```lean,ignore
|
||||
class Pure (f : Type u → Type v) where
|
||||
pure {α : Type u} : α → f α
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
-/
|
||||
#eval (pure 10 : Option Nat) -- some 10
|
||||
/-!
|
||||
|
||||
Here we used the `Option` implementation of `pure` to wrap the `Nat 10` value in an `Option Nat`
|
||||
type resulting in the value `some 10`, and in fact if you look at the Monad instance of `Option` , you
|
||||
will see that `pure` is indeed implemented using `Option.some`:
|
||||
|
||||
-/
|
||||
instance : Monad Option where
|
||||
pure := Option.some
|
||||
/-!
|
||||
|
||||
The `Seq` type class is also a simple type class that provides the `seq` operator which can
|
||||
also be written using the special syntax `<*>`.
|
||||
|
||||
```lean,ignore
|
||||
class Seq (f : Type u → Type v) : Type (max (u+1) v) where
|
||||
seq : {α β : Type u} → f (α → β) → (Unit → f α) → f β
|
||||
```
|
||||
|
||||
|
||||
## Basic Applicative Examples
|
||||
|
||||
Many of the basic functors also have instances of `Applicative`.
|
||||
For example, `Option` is also `Applicative`.
|
||||
|
||||
So let's take a look and what the `seq` operator can do. Suppose you want to multiply two `Option Nat`
|
||||
objects. Your first attempt might be this:
|
||||
|
||||
-/
|
||||
#check_failure (some 4) * (some 5) -- failed to synthesize instance
|
||||
/-!
|
||||
|
||||
You then might wonder how to use the `Functor.map` to solve this since you could do these before:
|
||||
|
||||
-/
|
||||
#eval (some 4).map (fun x => x * 5) -- some 20
|
||||
|
||||
#eval (some 4).map (· * 5) -- some 20
|
||||
|
||||
#eval (· * 5) <$> (some 4) -- some 20
|
||||
/-!
|
||||
|
||||
Remember that `<$>` is the infix notation for `Functor.map`.
|
||||
|
||||
The functor `map` operation can apply a multiplication to the value in the `Option` and then lift the
|
||||
result back up to become a new `Option` , but this isn't what you need here.
|
||||
|
||||
The `Seq.seq` operator `<*>` can help since it can apply a function to the items inside a
|
||||
container and then lift the result back up to the desired type, namely `Option` .
|
||||
|
||||
There are two ways to do this:
|
||||
|
||||
-/
|
||||
#eval pure (.*.) <*> some 4 <*> some 5 -- some 20
|
||||
|
||||
#eval (.*.) <$> some 4 <*> some 5 -- some 20
|
||||
/-!
|
||||
|
||||
In the first way, we start off by wrapping the function in an applicative using pure. Then we apply
|
||||
this to the first `Option` , and again to the second `Option` in a chain of operations. So you can see
|
||||
how `Seq.seq` can be chained in fact, `Seq.seq` is really all about chaining of operations.
|
||||
|
||||
But in this case there is a simpler way. In the second way, you can see that "applying" a single
|
||||
function to a container is the same as using `Functor.map`. So you use `<$>` to "transform" the first
|
||||
option into an `Option` containing a function, and then apply this function over the second value.
|
||||
|
||||
Now if either side is `none`, the result is `none`, as expected, and in this case the
|
||||
`seq` operator was able to eliminate the multiplication:
|
||||
|
||||
-/
|
||||
#eval (.*.) <$> none <*> some 5 -- none
|
||||
#eval (.*.) <$> some 4 <*> none -- none
|
||||
/-!
|
||||
|
||||
For a more interesting example, let's make `List` an applicative by adding the following
|
||||
definition:
|
||||
|
||||
-/
|
||||
instance : Applicative List where
|
||||
pure := List.pure
|
||||
seq f x := List.bind f fun y => Functor.map y (x ())
|
||||
/-!
|
||||
|
||||
Notice you can now sequence a _list_ of functions and a _list_ of items.
|
||||
The trivial case of sequencing a singleton list is in fact the same as `map`, as you saw
|
||||
earlier with the `Option` examples:
|
||||
|
||||
-/
|
||||
#eval [ (·+2)] <*> [4, 6] -- [6, 8]
|
||||
#eval (·+2) <$> [4,6] -- [6, 8]
|
||||
/-!
|
||||
|
||||
But now with list it is easier to show the difference when you do this:
|
||||
|
||||
-/
|
||||
#eval [(·+2), (· *3)] <*> [4, 6] -- [6, 8, 12, 18]
|
||||
/-!
|
||||
|
||||
Why did this produce 4 values? The reason is because `<*>` applies _every_ function to _every_
|
||||
value in a pairwise manner. This makes sequence really convenient for solving certain problems. For
|
||||
example, how do you get the pairwise combinations of all values from two lists?
|
||||
|
||||
-/
|
||||
#eval Prod.mk <$> [1, 2, 3] <*> [4, 5, 6]
|
||||
-- [(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]
|
||||
/-!
|
||||
|
||||
How do you get the sum of these pairwise values?
|
||||
-/
|
||||
#eval (·+·) <$> [1, 2, 3] <*> [4, 5, 6]
|
||||
-- [5, 6, 7, 6, 7, 8, 7, 8, 9]
|
||||
/-!
|
||||
|
||||
Here you can use `<$>` to "transform" each element of the first list into a function, and then apply
|
||||
these functions over the second list.
|
||||
|
||||
If you have 3 lists, and want to find all combinations of 3 values across those lists you
|
||||
would need helper function that can create a tuple out of 3 values, and Lean provides a
|
||||
very convenient syntax for that `(·,·,·)`:
|
||||
|
||||
-/
|
||||
#eval (·,·,·) <$> [1, 2] <*> [3, 4] <*> [5, 6]
|
||||
-- [(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]
|
||||
/-!
|
||||
|
||||
And you could sum these combinations if you first define a sum function that takes three inputs and
|
||||
then you could chain apply this over the three lists. Again lean can create such a function
|
||||
with the expression `(·+·+·)`:
|
||||
|
||||
-/
|
||||
#eval (·+·+·) <$> [1, 2] <*> [3, 4] <*> [5, 6]
|
||||
-- [9, 10, 10, 11, 10, 11, 11, 12]
|
||||
/-!
|
||||
|
||||
And indeed each sum here matches the expected values if you manually sum the triples we
|
||||
show above.
|
||||
|
||||
**Side note:** there is another way to combine lists with a function that does not do the pairwise
|
||||
combinatorics, it is called `List.zipWith`:
|
||||
|
||||
-/
|
||||
#eval List.zipWith (·+·) [1, 2, 3] [4, 5, 6]
|
||||
-- [5, 7, 9]
|
||||
/-!
|
||||
|
||||
And there is a helper function named `List.zip` that calls `zipWith` using the function `Prod.mk`
|
||||
so you get a nice zipped list like this:
|
||||
|
||||
-/
|
||||
#eval List.zip [1, 2, 3] [4, 5, 6]
|
||||
-- [(1, 4), (2, 5), (3, 6)]
|
||||
/-!
|
||||
|
||||
And of couse, as you would expect, there is an `unzip` also:
|
||||
|
||||
-/
|
||||
#eval List.unzip (List.zip [1, 2, 3] [4, 5, 6])
|
||||
-- ([1, 2, 3], [4, 5, 6])
|
||||
/-!
|
||||
|
||||
## Example: A Functor that is not Applicative
|
||||
|
||||
From the chapter on [functors](functors.lean.md) you might remember this example of `LivingSpace`
|
||||
that had a `Functor` instance:
|
||||
|
||||
-/
|
||||
structure LivingSpace (α : Type) where
|
||||
totalSize : α
|
||||
numBedrooms : Nat
|
||||
masterBedroomSize : α
|
||||
livingRoomSize : α
|
||||
kitchenSize : α
|
||||
deriving Repr, BEq
|
||||
|
||||
def LivingSpace.map (f : α → β) (s : LivingSpace α) : LivingSpace β :=
|
||||
{ totalSize := f s.totalSize
|
||||
numBedrooms := s.numBedrooms
|
||||
masterBedroomSize := f s.masterBedroomSize
|
||||
livingRoomSize := f s.livingRoomSize
|
||||
kitchenSize := f s.kitchenSize }
|
||||
|
||||
instance : Functor LivingSpace where
|
||||
map := LivingSpace.map
|
||||
/-!
|
||||
|
||||
It wouldn't really make sense to make an `Applicative` instance here. How would you write `pure` in
|
||||
the `Applicative` instance? By taking a single value and plugging it in for total size _and_ the
|
||||
master bedroom size _and_ the living room size? That wouldn't really make sense. And what would the
|
||||
numBedrooms value be for the default? What would it mean to "chain" two of these objects together?
|
||||
|
||||
If you can't answer these questions very well, then it suggests this type isn't really an
|
||||
Applicative functor.
|
||||
|
||||
## SeqLeft and SeqRight
|
||||
|
||||
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 `*>` 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
|
||||
pointing at. For example:
|
||||
|
||||
-/
|
||||
#eval (some 1) *> (some 2) -- Some 2
|
||||
#eval (some 1) <* (some 2) -- Some 1
|
||||
/-!
|
||||
|
||||
So these are a kind of "discard" operation. Run all the actions, but only return the values that you
|
||||
care about. It will be easier to see these in action when you get to full Monads, but they are used
|
||||
heavily in the Lean `Parsec` parser combinator library where you will find parsing functions like
|
||||
this one which parses the XML declaration `<?xml version="1.0" encoding='utf-8' standalone="yes">`:
|
||||
|
||||
```lean
|
||||
def XMLdecl : Parsec Unit := do
|
||||
skipString "<?xml"
|
||||
VersionInfo
|
||||
optional EncodingDecl *> optional SDDecl *> optional S *> skipString "?>"
|
||||
```
|
||||
|
||||
But you will need to understand full Monads before this will make sense.
|
||||
|
||||
## Lazy Evaluation
|
||||
|
||||
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 somthing interesting:
|
||||
|
||||
-/
|
||||
#eval Seq.seq ((.*.) <$> some 4) (fun (_ : Unit) => some 5) -- some 20
|
||||
/-!
|
||||
|
||||
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:
|
||||
```lean
|
||||
class Seq (f : Type u → Type v) where
|
||||
seq : {α β : Type u} → f (α → β) → (Unit → f α) → f β
|
||||
```
|
||||
|
||||
You will see this function defined here: `(Unit → f α)`, this is a function that takes `Unit` as input
|
||||
and produces the output of type `f α` where `f` is the container type `Type u -> Type v`, in this example `Option`
|
||||
and `α` is the element type `Nat`, so `fun (_ : Unit) => some 5` matches this definition because
|
||||
it is taking an input of type Unit and producing `some 5` which is type `Option Nat`.
|
||||
|
||||
The that `seq` is defined this way is because Lean is an eagerly evaluated language
|
||||
(call-by-value), you have to use this kind of Unit function whenever you want to explicitly delay
|
||||
evaluation and `seq` wants that so it can eliminate unnecessary function evaluations whenever
|
||||
possible.
|
||||
|
||||
Fortunately the `<*>` infix notation hides this from you by creating this wrapper function for you.
|
||||
If you look up the notation using F12 in VS Code you will find it contains `(fun _ : Unit => b)`.
|
||||
|
||||
Now to complete this picture you will find the default implementation of `seq` on the Lean `Monad`
|
||||
type class:
|
||||
|
||||
```lean
|
||||
class Monad (m : Type u → Type v) extends Applicative m, Bind m where
|
||||
seq f x := bind f fun y => Functor.map y (x ())
|
||||
```
|
||||
|
||||
Notice here that `x` is the `(Unit → f α)` function, and it is calling that function by passing the
|
||||
Unit value `()`, which is the Unit value (Unit.unit). All this just to ensure delayed evaluation.
|
||||
|
||||
## How do Applicatives help with Monads?
|
||||
|
||||
Applicatives are helpful for the same reasons as functors. They're a relatively simple abstract
|
||||
structure that has practical applications in your code. Now that you understand how chaining
|
||||
operations can fit into a structure definition, you're in a good position to start learning about
|
||||
[Monads](monads.lean.md)!
|
||||
-/
|
||||
@@ -1,166 +0,0 @@
|
||||
/-!
|
||||
# Except
|
||||
|
||||
The `Except` Monad adds exception handling behavior to your functions. Exception handling
|
||||
in other languages like Python or Java is done with a built in `throw` method that you
|
||||
can use anywhere. In `Lean` you can only `throw` an exception when your function is
|
||||
executing in the context of an `Except` monad.
|
||||
|
||||
-/
|
||||
def divide (x y: Float): Except String Float :=
|
||||
if y == 0 then
|
||||
throw "can't divide by zero"
|
||||
else
|
||||
pure (x / y)
|
||||
|
||||
#eval divide 5 2 -- Except.ok 2.500000
|
||||
#eval divide 5 0 -- Except.error "can't divide by zero"
|
||||
|
||||
/-!
|
||||
|
||||
Just as the `read` operation was available from the `ReaderM` monad and the `get` and `set`
|
||||
operations came with the `StateM` monad, here you can see a `throw` operation is provided by the
|
||||
`Except` monad.
|
||||
|
||||
So in Lean, `throw` is not available everywhere like it is in most imperative programming languages.
|
||||
You have to declare your function can throw by changing the type signature to `Except String Float`.
|
||||
This creates a function that might return an error of type `String` or it might return a value of
|
||||
type `Float` in the non-error case.
|
||||
|
||||
Once your function is monadic you also need to use the `pure` constructor of the `Except` monad to
|
||||
convert the pure non-monadic value `x / y` into the required `Except` object. See
|
||||
[Applicatives](applicatives.lean.md) for details on `pure`.
|
||||
|
||||
Now this return typing would get tedious if you had to include it everywhere that you call this
|
||||
function, however, Lean type inference can clean this up. For example, you can define a test
|
||||
function can calls the `divide` function and you don't need to say anything here about the fact that
|
||||
it might throw an error, because that is inferred:
|
||||
-/
|
||||
def test := divide 5 0
|
||||
|
||||
#check test -- Except String Float
|
||||
/-!
|
||||
|
||||
Notice the Lean compiler infers the required `Except String Float` type information for you.
|
||||
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 (>>=)`:
|
||||
-/
|
||||
def square (x : Float) : Except String Float :=
|
||||
if x >= 100 then
|
||||
throw "it's absolutely huge"
|
||||
else
|
||||
pure (x * x)
|
||||
|
||||
#eval divide 6 2 >>= square -- Except.ok 9.000000
|
||||
#eval divide 6 0 >>= square -- Except.error "can't divide by zero"
|
||||
#eval divide 100 1 >>= square -- Except.error "it's absolutely huge"
|
||||
|
||||
def chainUsingDoNotation := do
|
||||
let r ← divide 6 0
|
||||
square r
|
||||
|
||||
#eval chainUsingDoNotation -- Except.error "can't divide by zero"
|
||||
|
||||
/-!
|
||||
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 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.
|
||||
|
||||
## Try/Catch
|
||||
|
||||
Now with all good exception handling you also want to be able to catch exceptions so your program
|
||||
can try continue on or do some error recovery task, which you can do like this:
|
||||
-/
|
||||
def testCatch :=
|
||||
try
|
||||
let r ← divide 8 0 -- 'r' is type Float
|
||||
pure (toString r)
|
||||
catch e =>
|
||||
pure s!"Caught exception: {e}"
|
||||
|
||||
#check testCatch -- Except String String
|
||||
/-!
|
||||
|
||||
Note that the type inferred by Lean for this function is `Except String String` so unlike the
|
||||
`test` function earlier, this time Lean type inference has figured out that since the pure
|
||||
value `(toString r)` is of type `String`, then this function must have type `Except String String`
|
||||
so you don't have to explicitly state this. You can always hover your mouse over `testCatch`
|
||||
or use `#check testCatch` to query Lean interactively to figure out what type inference
|
||||
has decided. Lean type inference makes life easy for you, so it's good to use it
|
||||
when you can.
|
||||
|
||||
You can now see the try/catch working in this eval:
|
||||
-/
|
||||
#eval testCatch -- Except.ok "Caught exception: can't divide by zero"
|
||||
/-!
|
||||
|
||||
Notice the `Caught exception:` wrapped message is returned, and that it is returned as an
|
||||
`Except.ok` value, meaning `testCatch` eliminated the error result as expected.
|
||||
|
||||
So you've interleaved a new concept into your functions (exception handling) and the compiler is still
|
||||
able to type check everything just as well as it does for pure functions and it's been able to infer
|
||||
some things along the way to make it even easier to manage.
|
||||
|
||||
Now you might be wondering why `testCatch` doesn't infer the return type `String`? Lean does this as a
|
||||
convenience since you could have a rethrow in or after the catch block. If you really want to stop
|
||||
the `Except` type from bubbling up you can unwrap it like this:
|
||||
|
||||
-/
|
||||
def testUnwrap : String := Id.run do
|
||||
let r ← divide 8 0 -- r is type Except String Float
|
||||
match r with
|
||||
| .ok a => toString a -- 'a' is type Float
|
||||
| .error e => s!"Caught exception: {e}"
|
||||
|
||||
#check testUnwrap -- String
|
||||
#eval testUnwrap -- "Caught exception: can't divide by zero"
|
||||
|
||||
/-!
|
||||
|
||||
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.
|
||||
|
||||
## Monadic functions
|
||||
|
||||
You can also write functions that are designed to operate in the context of a monad.
|
||||
These functions typically end in upper case M like `List.forM` used below:
|
||||
-/
|
||||
|
||||
def validateList (x : List Nat) (max : Nat): Except String Unit := do
|
||||
x.forM fun a => do
|
||||
if a > max then throw "illegal value found in list"
|
||||
|
||||
#eval validateList [1, 2, 5, 3, 8] 10 -- Except.ok ()
|
||||
#eval validateList [1, 2, 5, 3, 8] 5 -- Except.error "illegal value found in list"
|
||||
|
||||
/-!
|
||||
Notice here that the `List.forM` function passes the monadic context through to the inner function
|
||||
so it can use the `throw` function from the `Except` monad.
|
||||
|
||||
The `List.forM` function is defined like this where `[Monad m]` means "in the context of a monad `m`":
|
||||
|
||||
-/
|
||||
def forM [Monad m] (as : List α) (f : α → m PUnit) : m PUnit :=
|
||||
match as with
|
||||
| [] => pure ⟨⟩
|
||||
| a :: as => do f a; List.forM as f
|
||||
/-!
|
||||
|
||||
## Summary
|
||||
|
||||
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 fuunction. To learn the answer, head to [Monad Transformers](transformers.lean.md).
|
||||
|
||||
-/
|
||||
@@ -1,227 +0,0 @@
|
||||
/-!
|
||||
# Functor
|
||||
|
||||
A `Functor` is any type that can act as a generic container that allows you to transform the
|
||||
underlying values inside the container using a function, so that the values are all updated, but the
|
||||
structure of the container is the same. This is called "mapping".
|
||||
|
||||
A List is one of the most basic examples of a `Functor`.
|
||||
|
||||
A list contains zero or more elements of the same, underlying type. When you `map` a function over
|
||||
a list, you create a new list with the same number of elements, where each has been transformed by
|
||||
the function:
|
||||
-/
|
||||
#eval List.map (λ x => toString x) [1,2,3] -- ["1", "2", "3"]
|
||||
|
||||
-- you can also write this using dot notation on the List object
|
||||
#eval [1,2,3].map (λ x => toString x) -- ["1", "2", "3"]
|
||||
|
||||
/-!
|
||||
Here we converted a list of natural numbers (Nat) to a list of strings where the lambda function
|
||||
here used `toString` to do the transformation of each element. Notice that when you apply `map` the
|
||||
"structure" of the object remains the same, in this case the result is always a `List` of the same
|
||||
size.
|
||||
|
||||
Note that in Lean a lambda function can be written using `fun` keyword or the unicode
|
||||
symbol `λ` which you can type in VS code using `\la `.
|
||||
|
||||
List has a specialized version of `map` defined as follows:
|
||||
-/
|
||||
def map (f : α → β) : List α → List β
|
||||
| [] => []
|
||||
| a::as => f a :: map f as
|
||||
|
||||
/-!
|
||||
This is a very generic `map` function that can take any function that converts `(α → β)` and use it
|
||||
to convert `List α → List β`. Notice the function call `f a` above, this application of `f` is
|
||||
producing the converted items for the new list.
|
||||
|
||||
Let's look at some more examples:
|
||||
|
||||
-/
|
||||
-- List String → List Nat
|
||||
#eval ["elephant", "tiger", "giraffe"].map (fun s => s.length)
|
||||
-- [8, 5, 7]
|
||||
|
||||
-- List Nat → List Float
|
||||
#eval [1,2,3,4,5].map (fun s => (s.toFloat) ^ 3.0)
|
||||
-- [1.000000, 8.000000, 27.000000, 64.000000, 125.000000]
|
||||
|
||||
--- List String → List String
|
||||
#eval ["chris", "david", "mark"].map (fun s => s.capitalize)
|
||||
-- ["Chris", "David", "Mark"]
|
||||
/-!
|
||||
|
||||
Another example of a functor is the `Option` type. Option contains a value or nothing and is handy
|
||||
for code that has to deal with optional values, like optional command line arguments.
|
||||
|
||||
Remember you can construct an Option using the type constructors `some` or `none`:
|
||||
|
||||
-/
|
||||
#check some 5 -- Option Nat
|
||||
#eval some 5 -- some 5
|
||||
#eval (some 5).map (fun x => x + 1) -- some 6
|
||||
#eval (some 5).map (fun x => toString x) -- some "5"
|
||||
/-!
|
||||
|
||||
Lean also provides a convenient short hand syntax for `(fun x => x + 1)`, namely `(· + 1)`
|
||||
using the middle dot unicode character which you can type in VS code using `\. `.
|
||||
|
||||
-/
|
||||
#eval (some 4).map (· * 5) -- some 20
|
||||
/-!
|
||||
|
||||
The `map` function preserves the `none` state of the Option, so again
|
||||
map preserves the structure of the object.
|
||||
|
||||
-/
|
||||
def x : Option Nat := none
|
||||
#eval x.map (fun x => toString x) -- none
|
||||
#check x.map (fun x => toString x) -- Option String
|
||||
/-!
|
||||
|
||||
Notice that even in the `none` case it has transformed `Option Nat` into `Option String` as
|
||||
you see in the `#check` command.
|
||||
|
||||
## How to make a Functor Instance?
|
||||
|
||||
The `List` type is made an official `Functor` by the following type class instance:
|
||||
|
||||
-/
|
||||
instance : Functor List where
|
||||
map := List.map
|
||||
/-!
|
||||
|
||||
Notice all you need to do is provide the `map` function implementation. For a quick
|
||||
example, let's supposed you create a new type describing the measurements of a home
|
||||
or apartment:
|
||||
|
||||
-/
|
||||
structure LivingSpace (α : Type) where
|
||||
totalSize : α
|
||||
numBedrooms : Nat
|
||||
masterBedroomSize : α
|
||||
livingRoomSize : α
|
||||
kitchenSize : α
|
||||
deriving Repr, BEq
|
||||
/-!
|
||||
|
||||
Now you can construct a `LivingSpace` in square feet using floating point values:
|
||||
-/
|
||||
abbrev SquareFeet := Float
|
||||
|
||||
def mySpace : LivingSpace SquareFeet :=
|
||||
{ totalSize := 1800, numBedrooms := 4, masterBedroomSize := 500,
|
||||
livingRoomSize := 900, kitchenSize := 400 }
|
||||
/-!
|
||||
|
||||
Now, suppose you want anyone to be able to map a `LivingSpace` from one type of measurement unit to
|
||||
another. Then you would provide a `Functor` instance as follows:
|
||||
|
||||
-/
|
||||
def LivingSpace.map (f : α → β) (s : LivingSpace α) : LivingSpace β :=
|
||||
{ totalSize := f s.totalSize
|
||||
numBedrooms := s.numBedrooms
|
||||
masterBedroomSize := f s.masterBedroomSize
|
||||
livingRoomSize := f s.livingRoomSize
|
||||
kitchenSize := f s.kitchenSize }
|
||||
|
||||
instance : Functor LivingSpace where
|
||||
map := LivingSpace.map
|
||||
/-!
|
||||
|
||||
Notice this functor instance takes `LivingSpace` and not the fully qualified type `LivingSpace SquareFeet`.
|
||||
Notice below that `LivingSpace` is a function from Type to Type. For example, if you give it type `SquareFeet`
|
||||
it gives you back the fully qualified type `LivingSpace SquareFeet`.
|
||||
|
||||
-/
|
||||
#check LivingSpace -- Type → Type
|
||||
/-!
|
||||
|
||||
So the `instance : Functor` then is operating on the more abstract, or generic `LivingSpace` saying
|
||||
for the whole family of types `LivingSpace α` you can map to `LivingSpace β` using the generic
|
||||
`LivingSpace.map` map function by simply providing a function that does the more primitive mapping
|
||||
from `(f : α → β)`. So `LivingSpace.map` is a sort of function applicator.
|
||||
This is called a "higher order function" because it takes a function as input
|
||||
`(α → β)` and returns another function as output `F α → F β`.
|
||||
|
||||
Notice that `LivingSpace.map` applies a function `f` to convert the units of all the LivingSpace
|
||||
fields, except for `numBedrooms` which is a count (and therefore is not a measurement that needs
|
||||
converting).
|
||||
|
||||
So now you can define a simple conversion function, let's say you want square meters instead:
|
||||
|
||||
-/
|
||||
abbrev SquareMeters := Float
|
||||
def squareFeetToMeters (ft : SquareFeet ) : SquareMeters := (ft / 10.7639104)
|
||||
/-!
|
||||
|
||||
and now bringing it all together you can use the simple function `squareFeetToMeters` to map
|
||||
`mySpace` to square meters:
|
||||
|
||||
-/
|
||||
#eval mySpace.map squareFeetToMeters
|
||||
/-
|
||||
{ totalSize := 167.225472,
|
||||
numBedrooms := 4,
|
||||
masterBedroomSize := 46.451520,
|
||||
livingRoomSize := 83.612736,
|
||||
kitchenSize := 37.161216 }
|
||||
-/
|
||||
/-!
|
||||
|
||||
Lean also defines custom infix operator `<$>` for `Functor.map` which allows you to write this:
|
||||
-/
|
||||
#eval (fun s => s.length) <$> ["elephant", "tiger", "giraffe"]
|
||||
#eval (fun x => x + 1) <$> (some 5) -- some 6
|
||||
/-!
|
||||
|
||||
Note that the infix operator is left associative which means it binds more tightly to the
|
||||
function on the left than to the expression on the right, this means you can often drop the
|
||||
parentheses on the right like this:
|
||||
|
||||
-/
|
||||
#eval (fun x => x + 1) <$> some 5 -- some 6
|
||||
/-!
|
||||
|
||||
Note that Lean lets you define your own syntax, so `<$>` is nothing special.
|
||||
You can define your own infix operator like this:
|
||||
|
||||
-/
|
||||
infixr:100 " doodle " => Functor.map
|
||||
|
||||
#eval (· * 5) doodle [1, 2, 3] -- [5, 10, 15]
|
||||
|
||||
/-!
|
||||
Wow, this is pretty powerful. By providing a functor instance on `LivingSpace` with an
|
||||
implementation of the `map` function it is now super easy for anyone to come along and
|
||||
transform the units of a `LivingSpace` using very simple functions like `squareFeetToMeters`. Notice
|
||||
that squareFeetToMeters knows nothing about `LivingSpace`.
|
||||
|
||||
## How do Functors help with Monads ?
|
||||
|
||||
Functors are an abstract mathematical structure that is represented in Lean with a type class. The
|
||||
Lean functor defines both `map` and a special case for working on constants more efficiently called
|
||||
`mapConst`:
|
||||
|
||||
```lean
|
||||
class Functor (f : Type u → Type v) : Type (max (u+1) v) where
|
||||
map : {α β : Type u} → (α → β) → f α → f β
|
||||
mapConst : {α β : Type u} → α → f β → f α
|
||||
```
|
||||
|
||||
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 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
|
||||
container type from `F α → F β`. This corresponds to the category-theory notion of
|
||||
[functor](https://en.wikipedia.org/wiki/Functor) in the special case where the category is the
|
||||
category of types and functions between them.
|
||||
|
||||
Understanding abstract mathematical structures is a little tricky for most people. So it helps to
|
||||
start with a simpler idea like functors before you try to understand monads. Building on
|
||||
functors is the next abstraction called [Applicatives](applicatives.lean.md).
|
||||
-/
|
||||
@@ -1,63 +0,0 @@
|
||||
# Monads
|
||||
|
||||
Monads are used heavily in Lean, as they are also in Haskell. Monads come from the wonderful world
|
||||
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 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
|
||||
and monads that will also be covered in this chapter:
|
||||
|
||||

|
||||
|
||||
This chapter is organized to give you a bottom up introduction to monads, starting with functors and
|
||||
applicative functors, you'll get an intuition for how these abstract structures work in Lean. Then
|
||||
you'll dive into monads and learn how to use some of the most useful built-in ones.
|
||||
|
||||
## [Functor](functors.lean.md)
|
||||
A functor is a type class that provides a map function and the map function is something many
|
||||
people are already familiar with so this should be easy to follow. Here you will see some
|
||||
concrete examples in action with `List` and `Option`.
|
||||
|
||||
## [Applicative Functors](applicatives.lean.md)
|
||||
Applicatives are a little more difficult to understand than functors, but their functionality can
|
||||
still be summed up in a couple simple functions. Here you will learn how to create an
|
||||
`Applicative List` and a completely custom `Applicative` type.
|
||||
|
||||
## [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 lean the specifics of how
|
||||
to actually use monads with some examples using the `Option` monad and the all important `IO` monad.
|
||||
|
||||
## [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.
|
||||
|
||||
## [State Monad](states.lean.md)
|
||||
This section introduces the `StateM` monad. This monad allows you to access a particular type that you can
|
||||
both read from and write to. It opens the door to fully stateful programming, allowing you to do many
|
||||
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 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 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 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.
|
||||
@@ -1,318 +0,0 @@
|
||||
/-!
|
||||
# Monad Laws
|
||||
|
||||
In the previous sections you learned how to use [Functors](functors.lean.md),
|
||||
[Applicatives](applicatives.lean.md), and [Monads](monads.lean.md), and you played with some useful
|
||||
instances including [Option](monads.lean.md), [IO](monads.lean.md), [Reader](readers.lean.md),
|
||||
[State](states.lean.md) and [Except](except.lean.md) and you learned about composition using [Monad
|
||||
Transformers](transformers.lean.md).
|
||||
|
||||
So far, you've learned the concrete details you need in order to _use_ monads in your Lean programs.
|
||||
But there's still one more important concept you need if you want to _create_ new functors,
|
||||
applicatives and monads. Namely, the notion of _structural "laws"_ -- rules that these type
|
||||
classes should follow in order to meet other programmers' expectations about your code.
|
||||
|
||||
## Life without Laws
|
||||
|
||||
Remember Lean represents each of these abstract structures by a type class. Each of these type classes
|
||||
has one or two main functions. So, as long as you implement those functions and it type checks, you
|
||||
have a new functor, applicative, or monad, right?
|
||||
|
||||
Well not quite. Yes, your program will compile and you'll be able to use the instances. But this
|
||||
doesn't mean your instances follow the mathematical constructs. If they don't, your instances won't
|
||||
fulfill other programmers' expectations. Each type class has its own "laws". For instance, suppose
|
||||
you have the following Point Functor:
|
||||
-/
|
||||
structure Point (α : Type) where
|
||||
x : α
|
||||
y : α
|
||||
deriving Repr, BEq
|
||||
|
||||
def Point.map (f : α → β) (s : Point α) : Point β :=
|
||||
{ x := f s.y, -- an example of something weird
|
||||
y := f s.x }
|
||||
|
||||
instance : Functor Point where
|
||||
map := Point.map
|
||||
|
||||
#eval (·+2) <$> (Point.mk 1 2) -- { x := 4, y := 3 }
|
||||
|
||||
/-!
|
||||
This Point does something weird, when you `map` it because it transposes the `x` and `y` coordinates
|
||||
which is not what other people would expect from a `map` function. In fact, it breaks the rules
|
||||
as you will see below.
|
||||
|
||||
## What are the Functor laws?
|
||||
|
||||
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. 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:
|
||||
|
||||
-/
|
||||
def list1 := [1,2,3]
|
||||
|
||||
#eval id <$> list1 == list1 -- true
|
||||
/-!
|
||||
|
||||
Now let's try the same test on the `Point` functor:
|
||||
-/
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
functor. In Lean you can compose two functions using `Function.comp f g` (or the syntax `f ∘ g`,
|
||||
which you can type in VS code using `\o `) and you will get the same results from both of these
|
||||
showing that the composition law holds for `List Nat`:
|
||||
|
||||
-/
|
||||
def double (x : Nat) := x + x
|
||||
def square (x : Nat) := x * x
|
||||
|
||||
#eval double <$> (square <$> list1) -- [2, 8, 18]
|
||||
|
||||
#eval (double <$> (square <$> list1)) == ((double ∘ square) <$> list1) -- true
|
||||
|
||||
-- ok, what about the Point class?
|
||||
#eval double <$> (square <$> p1) -- { x := 2, y := 8 }
|
||||
#eval (double ∘ square) <$> p1 -- { x := 8, y := 2 }
|
||||
|
||||
#eval double <$> (square <$> p1) == (double ∘ square) <$> p1 -- false
|
||||
/-!
|
||||
Note that composition also fails on the bad `Point` because the x/y transpose.
|
||||
|
||||
As you can see this bad `Point` implementation violates both of the functor laws. In this case it
|
||||
would not be a true functor. Its behavior would confuse any other programmers trying to use it. You
|
||||
should take care to make sure that your instances make sense. Once you get a feel for these type
|
||||
classes, the likelihood is that the instances you'll create will follow the laws.
|
||||
|
||||
You can also write a bad functor that passes one law but not the other like this:
|
||||
-/
|
||||
def bad_option_map {α β : Type u} : (α → β) → Option α → Option β
|
||||
| _, _ => none
|
||||
|
||||
instance : Functor Option where
|
||||
map := bad_option_map
|
||||
|
||||
def t1 : Option Nat := some 10
|
||||
|
||||
#eval id <$> t1 == t1 -- false
|
||||
#eval double <$> (square <$> t1) == (double ∘ square) <$> t1 -- true
|
||||
/-!
|
||||
|
||||
This fails the id law but obeys the composition law. Hopefully this explains the value of these
|
||||
laws, and you don't need to see any more bad examples!
|
||||
|
||||
## What are the Applicative Laws?
|
||||
|
||||
While functors have two laws, applicatives have four laws:
|
||||
|
||||
- Identity
|
||||
- Homomorphism
|
||||
- Interchange
|
||||
- Composition
|
||||
|
||||
### Identity
|
||||
|
||||
`pure id <*> v = v`
|
||||
|
||||
Applying the identity function through an applicative structure should not change the underlying
|
||||
values or structure. For example:
|
||||
-/
|
||||
instance : Applicative List where
|
||||
pure := List.pure
|
||||
seq f x := List.bind f fun y => Functor.map y (x ())
|
||||
|
||||
#eval pure id <*> [1, 2, 3] -- [1, 2, 3]
|
||||
/-!
|
||||
|
||||
The `pure id` statement here is wrapping the identity function in an applicative structure
|
||||
so that you can apply that over the container `[1, 2, 3]` using the Applicative `seq` operation
|
||||
which has the notation `<*>`.
|
||||
|
||||
To prove this for all values `v` and any applicative `m` you can write this theorem:
|
||||
-/
|
||||
example [Applicative m] [LawfulApplicative m] (v : m α) :
|
||||
pure id <*> v = v :=
|
||||
by simp -- Goals accomplished 🎉
|
||||
/-!
|
||||
|
||||
### Homomorphism
|
||||
|
||||
`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
|
||||
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.
|
||||
|
||||
For example:
|
||||
|
||||
-/
|
||||
def x := 1
|
||||
def f := (· + 2)
|
||||
|
||||
#eval pure f <*> pure x = (pure (f x) : List Nat) -- true
|
||||
/-!
|
||||
|
||||
You should see a distinct pattern here. The overriding theme of almost all these laws is that these
|
||||
`Applicative` types should behave like normal containers. The `Applicative` functions should not
|
||||
have any side effects. All they should do is facilitate the wrapping, unwrapping, and transformation
|
||||
of data contained in the container resulting in a new container that has the same structure.
|
||||
|
||||
### Interchange
|
||||
|
||||
`u <*> pure y = pure (. y) <*> u`.
|
||||
|
||||
This law is is a little more complicated, so don't sweat it too much. It states that the order that
|
||||
you wrap things shouldn't matter. One the left, you apply any applicative `u` over a pure wrapped
|
||||
object. On the right, you first wrap a function applying the object as an argument. Note that `(·
|
||||
y)` is short hand for: `fun f => f y`. Then you apply this to the first applicative `u`. These
|
||||
should be the same.
|
||||
|
||||
For example:
|
||||
|
||||
-/
|
||||
def y := 4
|
||||
def g : List (Nat → Nat) := [(· + 2)]
|
||||
|
||||
#eval g <*> pure y = pure (· y) <*> g -- true
|
||||
/-!
|
||||
|
||||
You can prove this with the following theorem:
|
||||
-/
|
||||
example [Applicative m] [LawfulApplicative m] (u : m (α → β)) (y : α) :
|
||||
u <*> pure y = pure (· y) <*> u :=
|
||||
by simp [pure_seq] -- Goals accomplished 🎉
|
||||
|
||||
/-!
|
||||
|
||||
### Composition:
|
||||
|
||||
`u <*> v <*> w = u <*> (v <*> w)`
|
||||
|
||||
This final applicative law mimics the second functor law. It is a composition law. It states that
|
||||
function composition holds across applications within the applicative:
|
||||
|
||||
For example:
|
||||
|
||||
-/
|
||||
def u := [1, 2]
|
||||
def v := [3, 4]
|
||||
def w := [5, 6]
|
||||
|
||||
#eval pure (·+·+·) <*> u <*> v <*> w
|
||||
-- [9, 10, 10, 11, 10, 11, 11, 12]
|
||||
|
||||
#eval let grouping := pure (·+·) <*> v <*> w
|
||||
pure (·+·) <*> u <*> grouping
|
||||
-- [9, 10, 10, 11, 10, 11, 11, 12]
|
||||
/-!
|
||||
|
||||
To test composition you see the separate grouping `(v <*> w)` then that can be used in the outer
|
||||
sequence `u <*> grouping` to get the same final result `[9, 10, 10, 11, 10, 11, 11, 12]`.
|
||||
|
||||
## What are the Monad Laws?
|
||||
|
||||
Monads have three laws:
|
||||
|
||||
- Left Identity
|
||||
- Right Identity
|
||||
- Associativity
|
||||
|
||||
### Left Identity
|
||||
|
||||
Identity laws for monads specify that `pure` by itself shouldn't really change anything about the
|
||||
structure or its values.
|
||||
|
||||
Left identity is `x >>= pure = x` and is demonstrated by the following examples on a monadic `List`:
|
||||
-/
|
||||
instance : Monad List where
|
||||
pure := List.pure
|
||||
bind := List.bind
|
||||
|
||||
#eval ["apple", "orange"] >>= pure -- ["apple", "orange"]
|
||||
#eval [1,2,3] >>= pure -- [1,2,3]
|
||||
/-!
|
||||
|
||||
### Right Identity
|
||||
|
||||
Right identity is `pure x >>= f = f x` and is demonstrated by the following example:
|
||||
-/
|
||||
def h (x : Nat) : Option Nat := some (x + 1)
|
||||
def z := 5
|
||||
|
||||
#eval pure z >>= h -- some 6
|
||||
#eval h z -- some 6
|
||||
|
||||
#eval pure z >>= h = h x -- true
|
||||
/-!
|
||||
|
||||
So in this example, with this specific `z` and `h`, you see that the rule holds true.
|
||||
|
||||
|
||||
### Associativity
|
||||
|
||||
The associativity law is written as:
|
||||
```lean,ignore
|
||||
x >>= f >>= g = x >>= (λ x => f x >>= g)
|
||||
```
|
||||
where `(x : m α)` and `(f : α → m β)` and `(g : β → m γ)`.
|
||||
|
||||
The associativity law is difficult to parse like some of the applicative laws, but what it is saying
|
||||
is that if you change the grouping of `bind` operations, you should still get the same result.
|
||||
This law has a parallel structure to the other composition laws.
|
||||
|
||||
You can see this in action in the following rewrite of `runOptionFuncsBind` from [monads](monads.lean.md):
|
||||
-/
|
||||
def optionFunc1 : String -> Option Nat
|
||||
| "" => none
|
||||
| str => some str.length
|
||||
|
||||
def optionFunc2 (i : Nat) : Option Float :=
|
||||
if i % 2 == 0 then none else some (i.toFloat * 3.14159)
|
||||
|
||||
def optionFunc3 (f : Float) : Option (List Nat) :=
|
||||
if f > 15.0 then none else some [f.floor.toUInt32.toNat, f.ceil.toUInt32.toNat]
|
||||
|
||||
|
||||
def runOptionFuncsBind (input : String) : Option (List Nat) :=
|
||||
optionFunc1 input >>= optionFunc2 >>= optionFunc3
|
||||
|
||||
def runOptionFuncsBindGrouped (input : String) : Option (List Nat) :=
|
||||
optionFunc1 input >>= (λ x => optionFunc2 x >>= optionFunc3)
|
||||
|
||||
#eval runOptionFuncsBind "big" -- some [9, 10]
|
||||
#eval runOptionFuncsBindGrouped "big" -- some [9, 10]
|
||||
/-!
|
||||
|
||||
Notice here we had to insert a `λ` function just like the definition says: `(λ x => f x >>= g)`.
|
||||
This is because unlike applicatives, you can't resolve the structure of later operations without the
|
||||
results of earlier operations quite as well because of the extra context monads provide. But you can
|
||||
still group their later operations into composite functions taking their inputs from earlier on, and
|
||||
the result should be the same.
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
While these laws may be a bit difficult to understand just by looking at them, the good news is that
|
||||
most of the instances you'll make will naturally follow the laws so long as you keep it simple, so
|
||||
you shouldn't have to worry about them too much.
|
||||
|
||||
There are two main ideas from all the laws:
|
||||
|
||||
1. Applying the identity or pure function should not change the underlying values or structure.
|
||||
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 bahavior of your
|
||||
new functors, applicatives and monads.
|
||||
|
||||
-/
|
||||
@@ -1,300 +0,0 @@
|
||||
/-!
|
||||
# Monads
|
||||
|
||||
Building on [Functors](functors.lean.md) and [Applicatives](applicatives.lean.md) we can now
|
||||
introduce [monads](https://en.wikipedia.org/wiki/Monad_%28category_theory%29).
|
||||
|
||||
A monad is another type of abstract, functional structure. Let's explore what makes it different
|
||||
from the first two structures.
|
||||
|
||||
## What is a Monad?
|
||||
|
||||
A monad is a computational context. It provides a structure that allows you to chain together
|
||||
operations that have some kind of shared state or similar effect. Whereas pure functional code can
|
||||
only operate on explicit input parameters and affect the program through explicit return values,
|
||||
operations in a monad can affect other computations in the chain implicitly through side effects,
|
||||
especially modification of an implicitly shared value.
|
||||
|
||||
## How are monads represented in Lean?
|
||||
|
||||
Like functors and applicatives, monads are represented with a type class in Lean:
|
||||
|
||||
```lean,ignore
|
||||
class Monad (m : Type u → Type v) extends Applicative m, Bind m where
|
||||
```
|
||||
|
||||
Just as every applicative is a functor, every monad is also an applicative and there's one more new
|
||||
base type class used here that you need to understand, namely, `Bind`.
|
||||
|
||||
```lean,ignore
|
||||
class Bind (f : Type u → Type v) where
|
||||
bind : {α β : Type u} → f α → (α → f β) → f β
|
||||
```
|
||||
|
||||
The `bind` operator also has infix notation `>>=` where `x >>= g` represents the result of executing
|
||||
`x` to get a value of type `f α` then unwrapping the value `α` from that and passing it to function
|
||||
`g` of type `α → f β` returning the result of type `f β` where `f` is the target structure type
|
||||
(like `Option` or List)
|
||||
|
||||
This `bind` operation looks similar to the other ones you've seen so far, if you put them all
|
||||
together `Monad` has the following operations:
|
||||
|
||||
```lean,ignore
|
||||
class Monad (f : Type u → Type v) extends Applicative f, Bind f where
|
||||
pure {α : Type u} : α → f α
|
||||
map : {α β : Type u} → (α → β) → f α → f β
|
||||
seq : {α β : Type u} → f (α → β) → (Unit → f α) → f β
|
||||
bind : {α β : Type u} → f α → (α → f β) → f β
|
||||
...
|
||||
```
|
||||
|
||||
Notice `Monad` also contains `pure` it must also have a "default" way to wrap a value in the
|
||||
structure.
|
||||
|
||||
The `bind` operator is similar to the applicative `seq` operator in that it chains two operations,
|
||||
with one of them being function related. Notice that `bind`, `seq` and `map` all take a function of
|
||||
some kind. Let's examine those function types:
|
||||
|
||||
- map: `(α → β)`
|
||||
- seq: `f (α → β)`
|
||||
- bind: `(α → f β)`
|
||||
|
||||
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 also used by `seq` in this comparison, since that
|
||||
was explained in [Applicatives Lazy Evaluation](applicatives.lean.md#lazy-evaluation).
|
||||
|
||||
## Basic Monad Example
|
||||
|
||||
Just as `Option` is a functor and an applicative functor, it is also a monad! Let's start with how
|
||||
`Option` implements the Monad type class.
|
||||
|
||||
-/
|
||||
instance : Monad Option where
|
||||
pure := Option.some
|
||||
bind := Option.bind
|
||||
/-!
|
||||
|
||||
where:
|
||||
|
||||
```lean,ignore
|
||||
def Option.bind : Option α → (α → Option β) → Option β
|
||||
| none, _ => none
|
||||
| some a, f => f a
|
||||
```
|
||||
|
||||
> **Side note**: this function definition is using a special shorthand syntax in Lean where the `:=
|
||||
match a, b with` code can be collapsed away. To make this more clear consider the following simpler
|
||||
example, where `Option.bind` is using the second form like `bar`:
|
||||
|
||||
-/
|
||||
def foo (x : Option Nat) (y : Nat) : Option Nat :=
|
||||
match x, y with
|
||||
| none, _ => none
|
||||
| some x, y => some (x + y)
|
||||
|
||||
def bar : Option Nat → Nat → Option Nat
|
||||
| none, _ => none
|
||||
| some x, y => some (x + y)
|
||||
|
||||
#eval foo (some 1) 2 -- some 3
|
||||
#eval bar (some 1) 2 -- some 3
|
||||
/-!
|
||||
What is important is that `Option.bind` is using a `match` statement to unwrap the input value
|
||||
`Option α`, if it is `none` then it does nothing and returns `none`, if it has a value of type `α`
|
||||
then it applies the function in the second argument `(α → Option β)` to this value, which is
|
||||
the expression `f a` that you see in the line ` | some a, f => f a` above. The function
|
||||
returns a result of type `Option β` which then becomes the return value for `bind`. So there
|
||||
is no structure wrapping required on the return value since the input function already did that.
|
||||
|
||||
But let's bring in the definition of a monad. What does it mean to describe `Option` as a
|
||||
computational context?
|
||||
|
||||
The `Option` monad encapsulates the context of failure. Essentially, the `Option` monad lets us
|
||||
abort a series of operations whenever one of them fails. This allows future operations to assume
|
||||
that all previous operations have succeeded. Here's some code to motivate this idea:
|
||||
|
||||
-/
|
||||
def optionFunc1 : String -> Option Nat
|
||||
| "" => none
|
||||
| str => some str.length
|
||||
|
||||
def optionFunc2 (i : Nat) : Option Float :=
|
||||
if i % 2 == 0 then none else some (i.toFloat * 3.14159)
|
||||
|
||||
def optionFunc3 (f : Float) : Option (List Nat) :=
|
||||
if f > 15.0 then none else some [f.floor.toUInt32.toNat, f.ceil.toUInt32.toNat]
|
||||
|
||||
def runOptionFuncs (input : String) : Option (List Nat) :=
|
||||
match optionFunc1 input with
|
||||
| none => none
|
||||
| some i => match optionFunc2 i with
|
||||
| none => none
|
||||
| some f => optionFunc3 f
|
||||
|
||||
#eval runOptionFuncs "big" -- some [9, 10]
|
||||
/-!
|
||||
|
||||
Here you see three different functions that could fail. These are then combined in `runOptionFuncs`.
|
||||
But then you have to use nested `match` expressions to check if the previous result succeeded. It
|
||||
would be very tedious to continue this pattern much longer.
|
||||
|
||||
The `Option` monad helps you fix this. Here's what this function looks like using the `bind`
|
||||
operator.
|
||||
|
||||
-/
|
||||
|
||||
def runOptionFuncsBind (input : String) : Option (List Nat) :=
|
||||
optionFunc1 input >>= optionFunc2 >>= optionFunc3
|
||||
|
||||
#eval runOptionFuncsBind "big" -- some [9, 10]
|
||||
/-!
|
||||
|
||||
It's much cleaner now! You take the first result and pass it into the second and third functions
|
||||
using the `bind` operation. The monad instance handles all the failure cases so you don't have to!
|
||||
|
||||
Let's see why the types work out. The result of `optionFunc1` input is simply `Option Nat`. Then the
|
||||
bind operator allows you to take this `Option Nat` value and combine it with `optionFunc2`, whose type
|
||||
is `Nat → Option Float` The **bind operator resolves** these to an `Option Float`. Then you pass this
|
||||
similarly through the bind operator to `optionFunc3`, resulting in the final type, `Option (List Nat)`.
|
||||
|
||||
Your functions will not always combine so cleanly though. This is where `do` notation comes into play.
|
||||
This notation allows you to write monadic operations one after another, line-by-line. It almost makes
|
||||
your code look like imperative programming. You can rewrite the above as:
|
||||
-/
|
||||
|
||||
def runOptionFuncsDo (input : String) : Option (List Nat) := do
|
||||
let i ← optionFunc1 input
|
||||
let f ← optionFunc2 i
|
||||
optionFunc3 f
|
||||
|
||||
#eval runOptionFuncsDo "big" -- some [9, 10]
|
||||
/-!
|
||||
|
||||
The `←` operator used here is special. It effectively unwraps the value on the right-hand side from
|
||||
the monad. This means the value `i` has type `Nat`, _even though_ the result of `optionFunc1` is
|
||||
`Option Nat`. This is done using a `bind` operation under the hood.
|
||||
|
||||
> Note you can use `<-` or the nice unicode symbol `←` which you can type into VS code by typing
|
||||
these characters `\l `. When you type the final space, `\l` is replaced with `←`.
|
||||
|
||||
Observe that we do not unwrap the final line of the computation. The function result is `Option
|
||||
(List Nat)` which matches what `optionFunc3` returns. At first glance, this may look more complicated
|
||||
than the `bind` example. However, it gives you a lot more flexibility, like mixing monadic and
|
||||
non-monadic statements, using if then/else structures with their own local do blocks and so on. It
|
||||
is particularly helpful when one monadic function depends on multiple previous functions.
|
||||
|
||||
## Example using List
|
||||
|
||||
You can easily make `List` into a monad with the following, since List already provides an
|
||||
implementation of `pure` and `bind`.
|
||||
|
||||
-/
|
||||
instance : Monad List where
|
||||
pure := List.pure
|
||||
bind := List.bind
|
||||
/-!
|
||||
|
||||
Like you saw with the applicative `seq` operator, the `bind` operator applies the given function
|
||||
to every element of the list. It is useful to look at the bind implementation for List:
|
||||
|
||||
-/
|
||||
open List
|
||||
def bind (a : List α) (b : α → List β) : List β := join (map b a)
|
||||
/-!
|
||||
|
||||
So `Functor.map` is used to apply the function `b` to every element of `a` but this would
|
||||
return a whole bunch of little lists, so `join` is used to turn those back into a single list.
|
||||
|
||||
Here's an example where you use `bind` to convert a list of strings into a combined list of chars:
|
||||
|
||||
-/
|
||||
|
||||
#eval "apple".toList -- ['a', 'p', 'p', 'l', 'e']
|
||||
|
||||
#eval ["apple", "orange"] >>= String.toList
|
||||
-- ['a', 'p', 'p', 'l', 'e', 'o', 'r', 'a', 'n', 'g', 'e']
|
||||
|
||||
/-!
|
||||
|
||||
|
||||
## The IO Monad
|
||||
|
||||
The `IO Monad` is perhaps the most important monad in Lean. It is also one of the hardest monads to
|
||||
understand starting out. Its actual implementation is too intricate to discuss when first learning
|
||||
monads. So it is best to learn by example.
|
||||
|
||||
What is the **computational context** that describes the IO monad? IO operations can read
|
||||
information from or write information to the terminal, file system, operating system, and/or
|
||||
network. They interact with systems outside of your program. If you want to get user input, print a
|
||||
message to the user, read information from a file, or make a network call, you'll need to do so
|
||||
within the IO Monad.
|
||||
|
||||
The state of the world outside your program can change at virtually any moment, and so this IO
|
||||
context is particularly special. So these IO operations are "side effects" which means you cannot
|
||||
perform them from "pure" Lean functions.
|
||||
|
||||
Now, the most important job of pretty much any computer program is precisely to perform this
|
||||
interaction with the outside world. For this reason, the root of all executable Lean code is a
|
||||
function called main, with the type `IO Unit`. So every program starts in the IO monad!
|
||||
|
||||
When your function is `IO` monadic, you can get any input you need, call into "pure" code with the
|
||||
inputs, and then output the result in some way. The reverse does not work. You cannot call into IO
|
||||
code from pure code like you can call into a function that takes `Option` as input. Another way to
|
||||
say this is you cannot invent an `IO` context out of thin air, it has to be given to you in your
|
||||
`main` function.
|
||||
|
||||
Let's look at a simple program showing a few of the basic IO functions. It also uses `do` notation
|
||||
to make the code read nicely:
|
||||
-/
|
||||
def main : IO Unit := do
|
||||
IO.println "enter a line of text:"
|
||||
let stdin ← IO.getStdin -- IO IO.FS.Stream (monadic)
|
||||
let input ← stdin.getLine -- IO.FS.Stream → IO String (monadic)
|
||||
let uppercased := input.toUpper -- String → String (pure)
|
||||
IO.println uppercased -- IO Unit (monadic)
|
||||
/-!
|
||||
|
||||
So, once again you can see that the `do` notation lets you chain a series of monadic actions.
|
||||
`IO.getStdin` is of type `IO IO.FS.Stream` and `stdin.getLine` is of type `IO String`
|
||||
and `IO.println` is of type `IO Unit`.
|
||||
|
||||
In between you see a non-monadic expression `let uppercased := input.toUpper` which is fine too.
|
||||
A let statement can occur in any monad. Just as you could unwrap `i` from `Option Nat` to get the
|
||||
inner Nat, you can use `←` to unwrap the result of `getLine` to get a String. You can then manipulate
|
||||
this value using normal pure string functions like `toUpper`, and then you can pass the result to the
|
||||
`IO.println` function.
|
||||
|
||||
This is a simple echo program. It reads a line from the terminal, and then prints the line back out
|
||||
capitalized to the terminal. Hopefully it gives you a basic understanding of how IO works.
|
||||
|
||||
You can test this program using `lean --run` as follows:
|
||||
|
||||
```
|
||||
> lean --run Main.lean
|
||||
enter a line of text:
|
||||
the quick brown fox
|
||||
THE QUICK BROWN FOX
|
||||
```
|
||||
|
||||
Here the user entered the string `the quick brown fox` and got back the uppercase result.
|
||||
|
||||
## What separates Monads from Applicatives?
|
||||
|
||||
The key that separates these is **context**. You cannot really determine the structure of
|
||||
"future" operations without knowing the results of "past" operations, because the past can alter the
|
||||
context in which the future operations work. With applicatives, you can't get the final function
|
||||
result without evaluating everything, but you can determine the structure of how the operation will
|
||||
take place. This allows some degree of parallelism with applicatives that is not generally possible
|
||||
with monads.
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
Hopefully you now have a basic level understanding of what a monad is. But perhaps some more
|
||||
examples of what a "computational context" means would be useful to you. The Reader, State and
|
||||
Except monads each provide a concrete and easily understood context that can be compared easily to
|
||||
function parameters. You can learn more about those in [Reader monads](readers.lean.md),
|
||||
[State monads](states.lean.md), and the [Except monad](except.lean.md).
|
||||
-/
|
||||
@@ -1,194 +0,0 @@
|
||||
/-!
|
||||
# Readers
|
||||
|
||||
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
|
||||
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 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
|
||||
global variable. However, you want to load these parameters from the process environment variables,
|
||||
which requires the `IO` monad.
|
||||
-/
|
||||
|
||||
structure Environment where
|
||||
path : String
|
||||
home : String
|
||||
user : String
|
||||
deriving Repr
|
||||
|
||||
def getEnvDefault (name : String): IO String := do
|
||||
let val? ← IO.getEnv name
|
||||
pure <| match val? with
|
||||
| none => ""
|
||||
| some s => s
|
||||
|
||||
def loadEnv : IO Environment := do
|
||||
let path ← getEnvDefault "PATH"
|
||||
let home ← getEnvDefault "HOME"
|
||||
let user ← getEnvDefault "USER"
|
||||
pure { path, home, user }
|
||||
|
||||
def func1 (e : Environment) : Float :=
|
||||
let l1 := e.path.length
|
||||
let l2 := e.home.length * 2
|
||||
let l3 := e.user.length * 3
|
||||
(l1 + l2 + l3).toFloat * 2.1
|
||||
|
||||
def func2 (env : Environment) : Nat :=
|
||||
2 + (func1 env).floor.toUInt32.toNat
|
||||
|
||||
def func3 (env : Environment) : String :=
|
||||
"Result: " ++ (toString (func2 env))
|
||||
|
||||
def main : IO Unit := do
|
||||
let env ← loadEnv
|
||||
let str := func3 env
|
||||
IO.println str
|
||||
|
||||
#eval main -- Result: 7538
|
||||
|
||||
/-!
|
||||
The only function actually using the environment is func1. However func1 is a pure function. This
|
||||
means it cannot directly call loadEnv, an impure function in the IO monad. This means the
|
||||
environment has to be passed through as a variable to the other functions, just so they can
|
||||
ultimately pass it to func1. In a language with global variables, you could save env as a global
|
||||
value in main. Then func1 could access it directly. There would be no need to have it as a parameter
|
||||
to func1, func2 and func3. In larger programs, these "pass-through" variables can cause a lot of
|
||||
headaches.
|
||||
|
||||
## The Reader Solution
|
||||
|
||||
The `ReaderM` monad solves this problem. It effectively creates a global read-only value of a
|
||||
specified type. All functions within the monad can "read" the type. Let's look at how the `ReaderM`
|
||||
monad changes the shape of this code. Now the functions **no longer need** to be given the
|
||||
`Environment` as an explicit parameter, as they can access it through the monad.
|
||||
-/
|
||||
|
||||
def readerFunc1 : ReaderM Environment Float := do
|
||||
let env ← read
|
||||
let l1 := env.path.length
|
||||
let l2 := env.home.length * 2
|
||||
let l3 := env.user.length * 3
|
||||
return (l1 + l2 + l3).toFloat * 2.1
|
||||
|
||||
def readerFunc2 : ReaderM Environment Nat :=
|
||||
readerFunc1 >>= (fun x => return 2 + (x.floor.toUInt32.toNat))
|
||||
|
||||
def readerFunc3 : ReaderM Environment String := do
|
||||
let x ← readerFunc2
|
||||
return "Result: " ++ toString x
|
||||
|
||||
def main2 : IO Unit := do
|
||||
let env ← loadEnv
|
||||
let str := readerFunc3.run env
|
||||
IO.println str
|
||||
|
||||
#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 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`
|
||||
statement in Lean is closely related to `pure`, but a little different. First the similarity is that
|
||||
`return` and `pure` both lift a pure value up to the Monad type. But `return` is a keyword so you do
|
||||
not need to parenthesize the expression like you do when using `pure`. (Note: you can avoid
|
||||
parentheses when using `pure` by using the `<|` operator like we did above in the initial
|
||||
`getEnvDefault` function). Furthermore, `return` can also cause an early `return` in a monadic
|
||||
function similar to how it can in an imperative language while `pure` cannot.
|
||||
|
||||
> So technically if `return` is the last statement in a function it could be replaced with `pure <|`,
|
||||
but one could argue that `return` is still a little easier for most folks to read, just so long as
|
||||
you understand that `return` is doing more than other languages, it is also wrapping pure values in
|
||||
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 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.
|
||||
|
||||
The `let env ← read` expression in `readerFunc1` unwraps the environment from the `ReaderM` so we
|
||||
can use it. Each type of monad might provide one or more extra functions like this, functions that
|
||||
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.
|
||||
|
||||
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
|
||||
have an **explicit** Environment input parameter that needs to be passed along all the way to
|
||||
`readerFunc1`. Instead, the `ReaderM` monad is taking care of that for you, which gives you the
|
||||
illusion of something like global context where the context is now available to all functions that use
|
||||
the `ReaderM` monad.
|
||||
|
||||
The above code also introduces an important idea. Whenever you learn about a monad "X", there's
|
||||
often (but not always) a `run` function to execute that monad, and sometimes some additional
|
||||
functions like `read` that interact with the monad context.
|
||||
|
||||
You might be wondering, how does the context actually move through the `ReaderM` monad? How can you
|
||||
add an input argument to a function by modifying its return type? There is a special command in
|
||||
Lean that will show you the reduced types:
|
||||
-/
|
||||
#reduce ReaderM Environment String -- Environment → String
|
||||
/-!
|
||||
And you can see here that this type is actually a function! It's a function that takes an
|
||||
`Environment` as input and returns a `String`.
|
||||
|
||||
Now, remember in Lean that a function that takes an argument of type `Nat` and returns a `String`
|
||||
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 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
|
||||
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` typeclass to do this:
|
||||
|
||||
-/
|
||||
def readerFunc3WithReader : ReaderM Environment String := do
|
||||
let x ← withReader (λ env => { env with user := "new user" }) readerFunc2
|
||||
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`.
|
||||
|
||||
So `withReader f m` executes monad `m` in the `ReaderM` context modified by `f`.
|
||||
|
||||
## Handy shortcut with (← e)
|
||||
|
||||
If you use the operator `←` in a let expression and the variable is only used once you can
|
||||
eliminate the let expression and place the `←` operator in parentheses like this
|
||||
call to loadEnv:
|
||||
-/
|
||||
def main3 : IO Unit := do
|
||||
let str := readerFunc3 (← loadEnv)
|
||||
IO.println str
|
||||
/-!
|
||||
|
||||
## Conclusion
|
||||
|
||||
It might not seem like much has been accomplished with this `ReaderM Environment` monad, but you will
|
||||
find that in larger code bases, with many different types of monads all composed together this
|
||||
greatly cleans up the code. Monads provide a beautiful functional way of managing cross-cutting
|
||||
concerns that would otherwise make your code very messy.
|
||||
|
||||
Now it's time to move on to [StateM Monad](states.lean.md) which is like a `ReaderM` that is
|
||||
also updatable.
|
||||
-/
|
||||
@@ -1,252 +0,0 @@
|
||||
import Lean.Data.HashMap
|
||||
/-!
|
||||
# State
|
||||
|
||||
In the [previous section](readers.lean.md), you learned about the `ReaderM` monad. Hopefully this gave you
|
||||
a new perspective on Lean. It showed that, in fact, you _can_ have global variables of some sort;
|
||||
you just need to encode them in the type signature somehow, and this is what monads are for! In this
|
||||
part, you will explore the `StateM` monad, which is like a `ReaderM` only the state can also be updated.
|
||||
|
||||
## Motivating example: Tic Tac Toe
|
||||
|
||||
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.
|
||||
-/
|
||||
|
||||
open Std (HashMap)
|
||||
abbrev TileIndex := Nat × Nat -- a 2D index
|
||||
|
||||
inductive TileState where
|
||||
| TileEmpty | TileX | TileO
|
||||
deriving Repr, BEq
|
||||
|
||||
inductive Player where
|
||||
| XPlayer | OPlayer
|
||||
deriving Repr, BEq
|
||||
|
||||
abbrev Board := HashMap TileIndex TileState
|
||||
|
||||
structure GameState where
|
||||
board : Board
|
||||
currentPlayer : Player
|
||||
generator : StdGen
|
||||
|
||||
/-!
|
||||
Let's think at a high level about how some of the game functions would work. You could, for
|
||||
instance, have a function for selecting a random move. This would output a `TileIndex` to play and
|
||||
alter the game's number generator. You would then make a move based on the selected move and the
|
||||
current player. This would change the board state as well as swap the current player. In other
|
||||
words, you have operations that depend on the current state of the game, but also need to **update
|
||||
that state**.
|
||||
|
||||
## The StateM Monad to the Rescue
|
||||
|
||||
This is exactly the situation the `StateM` monad deals with. The `StateM` monad wraps computations in
|
||||
the context of reading and modifying a global state object.
|
||||
|
||||
It is parameterized by a single type parameter `s`, the state type in use. So just like the `ReaderM`
|
||||
has a single type you read from, the `StateM` has a single type you can both **read from and write
|
||||
to**. There are three primary actions you can take within the `StateM`monad:
|
||||
|
||||
- **get** - retrieves the state, like Reader.read
|
||||
- **set** - updates the state
|
||||
- **modifyGet** - retrieves the state, then updates it
|
||||
|
||||
There is also a `run` function, similar to `run` on `ReaderM`. Like the `ReaderM` monad, you must
|
||||
provide an initial state, in addition to the computation to run. `StateM` then produces two outputs:
|
||||
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 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`.
|
||||
|
||||
## Stateful Functions
|
||||
|
||||
Now you can examine some of the different functions mentioned above and determine their types.
|
||||
You can, for instance, pick a random move:
|
||||
|
||||
-/
|
||||
open TileState
|
||||
|
||||
def findOpen : StateM GameState (List TileIndex) := do
|
||||
let game ← get
|
||||
return game.board.toList.filterMap fun (i, x) => guard (x == TileEmpty) *> pure i
|
||||
|
||||
def chooseRandomMove : StateM GameState TileIndex := do
|
||||
let game ← get
|
||||
let openSpots ← findOpen
|
||||
let gen := game.generator
|
||||
let (i, gen') := randNat gen 0 (openSpots.length - 1)
|
||||
set { game with generator := gen' }
|
||||
return openSpots[i]!
|
||||
|
||||
/-!
|
||||
This returns a `TileIndex` and modifies the random number generator stored in the `GameState`!
|
||||
Notice you have a fun little use of the `Applicative.seqRight` operator `*>` in `findOpen`
|
||||
as described in [Applicatives](applicatives.lean.md).
|
||||
|
||||
Now you can create the function that can make a move:
|
||||
-/
|
||||
open Player
|
||||
|
||||
def tileStateForPlayer : Player → TileState
|
||||
| XPlayer => TileX
|
||||
| OPlayer => TileO
|
||||
|
||||
def nextPlayer : Player → Player
|
||||
| XPlayer => OPlayer
|
||||
| OPlayer => XPlayer
|
||||
|
||||
def applyMove (i : TileIndex): StateM GameState Unit := do
|
||||
let game ← get
|
||||
let p := game.currentPlayer
|
||||
let newBoard := game.board.insert i (tileStateForPlayer p)
|
||||
set { game with currentPlayer := nextPlayer p, board := newBoard }
|
||||
|
||||
/-!
|
||||
This updates the board in the `GameState` with the new tile, and then changes the current player,
|
||||
providing no output (`Unit` return type).
|
||||
|
||||
So finally, you can combine these functions together with `do` notation, and it actually looks quite
|
||||
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.
|
||||
-/
|
||||
|
||||
def isGameDone : StateM GameState Bool := do
|
||||
return (← findOpen).isEmpty
|
||||
|
||||
def nextTurn : StateM GameState Bool := do
|
||||
let i ← chooseRandomMove
|
||||
applyMove i
|
||||
isGameDone
|
||||
|
||||
/-!
|
||||
To give you a quick test harness that runs all moves for both players you can run this:
|
||||
-/
|
||||
|
||||
def initBoard : Board := Id.run do
|
||||
let mut board := HashMap.empty
|
||||
for i in [0:3] do
|
||||
for j in [0:3] do
|
||||
let t : TileIndex := (i, j)
|
||||
board := board.insert t TileEmpty
|
||||
board
|
||||
|
||||
def printBoard (board : Board) : IO Unit := do
|
||||
let mut row : List String := []
|
||||
for i in board.toList do
|
||||
let s := match i.2 with
|
||||
| TileEmpty => " "
|
||||
| TileX => "X"
|
||||
| TileO => "O"
|
||||
row := row.append [s]
|
||||
if row.length == 3 then
|
||||
IO.println row
|
||||
row := []
|
||||
|
||||
def playGame : StateM GameState Unit := do
|
||||
while true do
|
||||
let finished ← nextTurn
|
||||
if finished then return
|
||||
|
||||
def main : IO Unit := do
|
||||
let gen ← IO.stdGenRef.get
|
||||
let (x, gen') := randNat gen 0 1
|
||||
let gs := {
|
||||
board := initBoard,
|
||||
currentPlayer := if x = 0 then XPlayer else OPlayer,
|
||||
generator := gen' }
|
||||
let (_, g) := playGame |>.run gs
|
||||
printBoard g.board
|
||||
|
||||
#eval main
|
||||
-- [X, X, O]
|
||||
-- [X, O, O]
|
||||
-- [O, O, X]
|
||||
|
||||
/-!
|
||||
|
||||
Note that when you run the above code interactively the random number generator always starts in the
|
||||
same place. But if you run `lean --run states.lean` then you will see randomness in the result.
|
||||
|
||||
## Implementation
|
||||
|
||||
It may be helpful to see how the `StateM` monad adds the input state and output state. If you look
|
||||
at the reduced Type for `nextTurn`:
|
||||
-/
|
||||
#reduce StateM GameState Bool
|
||||
-- GameState → Bool × GameState
|
||||
/-!
|
||||
|
||||
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`. 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
|
||||
the state all the way through:
|
||||
-/
|
||||
def nextTurnManually : StateM GameState Bool
|
||||
| state =>
|
||||
let (i, gs) := chooseRandomMove |>.run state
|
||||
let (_, gs') := applyMove i |>.run gs
|
||||
let (result, gs'') := isGameDone |>.run gs'
|
||||
(result, gs'')
|
||||
|
||||
/-!
|
||||
|
||||
|
||||
## StateM vs ReaderM
|
||||
|
||||
While `ReaderM` functions can use `withReader` to modify the context before calling another function,
|
||||
`StateM` functions are a little more powerful, let's look at this function again:
|
||||
```
|
||||
def nextTurn : StateM GameState Bool := do
|
||||
let i ← chooseRandomMove
|
||||
applyMove i
|
||||
isGameDone
|
||||
```
|
||||
|
||||
In this function `chooseRandomMove` is modifying the state that `applyMove` is getting
|
||||
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.
|
||||
|
||||
## State, IO and other languages
|
||||
|
||||
When thinking about Lean, it is often seen as a restriction that you can't have global variables or
|
||||
`static` variables like you can with other languages like Python or C++. However, hopefully you see
|
||||
now this isn't true. You can have a data type with exactly the same functionality as a Python class.
|
||||
You would simply have many functions that can modify some global state using the `StateM` monad.
|
||||
|
||||
The difference is in Lean you simply put a label on these types of functions. You don't allow it to
|
||||
happen for free anywhere in an uncontrolled fashion because that results in too many sleepless
|
||||
nights debugging nasty code. You want to know when side effects can potentially happen, because
|
||||
knowing when they can happen makes your code easier to reason about. In a Python class, many of the
|
||||
methods won't actually need to modify the global state. But they could, which makes it harder to
|
||||
debug them. In Lean you can simply make these pure functions, and the compiler will ensure they stay
|
||||
pure and cannot modify any global state.
|
||||
|
||||
IO is the same way. It's not like you can't perform IO in Lean. Instead, you want to label the areas
|
||||
where you can, to increase your certainty about the areas where you don't need to. When you know part of
|
||||
your code cannot communicate with the outside world, you can be far more certain of its behavior.
|
||||
|
||||
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 reusable than code that uses global variables.
|
||||
|
||||
## Summary
|
||||
|
||||
That wraps it up for the `StateM` monad! There is one more very useful monad that can be used to do
|
||||
exception handling which will be covered in the [next section](except.lean.md).
|
||||
|
||||
-/
|
||||
@@ -1,316 +0,0 @@
|
||||
/-!
|
||||
# Monad Transformers
|
||||
|
||||
In the previous sections you learned about some handy monads [Option](monads.lean.md),
|
||||
[IO](monads.lean.md), [Reader](readers.lean.md), [State](states.lean.md) and
|
||||
[Except](except.lean.md), and you now know how to make your function use one of these, but what you
|
||||
do not yet know is how to make your function use multiple monads at once.
|
||||
|
||||
For example, suppose you need a function that wants to access some Reader context and optionally throw
|
||||
an exception? This would require composition of two monads `ReaderM` and `Except` and this is what
|
||||
monad transformers are for.
|
||||
|
||||
A monad transformer is fundamentally a wrapper type. It is generally parameterized by another
|
||||
monadic type. You can then run actions from the inner monad, while adding your own customized
|
||||
behavior for combining actions in this new monad. The common transformers add `T` to the end of an
|
||||
existing monad name. You will find `OptionT`, `ExceptT`, `ReaderT`, `StateT` but there is no transformer
|
||||
for `IO`. So generally if you need `IO` it becomes the innermost wrapped monad.
|
||||
|
||||
In the following example we use `ReaderT` to provide some read only context to a function
|
||||
and this `ReaderT` transformer will wrap an `Except` monad. If all goes well the
|
||||
`requiredArgument` returns the value of a required argument and `optionalSwitch`
|
||||
returns true if the optional argument is present.
|
||||
|
||||
-/
|
||||
abbrev Arguments := List String
|
||||
|
||||
def indexOf? [BEq α] (xs : List α) (s : α) (start := 0): Option Nat :=
|
||||
match xs with
|
||||
| [] => none
|
||||
| a :: tail => if a == s then some start else indexOf? tail s (start+1)
|
||||
|
||||
def requiredArgument (name : String) : ReaderT Arguments (Except String) String := do
|
||||
let args ← read
|
||||
let value := match indexOf? args name with
|
||||
| some i => if i + 1 < args.length then args[i+1]! else ""
|
||||
| none => ""
|
||||
if value == "" then throw s!"Command line argument {name} missing"
|
||||
return value
|
||||
|
||||
def optionalSwitch (name : String) : ReaderT Arguments (Except String) Bool := do
|
||||
let args ← read
|
||||
return match (indexOf? args name) with
|
||||
| some _ => true
|
||||
| none => false
|
||||
|
||||
#eval requiredArgument "--input" |>.run ["--input", "foo"]
|
||||
-- Except.ok "foo"
|
||||
|
||||
#eval requiredArgument "--input" |>.run ["foo", "bar"]
|
||||
-- Except.error "Command line argument --input missing"
|
||||
|
||||
#eval optionalSwitch "--help" |>.run ["--help"]
|
||||
-- Except.ok true
|
||||
|
||||
#eval optionalSwitch "--help" |>.run []
|
||||
-- Except.ok false
|
||||
|
||||
/-!
|
||||
Notice that `throw` was available from the inner `Except` monad. The cool thing is you can switch
|
||||
this around and get the exact same result using `ExceptT` as the outer monad transformer and
|
||||
`ReaderM` as the wrapped monad. Try changing requiredArgument to `ExceptT String (ReaderM Arguments) Bool`.
|
||||
|
||||
Note: the `|>.` notation is described in [Readers](readers.lean.md#the-reader-solution).
|
||||
|
||||
## Adding more layers
|
||||
|
||||
Here's the best part about monad transformers. Since the result of a monad transformer is itself a
|
||||
monad, you can wrap it inside another transformer! Suppose you need to pass in some read only context
|
||||
like the command line arguments, update some read-write state (like program Config) and optionally
|
||||
throw an exception, then you could write this:
|
||||
|
||||
-/
|
||||
structure Config where
|
||||
help : Bool := false
|
||||
verbose : Bool := false
|
||||
input : String := ""
|
||||
deriving Repr
|
||||
|
||||
abbrev CliConfigM := StateT Config (ReaderT Arguments (Except String))
|
||||
|
||||
def parseArguments : CliConfigM Bool := do
|
||||
let mut config ← get
|
||||
if (← optionalSwitch "--help") then
|
||||
throw "Usage: example [--help] [--verbose] [--input <input file>]"
|
||||
config := { config with
|
||||
verbose := (← optionalSwitch "--verbose"),
|
||||
input := (← requiredArgument "--input") }
|
||||
set config
|
||||
return true
|
||||
|
||||
def main (args : List String) : IO Unit := do
|
||||
let config : Config := { input := "default"}
|
||||
match parseArguments |>.run config |>.run args with
|
||||
| Except.ok (_, c) => do
|
||||
IO.println s!"Processing input '{c.input}' with verbose={c.verbose}"
|
||||
| Except.error s => IO.println s
|
||||
|
||||
|
||||
#eval main ["--help"]
|
||||
-- Usage: example [--help] [--verbose] [--input <input file>]
|
||||
|
||||
#eval main ["--input", "foo"]
|
||||
-- Processing input file 'foo' with verbose=false
|
||||
|
||||
#eval main ["--verbose", "--input", "bar"]
|
||||
-- Processing input 'bar' with verbose=true
|
||||
|
||||
/-!
|
||||
In this example `parseArguments` is actually three stacked monads, `StateM`, `ReaderM`, `Except`. Notice
|
||||
the convention of abbreviating long monadic types with an alias like `CliConfigM`.
|
||||
|
||||
## Monad Lifting
|
||||
|
||||
Lean makes it easy to compose functions that use different monads using a concept of automatic monad
|
||||
lifting. You already used lifting in the above code, because you were able to compose
|
||||
`optionalSwitch` which has type `ReaderT Arguments (Except String) Bool` and call it from
|
||||
`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 funciton:
|
||||
-/
|
||||
def divide (x : Float ) (y : Float): ExceptT String Id Float :=
|
||||
if y == 0 then
|
||||
throw "can't divide by zero"
|
||||
else
|
||||
pure (x / y)
|
||||
|
||||
#eval divide 6 3 -- Except.ok 2.000000
|
||||
#eval divide 1 0 -- Except.error "can't divide by zero"
|
||||
/-!
|
||||
|
||||
Notice here we used the `ExceptT` transformer, but we composed it with the `Id` identity monad.
|
||||
This is then the same as writing `Except String Float` since the identity monad does nothing.
|
||||
|
||||
Now suppose you want to count the number of times divide is called and store the result in some
|
||||
global state:
|
||||
-/
|
||||
|
||||
def divideCounter (x : Float) (y : Float) : StateT Nat (ExceptT String Id) Float := do
|
||||
modify fun s => s + 1
|
||||
divide x y
|
||||
|
||||
#eval divideCounter 6 3 |>.run 0 -- Except.ok (2.000000, 1)
|
||||
#eval divideCounter 1 0 |>.run 0 -- Except.error "can't divide by zero"
|
||||
|
||||
/-!
|
||||
|
||||
The `modify` function is a helper which makes it easier to use `modifyGet` from the `StateM` monad.
|
||||
But something interesting is happening here, `divideCounter` is returning the value of
|
||||
`divide`, but the types don't match, yet it works? This is monad lifting in action.
|
||||
|
||||
You can see this more clearly with the following test:
|
||||
|
||||
-/
|
||||
def liftTest (x : Except String Float) :
|
||||
StateT Nat (Except String) Float := x
|
||||
|
||||
#eval liftTest (divide 5 1) |>.run 3 -- Except.ok (5.000000, 3)
|
||||
|
||||
/-!
|
||||
|
||||
Notice that `liftTest` returned `x` without doing anything to it, yet that matched the return type
|
||||
`StateT Nat (Except String) Float`. Monad lifting is provided by monad transformers. if you
|
||||
`#print liftTest` you will see that Lean is implementing this using a call to a function named
|
||||
`monadLift` from the `MonadLift` type class:
|
||||
|
||||
```lean,ignore
|
||||
class MonadLift (m : Type u → Type v) (n : Type u → Type w) where
|
||||
monadLift : {α : Type u} → m α → n α
|
||||
```
|
||||
|
||||
So `monadLift` is a function for lifting a computation from an inner `Monad m α ` to an outer `Monad n α`.
|
||||
You could replace `x` in `liftTest` with `monadLift x` if you want to be explicit about it.
|
||||
|
||||
The StateT monad transformer defines an instance of `MonadLift` like this:
|
||||
|
||||
```lean
|
||||
@[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⟩
|
||||
```
|
||||
This means that any monad `m` can be wrapped in a `StateT` monad by using the function
|
||||
`fun s => do let a ← t; pure (a, s)` that takes state `s`, runs the inner monad action `t`, and
|
||||
returns the result and the new state in a pair `(a, s)` without making any changes to `s`.
|
||||
|
||||
Because `MonadLift` is a type class, Lean can automatically find the required `monadLift`
|
||||
instances in order to make your code compile and in this way it was able to find the `StateT.lift`
|
||||
function and use it to wrap the result of `divide` so that the correct type is returned from
|
||||
`divideCounter`.
|
||||
|
||||
If you have an instance `MonadLift m n` that means there is a way to turn a computation that happens
|
||||
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 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.
|
||||
|
||||
## Transitive lifting
|
||||
|
||||
There is also a transitive version of `MonadLift` called `MonadLiftT` which can lift multiple
|
||||
monad layers at once. In the following example we added another monad layer with
|
||||
`ReaderT String ...` and notice that `x` is also automatically lifted to match.
|
||||
|
||||
-/
|
||||
def liftTest2 (x : Except String Float) :
|
||||
ReaderT String (StateT Nat (Except String)) Float := x
|
||||
|
||||
#eval liftTest2 (divide 5 1) |>.run "" |>.run 3
|
||||
-- Except.ok (5.000000, 3)
|
||||
|
||||
/-!
|
||||
|
||||
The ReaderT monadLift is even simpler than the one for StateT:
|
||||
|
||||
```lean,ignore
|
||||
instance : MonadLift m (ReaderT ρ m) where
|
||||
monadLift x := fun _ => x
|
||||
```
|
||||
|
||||
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 and trivial way to implement a `ReaderM` monad.
|
||||
|
||||
## Add your own Custom MonadLift
|
||||
|
||||
This does not compile:
|
||||
-/
|
||||
def main2 : IO Unit := do
|
||||
try
|
||||
let ret ← divideCounter 5 2 |>.run 0
|
||||
IO.println (toString ret)
|
||||
catch e =>
|
||||
IO.println e
|
||||
|
||||
/-!
|
||||
saying:
|
||||
```
|
||||
typeclass instance problem is stuck, it is often due to metavariables
|
||||
ToString ?m.4786
|
||||
```
|
||||
|
||||
The reason is `divideCounter` returns the big `StateT Nat (ExceptT String Id) Float` and that type
|
||||
cannot be automatically lifted into the `main` return type of `IO Unit` unless you give it some
|
||||
help.
|
||||
|
||||
The following custom `MonadLift` solves this problem:
|
||||
|
||||
-/
|
||||
def liftIO (t : ExceptT String Id α) : IO α := do
|
||||
match t with
|
||||
| .ok r => EStateM.Result.ok r
|
||||
| .error s => EStateM.Result.error s
|
||||
|
||||
instance : MonadLift (ExceptT String Id) IO where
|
||||
monadLift := liftIO
|
||||
|
||||
def main3 : IO Unit := do
|
||||
try
|
||||
let ret ← divideCounter 5 2 |>.run 0
|
||||
IO.println (toString ret)
|
||||
catch e =>
|
||||
IO.println e
|
||||
|
||||
#eval main3 -- (2.500000, 1)
|
||||
/-!
|
||||
|
||||
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.
|
||||
|
||||
## Lifting ExceptT
|
||||
|
||||
In the previous [Except](except.lean.md) section you saw functions that `throw` Except
|
||||
values. When you get all the way back up to your `main` function which has type `IO Unit` you have
|
||||
the same problem you had above, because `Except String Float` doesn't match even if you use a
|
||||
`try/catch`.
|
||||
|
||||
-/
|
||||
|
||||
def main4 : IO Unit := do
|
||||
try
|
||||
let ret ← divide 5 0
|
||||
IO.println (toString ret) -- lifting happens here.
|
||||
catch e =>
|
||||
IO.println s!"Unhandled exception: {e}"
|
||||
|
||||
#eval main4 -- Unhandled exception: can't divide by zero
|
||||
|
||||
/-!
|
||||
|
||||
Without the `liftIO` the `(toString ret)` expression would not compile with a similar error:
|
||||
|
||||
```
|
||||
typeclass instance problem is stuck, it is often due to metavariables
|
||||
ToString ?m.6007
|
||||
```
|
||||
|
||||
So the general lesson is that if you see an error like this when using monads, check for
|
||||
a missing `MonadLift`.
|
||||
|
||||
## Summary
|
||||
|
||||
Now that you know how to combine your monads together, you're almost done with understanding the key
|
||||
concepts of monads! You could probably go out now and start writing some pretty nice code! But to
|
||||
truly master monads, you should know how to make your own, and there's one final concept that you
|
||||
should understand for that. This is the idea of type "laws". Each of the structures you've learned
|
||||
so far has a series of laws associated with it. And for your instances of these classes to make
|
||||
sense, they should follow the laws! Check out [Monad Laws](laws.lean.md).
|
||||
-/
|
||||
@@ -78,6 +78,4 @@ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left:
|
||||
.highlight .vg, .code .vg { color: #ce5c00; text-decoration: underline } /* Name.Variable.Global */
|
||||
.highlight .vi, .code .vi { color: #ce5c00 } /* Name.Variable.Instance */
|
||||
.highlight .vm, .code .vm { color: #ce5c00 } /* Name.Variable.Magic */
|
||||
.highlight .il, .code .il { color: #2e3436 } /* Literal.Number.Integer.Long */
|
||||
.hljs-doctag { color: green }
|
||||
.hljs-comment { color: green }
|
||||
.highlight .il, .code .il { color: #2e3436 } /* Literal.Number.Integer.Long */
|
||||
@@ -125,7 +125,7 @@ If you don't want to or cannot start the pinned editor from Nix, e.g. because yo
|
||||
$ 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).
|
||||
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>
|
||||
|
||||
|
||||
@@ -54,16 +54,20 @@ First [install Pygments](https://pygments.org/download/). Then save [`lean4.py`]
|
||||
% instruct minted to use our local theorem.py
|
||||
\newmintinline[lean]{lean4.py:Lean4Lexer -x}{bgcolor=white}
|
||||
\newminted[leancode]{lean4.py:Lean4Lexer -x}{fontsize=\footnotesize}
|
||||
\usemintedstyle{tango} % a nice, colorful theme
|
||||
|
||||
\begin{document}
|
||||
\begin{leancode}
|
||||
% some example options
|
||||
\begin{minted}[mathescape,
|
||||
linenos,
|
||||
numbersep=5pt,
|
||||
frame=lines,
|
||||
framesep=2mm]{Lean}
|
||||
theorem funext {f₁ f₂ : ∀ (x : α), β x} (h : ∀ x, f₁ x = f₂ x) : f₁ = f₂ := by
|
||||
show extfunApp (Quotient.mk' f₁) = extfunApp (Quotient.mk' f₂)
|
||||
show extfunApp (Quotient.mk f₁) = extfunApp (Quotient.mk f₂)
|
||||
apply congrArg
|
||||
apply Quotient.sound
|
||||
exact h
|
||||
\end{leancode}
|
||||
\end{minted}
|
||||
\end{document}
|
||||
```
|
||||
|
||||
|
||||
@@ -273,20 +273,6 @@ def getUnit [Monoid α] : α :=
|
||||
1
|
||||
```
|
||||
|
||||
Because many users were forgetting the `nat_lit` when defining `OfNat` instances, Lean also accepts `OfNat` instance
|
||||
declarations not using `nat_lit`. Thus, the following is also accepted.
|
||||
```lean
|
||||
class Monoid (α : Type u) where
|
||||
unit : α
|
||||
op : α → α → α
|
||||
|
||||
instance [s : Monoid α] : OfNat α 1 where
|
||||
ofNat := s.unit
|
||||
|
||||
def getUnit [Monoid α] : α :=
|
||||
1
|
||||
```
|
||||
|
||||
## Output parameters
|
||||
|
||||
By default, Lean only tries to synthesize an instance `Inhabited T` when the term `T` is known and does not
|
||||
|
||||
@@ -33,7 +33,6 @@ Lean has numerous features, including:
|
||||
- Powerful data types
|
||||
- Pattern matching
|
||||
- [Type classes](./typeclass.md)
|
||||
- [Monads](./monads/intro.md)
|
||||
- [Extensible syntax](./syntax.md)
|
||||
- Hygienic macros
|
||||
- [Dependent types](https://leanprover.github.io/theorem_proving_in_lean4/dependent_type_theory.html)
|
||||
|
||||
@@ -85,16 +85,15 @@ rec {
|
||||
# use same stage for retrieving dependencies
|
||||
lean-leanDeps = stage0;
|
||||
lean-final = self;
|
||||
leanFlags = [ "-DwarningAsError=true" ];
|
||||
} ({
|
||||
src = ../src;
|
||||
roots = [ { mod = args.name; glob = "andSubmodules"; } ];
|
||||
fullSrc = ../.;
|
||||
srcPrefix = "src";
|
||||
inherit debug;
|
||||
} // args);
|
||||
Init' = build { name = "Init"; deps = []; };
|
||||
Lean' = build { name = "Lean"; deps = [ Init' ]; };
|
||||
Std' = build { name = "Std"; deps = [ Init' ]; };
|
||||
Lean' = build { name = "Lean"; deps = [ Init' Std' ]; };
|
||||
attachSharedLib = sharedLib: pkg: pkg // {
|
||||
inherit sharedLib;
|
||||
mods = mapAttrs (_: m: m // { inherit sharedLib; propagatedLoadDynlibs = []; }) pkg.mods;
|
||||
@@ -102,21 +101,22 @@ rec {
|
||||
in (all: all // all.lean) rec {
|
||||
inherit (Lean) emacs-dev emacs-package vscode-dev vscode-package;
|
||||
Init = attachSharedLib leanshared Init';
|
||||
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Init ]; };
|
||||
stdlib = [ Init Lean ];
|
||||
Std = attachSharedLib leanshared Std' // { allExternalDeps = [ Init ]; };
|
||||
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Init Std ]; };
|
||||
stdlib = [ Init Std Lean ];
|
||||
modDepsFiles = symlinkJoin { name = "modDepsFiles"; paths = map (l: l.modDepsFile) (stdlib ++ [ Leanc ]); };
|
||||
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";
|
||||
Leanc = build { name = "Leanc"; src = lean-bin-tools-unwrapped.leanc_src; deps = stdlib; };
|
||||
stdlibLinkFlags = "-L${Init.staticLib} -L${Std.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 "-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} \
|
||||
${if stdenv.isDarwin then "-Wl,-force_load,${Init.staticLib}/libInit.a -Wl,-force_load,${Std.staticLib}/libStd.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 -lStd -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++"} -lm ${stdlibLinkFlags} \
|
||||
-o $out/$libName
|
||||
'';
|
||||
mods = Init.mods // Lean.mods;
|
||||
mods = Init.mods // Std.mods // Lean.mods;
|
||||
leanc = writeShellScriptBin "leanc" ''
|
||||
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable.override { withSharedStdlib = true; }}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${leanshared} "$@"
|
||||
'';
|
||||
|
||||
@@ -36,8 +36,7 @@ with builtins; let
|
||||
modToAbsPath = mod: "${src}/${modToPath mod}";
|
||||
modToLean = mod: "${modToAbsPath mod}.lean";
|
||||
bareStdenv = ./bareStdenv;
|
||||
mkBareDerivation = args: derivation (args // {
|
||||
name = lib.strings.sanitizeDerivationName args.name;
|
||||
mkBareDerivation = args@{ buildCommand, ... }: derivation (args // {
|
||||
stdenv = bareStdenv;
|
||||
inherit (stdenv) system;
|
||||
buildInputs = (args.buildInputs or []) ++ [ coreutils ];
|
||||
@@ -45,7 +44,7 @@ with builtins; let
|
||||
args = [ "-c" ''
|
||||
source $stdenv/setup
|
||||
set -u
|
||||
${args.buildCommand}
|
||||
${buildCommand}
|
||||
'' ];
|
||||
});
|
||||
runBareCommand = name: args: buildCommand: mkBareDerivation (args // { inherit name buildCommand; });
|
||||
@@ -208,7 +207,7 @@ with builtins; let
|
||||
if typeOf g == "string" then [g]
|
||||
else if g.glob == "one" then [g.mod]
|
||||
else if g.glob == "submodules" then submodules g.mod
|
||||
else if g.glob == "andSubmodules" then [g.mod] ++ submodules g.mod
|
||||
else if g.glob == "andSubmodules" then [g] ++ submodules g.mod
|
||||
else throw "unknown glob kind '${g}'";
|
||||
mods' = lib.foldr buildModAndDeps {} (concatMap expandGlob roots);
|
||||
allLinkFlags = lib.foldr (shared: acc: acc ++ [ "-L${shared}" "-l${shared.linkName or shared.name}" ]) linkFlags allNativeSharedLibs;
|
||||
|
||||
@@ -25,4 +25,4 @@ args=(-- "$@")
|
||||
# HACK: use stage 0 instead of 1 inside Lean's own `src/`
|
||||
[[ -d Lean && -f ../flake.nix ]] && target="@srcTarget@" && args=@srcArgs@
|
||||
|
||||
LEAN_SYSROOT="$(dirname "$0")/.." exec @nix@/bin/nix ${LEAN_NIX_ARGS:-} run "$target" ${args[*]}
|
||||
LEAN_SYSROOT="$(dirname "$0")/.." exec @nix@/bin/nix run "$target" ${args[*]}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
rm -r stage0 || true
|
||||
for pkg in Init Lean; do
|
||||
for pkg in Init Std 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
|
||||
|
||||
@@ -263,14 +263,14 @@ find_package(PythonInterp)
|
||||
|
||||
include_directories(${CMAKE_BINARY_DIR}/include)
|
||||
|
||||
# libleancpp/Lean as well as libleanrt/Init are cyclically dependent. This works by default on macOS, which also doesn't like
|
||||
# libleancpp/Lean as well as libleanrt/Init/Std are cyclically dependent. This works by default on macOS, which also doesn't like
|
||||
# the linker flags necessary on other platforms.
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lleanrt")
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lnodefs.js -lleanrt")
|
||||
else()
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
|
||||
string(APPEND LEANC_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lStd -lleanrt -Wl,--end-group")
|
||||
endif()
|
||||
|
||||
set(LEAN_CXX_STDLIB "-lstdc++" CACHE STRING "C++ stdlib linker flags")
|
||||
@@ -445,9 +445,9 @@ string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
|
||||
file(RELATIVE_PATH LIB ${LEAN_SOURCE_DIR} ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libStd.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
|
||||
else()
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
|
||||
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit -lStd -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
|
||||
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
|
||||
endif()
|
||||
@@ -462,7 +462,7 @@ add_custom_target(make_stdlib ALL
|
||||
# The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server
|
||||
# for a parallelized nested build, but CMake doesn't let us do that.
|
||||
# We use `lean` from the previous stage, but `leanc`, headers, etc. from the current stage
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Lean
|
||||
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean
|
||||
VERBATIM)
|
||||
|
||||
# We declare these as separate custom targets so they use separate `make` invocations, which makes `make` recompute which dependencies
|
||||
|
||||
@@ -15,8 +15,6 @@ import Init.WFTactics
|
||||
import Init.Data
|
||||
import Init.System
|
||||
import Init.Util
|
||||
import Init.Dynamic
|
||||
import Init.ShareCommon
|
||||
import Init.Meta
|
||||
import Init.NotationExtra
|
||||
import Init.SimpLemmas
|
||||
|
||||
@@ -9,7 +9,7 @@ import Init.NotationExtra
|
||||
|
||||
universe u v
|
||||
|
||||
/-! # Classical reasoning support -/
|
||||
/- Classical reasoning support -/
|
||||
|
||||
namespace Classical
|
||||
|
||||
@@ -65,7 +65,7 @@ noncomputable def inhabited_of_nonempty {α : Sort u} (h : Nonempty α) : Inhabi
|
||||
noncomputable def inhabited_of_exists {α : Sort u} {p : α → Prop} (h : ∃ x, p x) : Inhabited α :=
|
||||
inhabited_of_nonempty (Exists.elim h (fun w _ => ⟨w⟩))
|
||||
|
||||
/-- All propositions are `Decidable`. -/
|
||||
/- all propositions are Decidable -/
|
||||
noncomputable scoped instance (priority := low) propDecidable (a : Prop) : Decidable a :=
|
||||
choice <| match em a with
|
||||
| Or.inl h => ⟨isTrue h⟩
|
||||
@@ -129,14 +129,14 @@ theorem byContradiction {p : Prop} (h : ¬p → False) : p :=
|
||||
syntax "by_cases" (atomic(ident ":"))? term : tactic
|
||||
|
||||
macro_rules
|
||||
| `(tactic| by_cases $h : $e) =>
|
||||
| `(tactic| by_cases $h:ident : $e:term) =>
|
||||
`(tactic|
|
||||
cases em $e with
|
||||
| inl $h => _
|
||||
| inr $h => _)
|
||||
| `(tactic| by_cases $e) =>
|
||||
cases em $e:term with
|
||||
| inl $h:ident => _
|
||||
| inr $h:ident => _)
|
||||
| `(tactic| by_cases $e:term) =>
|
||||
`(tactic|
|
||||
cases em $e with
|
||||
cases em $e:term with
|
||||
| inl h => _
|
||||
| inr h => _)
|
||||
|
||||
|
||||
@@ -1,207 +1,45 @@
|
||||
/-
|
||||
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Mario Carneiro
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Prelude
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
|
||||
/-!
|
||||
# Coercion
|
||||
|
||||
Lean uses a somewhat elaborate system of typeclasses to drive the coercion system.
|
||||
Here a *coercion* means an invisible function that is automatically inserted
|
||||
to fix what would otherwise be a type error. For example, if we have:
|
||||
```
|
||||
def f (x : Nat) : Int := x
|
||||
```
|
||||
then this is clearly not type correct as is, because `x` has type `Nat` but
|
||||
type `Int` is expected, and normally you will get an error message saying exactly that.
|
||||
But before it shows that message, it will attempt to synthesize an instance of
|
||||
`CoeT Nat x Int`, which will end up going through all the other typeclasses defined
|
||||
below, to discover that there is an instance of `Coe Nat Int` defined.
|
||||
|
||||
This instance is defined as:
|
||||
```
|
||||
instance : Coe Nat Int := ⟨Int.ofNat⟩
|
||||
```
|
||||
so Lean will elaborate the original function `f` as if it said:
|
||||
```
|
||||
def f (x : Nat) : Int := Int.ofNat x
|
||||
```
|
||||
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:
|
||||
```
|
||||
def f (x y : Nat) : Int := x + y
|
||||
```
|
||||
This could be either `↑x + ↑y` where `+` is the addition on `Int`, or `↑(x + y)`
|
||||
where `+` is addition on `Nat`, or even `x + y` using a heterogeneous addition
|
||||
with the type `Nat → Nat → Int`. You can use the `↑` operator to disambiguate
|
||||
between these possibilities, but generally Lean will elaborate working from the
|
||||
"outside in", meaning that it will first look at the expression `_ + _ : Int`
|
||||
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.
|
||||
|
||||
## Important typeclasses
|
||||
|
||||
* `Coe α β` is the most basic class, and the usual one you will want to use
|
||||
when implementing a coercion for your own types.
|
||||
|
||||
* `CoeDep α (x : α) β` allows `β` to depend not only on `α` but on the value
|
||||
`x : α` itself. This is useful when the coercion function is dependent.
|
||||
An example of a dependent coercion is the instance for `Prop → Bool`, because
|
||||
it only holds for `Decidable` propositions. It is defined as:
|
||||
```
|
||||
instance (p : Prop) [Decidable p] : CoeDep Prop p Bool := ...
|
||||
```
|
||||
|
||||
* `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. This is automatically turned into `CoeFun.coe f x`.
|
||||
|
||||
* `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)`.
|
||||
|
||||
* `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'
|
||||
|
||||
/--
|
||||
`Coe α β` is the typeclass for coercions from `α` to `β`. It can be transitively
|
||||
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 (α : Sort u) (β : Sort v) where
|
||||
/-- Coerces a value of type `α` to type `β`. Accessible by the notation `↑x`,
|
||||
or by double type ascription `((x : α) : β)`. -/
|
||||
coe : α → β
|
||||
|
||||
/--
|
||||
Auxiliary class that contains the transitive closure of `Coe`.
|
||||
Users should generally not implement this directly.
|
||||
-/
|
||||
/-- Auxiliary class that contains the transitive closure of `Coe`. -/
|
||||
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 : α → β
|
||||
|
||||
/--
|
||||
`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.
|
||||
-/
|
||||
/- Expensive coercion that can only appear at the beginning of a sequence of coercions. -/
|
||||
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 : α → β
|
||||
|
||||
/--
|
||||
`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.
|
||||
-/
|
||||
/- Expensive coercion that can only appear at the end of a sequence of coercions. -/
|
||||
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 : α → β
|
||||
|
||||
/--
|
||||
Auxiliary class that contains `CoeHead` + `CoeTC` + `CoeTail`.
|
||||
|
||||
A `CoeHTCT` chain has the "grammar" `(CoeHead)? (Coe)* (CoeTail)?`, except that
|
||||
the empty sequence is not allowed.
|
||||
-/
|
||||
/-- Auxiliary class that contains `CoeHead` + `CoeTC` + `CoeTail`. -/
|
||||
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 : α → β
|
||||
|
||||
/--
|
||||
`CoeDep α (x : α) β` is a typeclass for dependent coercions, that is, the type `β`
|
||||
can depend on `x` (or rather, the value of `x` is available to typeclass search
|
||||
so an instance that relates `β` to `x` is allowed).
|
||||
|
||||
Dependent coercions do not participate in the transitive chaining process of
|
||||
regular coercions: they must exactly match the type mismatch on both sides.
|
||||
-/
|
||||
class CoeDep (α : 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 : β
|
||||
|
||||
/--
|
||||
`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)? (Coe)* (CoeTail)? | CoeDep`,
|
||||
except that the empty sequence is not allowed (identity coercions don't need
|
||||
the coercion system at all).
|
||||
-/
|
||||
/- Combines CoeHead, CoeTC, CoeTail, CoeDep -/
|
||||
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 : β
|
||||
|
||||
|
||||
/--
|
||||
`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. 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
|
||||
coe : (a : α) → γ a
|
||||
|
||||
/--
|
||||
`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 : α → β
|
||||
|
||||
/--
|
||||
`↑x` represents a coercion, which converts `x` of type `α` to type `β`, using
|
||||
typeclasses to resolve a suitable conversion function. You can often leave the
|
||||
`↑` off entirely, since coercion is triggered implicitly whenever there is a
|
||||
type error, but in ambiguous cases it can be useful to use `↑` to disambiguate
|
||||
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
|
||||
@@ -243,7 +81,7 @@ instance coeId {α : Sort u} (a : α) : CoeT α a α where
|
||||
instance coeSortToCoeTail [inst : CoeSort α β] : CoeTail α β where
|
||||
coe := inst.coe
|
||||
|
||||
/-! # Basic instances -/
|
||||
/- Basic instances -/
|
||||
|
||||
@[inline] instance boolToProp : Coe Bool Prop where
|
||||
coe b := Eq b true
|
||||
@@ -260,26 +98,15 @@ instance optionCoe {α : Type u} : CoeTail α (Option α) where
|
||||
instance subtypeCoe {α : Sort u} {p : α → Prop} : CoeHead (Subtype p) α where
|
||||
coe v := v.val
|
||||
|
||||
/-! # Coe bridge -/
|
||||
/- Coe bridge -/
|
||||
|
||||
/--
|
||||
Helper definition used by the elaborator. It is not meant to be used directly by users.
|
||||
|
||||
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] 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
|
||||
-- Helper definition used by the elaborator. It is not meant to be used directly by users
|
||||
@[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)
|
||||
|
||||
/--
|
||||
Helper definition used by the elaborator. It is not meant to be used directly by users.
|
||||
|
||||
This is used for coercing the result type under a monad.
|
||||
-/
|
||||
@[inline] def Lean.Internal.coeM {m : Type u → Type v} {α β : Type u}
|
||||
[∀ a, CoeT α a β] [Monad m] (x : m α) : m β := do
|
||||
-- Helper definition used by the elaborator. It is not meant to be used directly by users
|
||||
@[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)
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ def control {m : Type u → Type v} {n : Type u → Type w} [MonadControlT m n]
|
||||
(f : ({β : Type u} → n β → m (stM m n β)) → m (stM m n α)) : n α :=
|
||||
controlAt m f
|
||||
|
||||
/--
|
||||
/-
|
||||
Typeclass for the polymorphic `forM` operation described in the "do unchained" paper.
|
||||
Remark:
|
||||
- `γ` is a "container" type of elements of type `α`.
|
||||
|
||||
@@ -34,13 +34,10 @@ variable {ε : Type u}
|
||||
| Except.error err => Except.error err
|
||||
| Except.ok v => f v
|
||||
|
||||
/-- Returns true if the value is `Except.ok`, false otherwise. -/
|
||||
@[inline] protected def toBool : Except ε α → Bool
|
||||
| Except.ok _ => true
|
||||
| Except.error _ => false
|
||||
|
||||
abbrev isOk : Except ε α → Bool := Except.toBool
|
||||
|
||||
@[inline] protected def toOption : Except ε α → Option α
|
||||
| Except.ok a => some a
|
||||
| Except.error _ => none
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Control.Lawful
|
||||
|
||||
/-!
|
||||
/-
|
||||
The Exception monad transformer using CPS style.
|
||||
-/
|
||||
|
||||
|
||||
@@ -50,7 +50,13 @@ class LawfulMonad (m : Type u → Type v) [Monad m] extends LawfulApplicative m
|
||||
bind_assoc (x : m α) (f : α → m β) (g : β → m γ) : x >>= f >>= g = x >>= fun x => f x >>= g
|
||||
map_pure g x := (by rw [← bind_pure_comp, pure_bind])
|
||||
seq_pure g x := (by rw [← bind_map]; simp [map_pure, bind_pure_comp])
|
||||
seq_assoc x g h := (by simp [← bind_pure_comp, ← bind_map, bind_assoc, pure_bind])
|
||||
seq_assoc x g h := (by
|
||||
-- TODO: support for applying `symm` at `simp` arguments
|
||||
have bind_pure_comp_symm {α β : Type u} (f : α → β) (x : m α) : f <$> x = x >>= fun a => pure (f a) := by
|
||||
rw [bind_pure_comp]
|
||||
have bind_map_symm {α β : Type u} (f : m (α → (β : Type u))) (x : m α) : f <*> x = f >>= (. <$> x) := by
|
||||
rw [bind_map]
|
||||
simp[bind_pure_comp_symm, bind_map_symm, bind_assoc, pure_bind])
|
||||
|
||||
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
|
||||
attribute [simp] pure_bind bind_assoc
|
||||
@@ -84,7 +90,7 @@ theorem seqRight_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x *>
|
||||
theorem seqLeft_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x <* y = x >>= fun a => y >>= fun _ => pure a := by
|
||||
rw [seqLeft_eq]; simp [map_eq_pure_bind, seq_eq_bind_map]
|
||||
|
||||
/-! # Id -/
|
||||
/- Id -/
|
||||
|
||||
namespace Id
|
||||
|
||||
@@ -97,7 +103,7 @@ instance : LawfulMonad Id := by
|
||||
|
||||
end Id
|
||||
|
||||
/-! # ExceptT -/
|
||||
/- ExceptT -/
|
||||
|
||||
namespace ExceptT
|
||||
|
||||
@@ -173,11 +179,11 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (ExceptT ε m) where
|
||||
|
||||
end ExceptT
|
||||
|
||||
/-! # ReaderT -/
|
||||
/- ReaderT -/
|
||||
|
||||
namespace ReaderT
|
||||
|
||||
theorem ext {x y : ReaderT ρ m α} (h : ∀ ctx, x.run ctx = y.run ctx) : x = y := by
|
||||
theorem ext [Monad m] {x y : ReaderT ρ m α} (h : ∀ ctx, x.run ctx = y.run ctx) : x = y := by
|
||||
simp [run] at h
|
||||
exact funext h
|
||||
|
||||
@@ -186,56 +192,45 @@ theorem ext {x y : ReaderT ρ m α} (h : ∀ ctx, x.run ctx = y.run ctx) : x = y
|
||||
@[simp] theorem run_bind [Monad m] (x : ReaderT ρ m α) (f : α → ReaderT ρ m β) (ctx : ρ)
|
||||
: (x >>= f).run ctx = x.run ctx >>= λ a => (f a).run ctx := rfl
|
||||
|
||||
@[simp] theorem run_mapConst [Monad m] (a : α) (x : ReaderT ρ m β) (ctx : ρ)
|
||||
: (Functor.mapConst a x).run ctx = Functor.mapConst a (x.run ctx) := rfl
|
||||
|
||||
@[simp] theorem run_map [Monad m] (f : α → β) (x : ReaderT ρ m α) (ctx : ρ)
|
||||
: (f <$> x).run ctx = f <$> x.run ctx := rfl
|
||||
|
||||
@[simp] theorem run_monadLift [MonadLiftT n m] (x : n α) (ctx : ρ)
|
||||
: (monadLift x : ReaderT ρ m α).run ctx = (monadLift x : m α) := rfl
|
||||
|
||||
@[simp] theorem run_monadMap [MonadFunctor n m] (f : {β : Type u} → n β → n β) (x : ReaderT ρ m α) (ctx : ρ)
|
||||
@[simp] theorem run_monadMap [Monad m] [MonadFunctor n m] (f : {β : Type u} → n β → n β) (x : ReaderT ρ m α) (ctx : ρ)
|
||||
: (monadMap @f x : ReaderT ρ m α).run ctx = monadMap @f (x.run ctx) := rfl
|
||||
|
||||
@[simp] theorem run_read [Monad m] (ctx : ρ) : (ReaderT.read : ReaderT ρ m ρ).run ctx = pure ctx := rfl
|
||||
|
||||
@[simp] theorem run_seq {α β : Type u} [Monad m] (f : ReaderT ρ m (α → β)) (x : ReaderT ρ m α) (ctx : ρ)
|
||||
: (f <*> x).run ctx = (f.run ctx <*> x.run ctx) := rfl
|
||||
@[simp] theorem run_seq {α β : Type u} [Monad m] [LawfulMonad m] (f : ReaderT ρ m (α → β)) (x : ReaderT ρ m α) (ctx : ρ) : (f <*> x).run ctx = (f.run ctx <*> x.run ctx) := by
|
||||
rw [seq_eq_bind (m := m)]; rfl
|
||||
|
||||
@[simp] theorem run_seqRight [Monad m] (x : ReaderT ρ m α) (y : ReaderT ρ m β) (ctx : ρ)
|
||||
: (x *> y).run ctx = (x.run ctx *> y.run ctx) := rfl
|
||||
@[simp] theorem run_seqRight [Monad m] [LawfulMonad m] (x : ReaderT ρ m α) (y : ReaderT ρ m β) (ctx : ρ) : (x *> y).run ctx = (x.run ctx *> y.run ctx) := by
|
||||
rw [seqRight_eq_bind (m := m)]; rfl
|
||||
|
||||
@[simp] theorem run_seqLeft [Monad m] (x : ReaderT ρ m α) (y : ReaderT ρ m β) (ctx : ρ)
|
||||
: (x <* y).run ctx = (x.run ctx <* y.run ctx) := rfl
|
||||
|
||||
instance [Monad m] [LawfulFunctor m] : LawfulFunctor (ReaderT ρ m) where
|
||||
id_map := by intros; apply ext; simp
|
||||
map_const := by intros; funext a b; apply ext; intros; simp [map_const]
|
||||
comp_map := by intros; apply ext; intros; simp [comp_map]
|
||||
|
||||
instance [Monad m] [LawfulApplicative m] : LawfulApplicative (ReaderT ρ m) where
|
||||
seqLeft_eq := by intros; apply ext; intros; simp [seqLeft_eq]
|
||||
seqRight_eq := by intros; apply ext; intros; simp [seqRight_eq]
|
||||
pure_seq := by intros; apply ext; intros; simp [pure_seq]
|
||||
map_pure := by intros; apply ext; intros; simp [map_pure]
|
||||
seq_pure := by intros; apply ext; intros; simp [seq_pure]
|
||||
seq_assoc := by intros; apply ext; intros; simp [seq_assoc]
|
||||
@[simp] theorem run_seqLeft [Monad m] [LawfulMonad m] (x : ReaderT ρ m α) (y : ReaderT ρ m β) (ctx : ρ) : (x <* y).run ctx = (x.run ctx <* y.run ctx) := by
|
||||
rw [seqLeft_eq_bind (m := m)]; rfl
|
||||
|
||||
instance [Monad m] [LawfulMonad m] : LawfulMonad (ReaderT ρ m) where
|
||||
bind_pure_comp := by intros; apply ext; intros; simp [LawfulMonad.bind_pure_comp]
|
||||
bind_map := by intros; apply ext; intros; simp [bind_map]
|
||||
id_map := by intros; apply ext; intros; simp
|
||||
map_const := by intros; rfl
|
||||
seqLeft_eq := by intros; apply ext; intros; simp; apply LawfulApplicative.seqLeft_eq
|
||||
seqRight_eq := by intros; apply ext; intros; simp; apply LawfulApplicative.seqRight_eq
|
||||
pure_seq := by intros; apply ext; intros; simp; apply LawfulApplicative.pure_seq
|
||||
bind_pure_comp := by intros; apply ext; intros; simp; apply LawfulMonad.bind_pure_comp
|
||||
bind_map := by intros; rfl
|
||||
pure_bind := by intros; apply ext; intros; simp
|
||||
bind_assoc := by intros; apply ext; intros; simp
|
||||
|
||||
end ReaderT
|
||||
|
||||
/-! # StateRefT -/
|
||||
/- StateRefT -/
|
||||
|
||||
instance [Monad m] [LawfulMonad m] : LawfulMonad (StateRefT' ω σ m) :=
|
||||
inferInstanceAs (LawfulMonad (ReaderT (ST.Ref ω σ) m))
|
||||
|
||||
/-! # StateT -/
|
||||
/- StateT -/
|
||||
|
||||
namespace StateT
|
||||
|
||||
|
||||
@@ -32,4 +32,4 @@ instance : MonadControl m (ReaderT ρ m) where
|
||||
instance ReaderT.tryFinally [MonadFinally m] [Monad m] : MonadFinally (ReaderT ρ m) where
|
||||
tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx)
|
||||
|
||||
@[reducible] def ReaderM (ρ : Type u) := ReaderT ρ Id
|
||||
@[reducible] def Reader (ρ : Type u) := ReaderT ρ Id
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Control.Lawful
|
||||
|
||||
/-!
|
||||
/-
|
||||
The State monad transformer using CPS style.
|
||||
-/
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import Init.Control.State
|
||||
|
||||
def StateRefT' (ω : Type) (σ : Type) (m : Type → Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
|
||||
|
||||
/-! Recall that `StateRefT` is a macro that infers `ω` from the `m`. -/
|
||||
/- Recall that `StateRefT` is a macro that infers `ω` from the `m`. -/
|
||||
|
||||
@[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
|
||||
|
||||
@@ -10,183 +10,72 @@ import Init.NotationExtra
|
||||
|
||||
namespace Lean.Parser.Tactic.Conv
|
||||
|
||||
/-- `conv` is the syntax category for a "conv tactic", where "conv" is short
|
||||
for conversion. A conv tactic is a program which receives a target, printed as
|
||||
`| a`, and is tasked with coming up with some term `b` and a proof of `a = b`.
|
||||
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 := withPosition((colGe conv ";"?)+)
|
||||
syntax convSeqBracketed := "{" (conv ";"?)* "}"
|
||||
syntax convSeqBracketed := "{" (conv ";"?)+ "}"
|
||||
-- Order is important: a missing `conv` proof should not be parsed as `{ <missing> }`,
|
||||
-- automatically closing goals
|
||||
syntax convSeq := convSeqBracketed <|> convSeq1Indented
|
||||
|
||||
/--
|
||||
`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 (name := conv) "conv " (" at " ident)? (" in " term)? " => " convSeq : tactic
|
||||
|
||||
/-- `skip` does nothing. -/
|
||||
syntax (name := skip) "skip" : conv
|
||||
|
||||
/-- Traverses into the left subterm of a binary operator.
|
||||
(In general, for an `n`-ary operator, it traverses into the second to last argument.) -/
|
||||
syntax (name := lhs) "lhs" : conv
|
||||
|
||||
/-- Traverses into the right subterm of a binary operator.
|
||||
(In general, for an `n`-ary operator, it traverses into the last argument.) -/
|
||||
syntax (name := rhs) "rhs" : conv
|
||||
|
||||
/-- Reduces the target to Weak Head Normal Form. This reduces definitions
|
||||
in "head position" until a constructor is exposed. For example, `List.map f [a, b, c]`
|
||||
weak head normalizes to `f a :: List.map f [b, c]`. -/
|
||||
syntax (name := whnf) "whnf" : conv
|
||||
|
||||
/-- Expand let-declarations and let-variables. -/
|
||||
syntax (name := zeta) "zeta" : conv
|
||||
|
||||
/-- 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
|
||||
subgoals for all the function arguments. For example, if the target is `f x y` then
|
||||
`congr` produces two subgoals, one for `x` and one for `y`. -/
|
||||
syntax (name := congr) "congr" : conv
|
||||
|
||||
/--
|
||||
* `arg i` traverses into the `i`'th argument of the target. For example if the
|
||||
target is `f a b c d` then `arg 1` traverses to `a` and `arg 3` traverses to `c`.
|
||||
* `arg @i` is the same as `arg i` but it counts all arguments instead of just the
|
||||
explicit arguments. -/
|
||||
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 " (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 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 " ident : conv
|
||||
|
||||
/-- `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 " ident : 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
|
||||
|
||||
/-- `simp [thm]` performs simplification using `thm` and marked `@[simp]` lemmas.
|
||||
See the `simp` tactic for more information. -/
|
||||
syntax (name := simp) "simp " (config)? (discharger)? (&"only ")? ("[" (simpStar <|> simpErase <|> simpLemma),* "]")? : conv
|
||||
|
||||
/-- `simp_match` simplifies match expressions. For example,
|
||||
```
|
||||
match [a, b] with
|
||||
| [] => 0
|
||||
| hd :: tl => hd
|
||||
```
|
||||
simplifies to `a`. -/
|
||||
syntax (name := simpMatch) "simp_match" : conv
|
||||
|
||||
syntax (name := simpMatch) "simp_match " : conv
|
||||
|
||||
/-- Execute the given tactic block without converting `conv` goal into a regular goal -/
|
||||
syntax (name := nestedTacticCore) "tactic'" " => " tacticSeq : conv
|
||||
|
||||
/-- 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
|
||||
|
||||
/-- `{ 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) "(" 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
|
||||
|
||||
/-- `· conv` focuses on the main conv goal and tries to solve it using `s` -/
|
||||
macro dot:("·" <|> ".") s:convSeq : conv => `({%$dot ($s) })
|
||||
macro dot:("·" <|> ".") s:convSeq : conv => `({%$dot ($s:convSeq) })
|
||||
macro "rw " c:(config)? s:rwRuleSeq : conv => `(rewrite $[$c:config]? $s:rwRuleSeq)
|
||||
macro "erw " s:rwRuleSeq : conv => `(rw (config := { transparency := Meta.TransparencyMode.default }) $s:rwRuleSeq)
|
||||
|
||||
/-- `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 => `(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 => `(rw (config := { transparency := .default }) $s)
|
||||
|
||||
/-- `args` traverses into all arguments. Synonym for `congr`. -/
|
||||
macro "args" : conv => `(congr)
|
||||
/-- `left` traverses into the left argument. Synonym for `lhs`. -/
|
||||
macro "left" : conv => `(lhs)
|
||||
/-- `right` traverses into the right argument. Synonym for `rhs`. -/
|
||||
macro "right" : conv => `(rhs)
|
||||
/-- `intro` traverses into binders. Synonym for `ext`. -/
|
||||
macro "intro " xs:(colGt ident)* : conv => `(conv| ext $xs*)
|
||||
syntax "intro " (colGt ident)* : conv
|
||||
macro_rules
|
||||
| `(conv| intro $[$xs:ident]*) => `(conv| ext $xs*)
|
||||
|
||||
syntax enterArg := ident <|> ("@"? num)
|
||||
|
||||
/-- `enter [arg, ...]` is a compact way to describe a path to a subterm.
|
||||
It is a shorthand for other conv tactics as follows:
|
||||
* `enter [i]` is equivalent to `arg i`.
|
||||
* `enter [@i]` is equivalent to `arg @i`.
|
||||
* `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 " "[" (colGt enterArg),+ "]": conv
|
||||
macro_rules
|
||||
| `(conv| enter [$i:num]) => `(conv| arg $i)
|
||||
| `(conv| enter [@$i]) => `(conv| arg @$i)
|
||||
| `(conv| enter [@$i:num]) => `(conv| arg @$i)
|
||||
| `(conv| enter [$id:ident]) => `(conv| ext $id)
|
||||
| `(conv| enter [$arg, $args,*]) => `(conv| (enter [$arg]; enter [$args,*]))
|
||||
| `(conv| enter [$arg:enterArg, $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 "skip" : conv => `(tactic => rfl)
|
||||
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 => `(tactic => apply $e)
|
||||
|
||||
/-- `first | conv | ...` runs each `conv` until one succeeds, or else fails. -/
|
||||
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| repeat $seq) => `(conv| first | ($seq); repeat $seq | skip)
|
||||
|
||||
end Lean.Parser.Tactic.Conv
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,5 +27,3 @@ import Init.Data.Format
|
||||
import Init.Data.Stream
|
||||
import Init.Data.Prod
|
||||
import Init.Data.AC
|
||||
import Init.Data.Queue
|
||||
import Init.Data.Channel
|
||||
|
||||
@@ -34,7 +34,7 @@ def isEmpty (a : Array α) : Bool :=
|
||||
def singleton (v : α) : Array α :=
|
||||
mkArray 1 v
|
||||
|
||||
/-- Low-level version of `fget` which is as fast as a C array read.
|
||||
/- Low-level version of `fget` which is as fast as a C array read.
|
||||
`Fin` values are represented as tag pointers in the Lean runtime. Thus,
|
||||
`fget` may be slightly slower than `uget`. -/
|
||||
@[extern "lean_array_uget"]
|
||||
@@ -64,7 +64,7 @@ abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size =
|
||||
@[simp] theorem size_push (a : Array α) (v : α) : (push a v).size = a.size + 1 :=
|
||||
List.length_concat ..
|
||||
|
||||
/-- Low-level version of `fset` which is as fast as a C array fset.
|
||||
/- Low-level version of `fset` which is as fast as a C array fset.
|
||||
`Fin` values are represented as tag pointers in the Lean runtime. Thus,
|
||||
`fset` may be slightly slower than `uset`. -/
|
||||
@[extern "lean_array_uset"]
|
||||
@@ -141,7 +141,7 @@ def modify (a : Array α) (i : Nat) (f : α → α) : Array α :=
|
||||
def modifyOp (self : Array α) (idx : Nat) (f : α → α) : Array α :=
|
||||
self.modify idx f
|
||||
|
||||
/--
|
||||
/-
|
||||
We claim this unsafe implementation is correct because an array cannot have more than `usizeSz` elements in our runtime.
|
||||
|
||||
This kind of low level trick can be removed with a little bit of compiler support. For example, if the compiler simplifies `as.size < usizeSz` to true. -/
|
||||
@@ -157,7 +157,7 @@ def modifyOp (self : Array α) (idx : Nat) (f : α → α) : Array α :=
|
||||
pure b
|
||||
loop 0 b
|
||||
|
||||
/-- Reference implementation for `forIn` -/
|
||||
/- Reference implementation for `forIn` -/
|
||||
@[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
|
||||
@@ -175,7 +175,7 @@ protected def forIn {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m
|
||||
instance : ForIn m (Array α) α where
|
||||
forIn := Array.forIn
|
||||
|
||||
/-- See comment at `forInUnsafe` -/
|
||||
/- See comment at forInUnsafe -/
|
||||
@[inline]
|
||||
unsafe def foldlMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β → α → m β) (init : β) (as : Array α) (start := 0) (stop := as.size) : m β :=
|
||||
let rec @[specialize] fold (i : USize) (stop : USize) (b : β) : m β := do
|
||||
@@ -191,7 +191,7 @@ unsafe def foldlMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Mon
|
||||
else
|
||||
pure init
|
||||
|
||||
/-- Reference implementation for `foldlM` -/
|
||||
/- Reference implementation for `foldlM` -/
|
||||
@[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) :=
|
||||
@@ -210,7 +210,7 @@ def foldlM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : β
|
||||
else
|
||||
fold as.size (Nat.le_refl _)
|
||||
|
||||
/-- See comment at `forInUnsafe` -/
|
||||
/- See comment at forInUnsafe -/
|
||||
@[inline]
|
||||
unsafe def foldrMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → β → m β) (init : β) (as : Array α) (start := as.size) (stop := 0) : m β :=
|
||||
let rec @[specialize] fold (i : USize) (stop : USize) (b : β) : m β := do
|
||||
@@ -228,7 +228,7 @@ unsafe def foldrMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Mon
|
||||
else
|
||||
pure init
|
||||
|
||||
/-- Reference implementation for `foldrM` -/
|
||||
/- Reference implementation for `foldrM` -/
|
||||
@[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
|
||||
@@ -249,7 +249,7 @@ def foldrM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
|
||||
else
|
||||
pure init
|
||||
|
||||
/-- See comment at `forInUnsafe` -/
|
||||
/- See comment at forInUnsafe -/
|
||||
@[inline]
|
||||
unsafe def mapMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m β) (as : Array α) : m (Array β) :=
|
||||
let sz := USize.ofNat as.size
|
||||
@@ -266,7 +266,7 @@ unsafe def mapMUnsafe {α : Type u} {β : Type v} {m : Type v → Type w} [Monad
|
||||
pure (unsafeCast r)
|
||||
unsafeCast <| map 0 (unsafeCast as)
|
||||
|
||||
/-- Reference implementation for `mapM` -/
|
||||
/- Reference implementation for `mapM` -/
|
||||
@[implementedBy mapMUnsafe]
|
||||
def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α → m β) (as : Array α) : m (Array β) :=
|
||||
as.foldlM (fun bs a => do let b ← f a; pure (bs.push b)) (mkEmpty as.size)
|
||||
|
||||
@@ -36,39 +36,3 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
|
||||
apply propext; apply Iff.intro
|
||||
· intro h; simp [toArray] at h; have := of_toArrayAux_eq_toArrayAux h rfl; exact this.1
|
||||
· intro h; rw [h]
|
||||
|
||||
def Array.mapM' [Monad m] (f : α → m β) (as : Array α) : m { bs : Array β // bs.size = as.size } :=
|
||||
go 0 ⟨mkEmpty as.size, rfl⟩ (by simp_arith)
|
||||
where
|
||||
go (i : Nat) (acc : { bs : Array β // bs.size = i }) (hle : i ≤ as.size) : m { bs : Array β // bs.size = as.size } := do
|
||||
if h : i = as.size then
|
||||
return h ▸ acc
|
||||
else
|
||||
have hlt : i < as.size := Nat.lt_of_le_of_ne hle h
|
||||
let b ← f as[i]
|
||||
go (i+1) ⟨acc.val.push b, by simp [acc.property]⟩ hlt
|
||||
termination_by go i _ _ => as.size - i
|
||||
|
||||
@[inline] private unsafe def mapMonoMImp [Monad m] (as : Array α) (f : α → m α) : m (Array α) :=
|
||||
go 0 as
|
||||
where
|
||||
@[specialize] go (i : Nat) (as : Array α) : m (Array α) := do
|
||||
if h : i < as.size then
|
||||
let a := as[i]
|
||||
let b ← f a
|
||||
if ptrEq a b then
|
||||
go (i+1) as
|
||||
else
|
||||
go (i+1) (as.set ⟨i, h⟩ b)
|
||||
else
|
||||
return as
|
||||
|
||||
/--
|
||||
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`.
|
||||
-/
|
||||
@[implementedBy mapMonoMImp] def Array.mapMonoM [Monad m] (as : Array α) (f : α → m α) : m (Array α) :=
|
||||
as.mapM f
|
||||
|
||||
@[inline] def Array.mapMono (as : Array α) (f : α → α) : Array α :=
|
||||
Id.run <| as.mapMonoM f
|
||||
|
||||
@@ -47,9 +47,6 @@ termination_by aux j _ => as.size - j
|
||||
apply Nat.lt_trans (List.sizeOf_get ..)
|
||||
simp_arith
|
||||
|
||||
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that
|
||||
`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 =>
|
||||
`(first
|
||||
| apply sizeOf_get
|
||||
|
||||
@@ -102,7 +102,7 @@ partial def toList (bs : ByteArray) : List UInt8 :=
|
||||
none
|
||||
loop start
|
||||
|
||||
/--
|
||||
/-
|
||||
We claim this unsafe implementation is correct because an array cannot have more than `usizeSz` elements in our runtime.
|
||||
This is similar to the `Array` version.
|
||||
|
||||
@@ -120,7 +120,7 @@ partial def toList (bs : ByteArray) : List UInt8 :=
|
||||
pure b
|
||||
loop 0 b
|
||||
|
||||
/-- Reference implementation for `forIn` -/
|
||||
/- Reference implementation for `forIn` -/
|
||||
@[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
|
||||
@@ -138,8 +138,10 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : ByteAr
|
||||
instance : ForIn m ByteArray UInt8 where
|
||||
forIn := ByteArray.forIn
|
||||
|
||||
/-- See comment at `forInUnsafe` -/
|
||||
-- TODO: avoid code duplication.
|
||||
/-
|
||||
See comment at forInUnsafe
|
||||
TODO: avoid code duplication.
|
||||
-/
|
||||
@[inline]
|
||||
unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β → UInt8 → m β) (init : β) (as : ByteArray) (start := 0) (stop := as.size) : m β :=
|
||||
let rec @[specialize] fold (i : USize) (stop : USize) (b : β) : m β := do
|
||||
@@ -155,7 +157,7 @@ unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β
|
||||
else
|
||||
pure init
|
||||
|
||||
/-- Reference implementation for `foldlM` -/
|
||||
/- Reference implementation for `foldlM` -/
|
||||
@[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) :=
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Gabriel Ebner
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Queue
|
||||
import Init.System.Promise
|
||||
import Init.System.Mutex
|
||||
|
||||
namespace IO
|
||||
|
||||
/--
|
||||
Internal state of an `Channel`.
|
||||
|
||||
We maintain the invariant that at all times either `consumers` or `values` is empty.
|
||||
-/
|
||||
structure Channel.State (α : Type) where
|
||||
values : Std.Queue α := ∅
|
||||
consumers : Std.Queue (Promise (Option α)) := ∅
|
||||
closed := false
|
||||
deriving Inhabited
|
||||
|
||||
/--
|
||||
FIFO channel with unbounded buffer, where `recv?` returns a `Task`.
|
||||
|
||||
A channel can be closed. Once it is closed, all `send`s are ignored, and
|
||||
`recv?` returns `none` once the queue is empty.
|
||||
-/
|
||||
def Channel (α : Type) : Type := Mutex (Channel.State α)
|
||||
|
||||
instance : Nonempty (Channel α) :=
|
||||
inferInstanceAs (Nonempty (Mutex _))
|
||||
|
||||
/-- Creates a new `Channel`. -/
|
||||
def Channel.new : BaseIO (Channel α) :=
|
||||
Mutex.new {}
|
||||
|
||||
/--
|
||||
Sends a message on an `Channel`.
|
||||
|
||||
This function does not block.
|
||||
-/
|
||||
def Channel.send (v : α) (ch : Channel α) : BaseIO Unit :=
|
||||
ch.atomically do
|
||||
let st ← get
|
||||
if st.closed then return
|
||||
if let some (consumer, consumers) := st.consumers.dequeue? then
|
||||
consumer.resolve (some v)
|
||||
set { st with consumers }
|
||||
else
|
||||
set { st with values := st.values.enqueue v }
|
||||
|
||||
/--
|
||||
Closes an `Channel`.
|
||||
-/
|
||||
def Channel.close (ch : Channel α) : BaseIO Unit :=
|
||||
ch.atomically do
|
||||
let st ← get
|
||||
for consumer in st.consumers.toArray do consumer.resolve none
|
||||
set { st with closed := true, consumers := ∅ }
|
||||
|
||||
/--
|
||||
Receives a message, without blocking.
|
||||
The returned task waits for the message.
|
||||
Every message is only received once.
|
||||
|
||||
Returns `none` if the channel is closed and the queue is empty.
|
||||
-/
|
||||
def Channel.recv? (ch : Channel α) : BaseIO (Task (Option α)) :=
|
||||
ch.atomically do
|
||||
let st ← get
|
||||
if let some (a, values) := st.values.dequeue? then
|
||||
set { st with values }
|
||||
return .pure a
|
||||
else if !st.closed then
|
||||
let promise ← Promise.new
|
||||
set { st with consumers := st.consumers.enqueue promise }
|
||||
return promise.result
|
||||
else
|
||||
return .pure none
|
||||
|
||||
/--
|
||||
`ch.forAsync f` calls `f` for every messages received on `ch`.
|
||||
|
||||
Note that if this function is called twice, each `forAsync` only gets half the messages.
|
||||
-/
|
||||
partial def Channel.forAsync (f : α → BaseIO Unit) (ch : Channel α)
|
||||
(prio : Task.Priority := .default) : BaseIO (Task Unit) := do
|
||||
BaseIO.bindTask (prio := prio) (← ch.recv?) fun
|
||||
| none => return .pure ()
|
||||
| some v => do f v; ch.forAsync f prio
|
||||
|
||||
/--
|
||||
Receives all currently queued messages from the channel.
|
||||
|
||||
Those messages are dequeued and will not be returned by `recv?`.
|
||||
-/
|
||||
def Channel.recvAllCurrent (ch : Channel α) : BaseIO (Array α) :=
|
||||
ch.atomically do
|
||||
modifyGet fun st => (st.values.toArray, { st with values := ∅ })
|
||||
|
||||
/-- Type tag for synchronous (blocking) operations on a `Channel`. -/
|
||||
def Channel.Sync := Channel
|
||||
|
||||
/--
|
||||
Accesses synchronous (blocking) version of channel operations.
|
||||
|
||||
For example, `ch.sync.recv?` blocks until the next message,
|
||||
and `for msg in ch.sync do ...` iterates synchronously over the channel.
|
||||
These functions should only be used in dedicated threads.
|
||||
-/
|
||||
def Channel.sync (ch : Channel α) : Channel.Sync α := ch
|
||||
|
||||
/--
|
||||
Synchronously receives a message from the channel.
|
||||
|
||||
Every message is only received once.
|
||||
Returns `none` if the channel is closed and the queue is empty.
|
||||
-/
|
||||
def Channel.Sync.recv? (ch : Channel.Sync α) : BaseIO (Option α) := do
|
||||
IO.wait (← Channel.recv? ch)
|
||||
|
||||
private partial def Channel.Sync.forIn [Monad m] [MonadLiftT BaseIO m]
|
||||
(ch : Channel.Sync α) (f : α → β → m (ForInStep β)) : β → m β := fun b => do
|
||||
match ← ch.recv? with
|
||||
| some a =>
|
||||
match ← f a b with
|
||||
| .done b => pure b
|
||||
| .yield b => ch.forIn f b
|
||||
| none => pure b
|
||||
|
||||
/-- `for msg in ch.sync do ...` receives all messages in the channel until it is closed. -/
|
||||
instance [MonadLiftT BaseIO m] : ForIn m (Channel.Sync α) α where
|
||||
forIn ch b f := ch.forIn f b
|
||||
@@ -6,10 +6,6 @@ Author: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Data.UInt
|
||||
|
||||
/-- Determines if the given integer is a valid [Unicode scalar value](https://www.unicode.org/glossary/#unicode_scalar_value).
|
||||
|
||||
Note that values in `[0xd800, 0xdfff]` are reserved for [UTF-16 surrogate pairs](https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Surrogates).
|
||||
-/
|
||||
@[inline, reducible] def isValidChar (n : UInt32) : Prop :=
|
||||
n < 0xd800 ∨ (0xdfff < n ∧ n < 0x110000)
|
||||
|
||||
@@ -27,7 +23,6 @@ instance (a b : Char) : Decidable (a < b) :=
|
||||
instance (a b : Char) : Decidable (a ≤ b) :=
|
||||
UInt32.decLe _ _
|
||||
|
||||
/-- Determines if the given nat is a valid [Unicode scalar value](https://www.unicode.org/glossary/#unicode_scalar_value).-/
|
||||
abbrev isValidCharNat (n : Nat) : Prop :=
|
||||
n < 0xd800 ∨ (0xdfff < n ∧ n < 0x110000)
|
||||
|
||||
@@ -48,49 +43,34 @@ theorem isValidChar_of_isValidChar_Nat (n : Nat) (h : isValidCharNat n) : isVali
|
||||
theorem isValidChar_zero : isValidChar 0 :=
|
||||
Or.inl (by decide)
|
||||
|
||||
/-- Underlying unicode code point as a `Nat`. -/
|
||||
@[inline] def toNat (c : Char) : Nat :=
|
||||
c.val.toNat
|
||||
|
||||
instance : Inhabited Char where
|
||||
default := 'A'
|
||||
|
||||
/-- Is the character a space (U+0020) a tab (U+0009), a carriage return (U+000D) or a newline (U+000A)? -/
|
||||
def isWhitespace (c : Char) : Bool :=
|
||||
c = ' ' || c = '\t' || c = '\r' || c = '\n'
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZ`? -/
|
||||
def isUpper (c : Char) : Bool :=
|
||||
c.val ≥ 65 && c.val ≤ 90
|
||||
|
||||
/-- Is the character in `abcdefghijklmnopqrstuvwxyz`? -/
|
||||
def isLower (c : Char) : Bool :=
|
||||
c.val ≥ 97 && c.val ≤ 122
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`? -/
|
||||
def isAlpha (c : Char) : Bool :=
|
||||
c.isUpper || c.isLower
|
||||
|
||||
/-- Is the character in `0123456789`? -/
|
||||
def isDigit (c : Char) : Bool :=
|
||||
c.val ≥ 48 && c.val ≤ 57
|
||||
|
||||
/-- Is the character in `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`? -/
|
||||
def isAlphanum (c : Char) : Bool :=
|
||||
c.isAlpha || c.isDigit
|
||||
|
||||
/-- Convert an upper case character to its lower case character.
|
||||
|
||||
Only works on basic latin letters.
|
||||
-/
|
||||
def toLower (c : Char) : Char :=
|
||||
let n := toNat c;
|
||||
if n >= 65 ∧ n <= 90 then ofNat (n + 32) else c
|
||||
|
||||
/-- Convert a lower case character to its upper case character.
|
||||
|
||||
Only works on basic latin letters.
|
||||
-/
|
||||
def toUpper (c : Char) : Char :=
|
||||
let n := toNat c;
|
||||
if n >= 97 ∧ n <= 122 then ofNat (n - 32) else c
|
||||
|
||||
@@ -44,7 +44,7 @@ protected def mul : Fin n → Fin n → Fin n
|
||||
protected def sub : Fin n → Fin n → Fin n
|
||||
| ⟨a, h⟩, ⟨b, _⟩ => ⟨(a + (n - b)) % n, mlt h⟩
|
||||
|
||||
/-!
|
||||
/-
|
||||
Remark: mod/div/modn/land/lor can be defined without using (% n), but
|
||||
we are trying to minimize the number of Nat theorems
|
||||
needed to boostrap Lean.
|
||||
@@ -117,7 +117,7 @@ theorem val_lt_of_le (i : Fin b) (h : b ≤ n) : i.val < n :=
|
||||
|
||||
end Fin
|
||||
|
||||
instance [GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i where
|
||||
instance [GetElem Cont Nat Elem Dom] : GetElem Cont (Fin n) Elem fun xs i => Dom xs i where
|
||||
getElem xs i h := getElem xs i.1 h
|
||||
|
||||
macro_rules
|
||||
|
||||
@@ -31,34 +31,33 @@ structure Float where
|
||||
|
||||
instance : Inhabited Float := ⟨{ val := floatSpec.val }⟩
|
||||
|
||||
@[extern "lean_float_add"] opaque Float.add : Float → Float → Float
|
||||
@[extern "lean_float_sub"] opaque Float.sub : Float → Float → Float
|
||||
@[extern "lean_float_mul"] opaque Float.mul : Float → Float → Float
|
||||
@[extern "lean_float_div"] opaque Float.div : Float → Float → Float
|
||||
@[extern "lean_float_negate"] opaque Float.neg : Float → Float
|
||||
@[extern "lean_float_add"] opaque Float.add : Float → Float → Float
|
||||
@[extern "lean_float_sub"] opaque Float.sub : Float → Float → Float
|
||||
@[extern "lean_float_mul"] opaque Float.mul : Float → Float → Float
|
||||
@[extern "lean_float_div"] opaque Float.div : Float → Float → Float
|
||||
@[extern "lean_float_negate"] opaque Float.neg : Float → Float
|
||||
|
||||
set_option bootstrap.genMatcherCode false
|
||||
def Float.lt : Float → Float → Prop := fun a b =>
|
||||
def Float.lt : Float → Float → Prop := fun a b =>
|
||||
match a, b with
|
||||
| ⟨a⟩, ⟨b⟩ => floatSpec.lt a b
|
||||
|
||||
def Float.le : Float → Float → Prop := fun a b =>
|
||||
def Float.le : Float → Float → Prop := fun a b =>
|
||||
floatSpec.le a.val b.val
|
||||
|
||||
instance : Add Float := ⟨Float.add⟩
|
||||
instance : Sub Float := ⟨Float.sub⟩
|
||||
instance : Mul Float := ⟨Float.mul⟩
|
||||
instance : Div Float := ⟨Float.div⟩
|
||||
instance : Neg Float := ⟨Float.neg⟩
|
||||
instance : LT Float := ⟨Float.lt⟩
|
||||
instance : LE Float := ⟨Float.le⟩
|
||||
instance : Add Float := ⟨Float.add⟩
|
||||
instance : Sub Float := ⟨Float.sub⟩
|
||||
instance : Mul Float := ⟨Float.mul⟩
|
||||
instance : Div Float := ⟨Float.div⟩
|
||||
instance : Neg Float := ⟨Float.neg⟩
|
||||
instance : LT Float := ⟨Float.lt⟩
|
||||
instance : LE Float := ⟨Float.le⟩
|
||||
|
||||
/-- Note: this is not reflexive since `NaN != NaN`.-/
|
||||
@[extern "lean_float_beq"] opaque Float.beq (a b : Float) : Bool
|
||||
|
||||
instance : BEq Float := ⟨Float.beq⟩
|
||||
|
||||
@[extern "lean_float_decLt"] opaque Float.decLt (a b : Float) : Decidable (a < b) :=
|
||||
@[extern "lean_float_decLt"] opaque Float.decLt (a b : Float) : Decidable (a < b) :=
|
||||
match a, b with
|
||||
| ⟨a⟩, ⟨b⟩ => floatSpec.decLt a b
|
||||
|
||||
@@ -71,31 +70,12 @@ instance floatDecLe (a b : Float) : Decidable (a ≤ b) := Float.decLe a b
|
||||
|
||||
@[extern "lean_float_to_string"] opaque Float.toString : Float → String
|
||||
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt8, returns 0. -/
|
||||
@[extern "lean_float_to_uint8"] opaque Float.toUInt8 : Float → UInt8
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt16, returns 0. -/
|
||||
@[extern "lean_float_to_uint16"] opaque Float.toUInt16 : Float → UInt16
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt32, returns 0. -/
|
||||
@[extern "lean_float_to_uint32"] opaque Float.toUInt32 : Float → UInt32
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for UInt64, returns 0. -/
|
||||
@[extern "lean_float_to_uint64"] opaque Float.toUInt64 : Float → UInt64
|
||||
/-- If the given float is positive, truncates the value to the nearest positive integer.
|
||||
If negative or larger than the maximum value for USize, returns 0. -/
|
||||
@[extern "lean_float_to_usize"] opaque Float.toUSize : Float → USize
|
||||
|
||||
@[extern "lean_float_isnan"] opaque Float.isNaN : Float → Bool
|
||||
@[extern "lean_float_isfinite"] opaque Float.isFinite : Float → Bool
|
||||
@[extern "lean_float_isinf"] opaque Float.isInf : Float → Bool
|
||||
/-- Splits the given float `x` into a significand/exponent pair `(s, i)`
|
||||
such that `x = s * 2^i` where `s ∈ (-1;-0.5] ∪ [0.5; 1)`.
|
||||
Returns an undefined value if `x` is not finite.
|
||||
-/
|
||||
@[extern "lean_float_frexp"] opaque Float.frExp : Float → Float × Int
|
||||
|
||||
instance : ToString Float where
|
||||
toString := Float.toString
|
||||
|
||||
@@ -130,7 +110,6 @@ instance : ReprAtom Float := ⟨⟩
|
||||
@[extern "ceil"] opaque Float.ceil : Float → Float
|
||||
@[extern "floor"] opaque Float.floor : Float → Float
|
||||
@[extern "round"] opaque Float.round : Float → Float
|
||||
@[extern "fabs"] opaque Float.abs : Float → Float
|
||||
|
||||
instance : Pow Float Float := ⟨Float.pow⟩
|
||||
|
||||
|
||||
@@ -84,11 +84,12 @@ partial def toList (ds : FloatArray) : List Float :=
|
||||
r.reverse
|
||||
loop 0 []
|
||||
|
||||
/--
|
||||
/-
|
||||
We claim this unsafe implementation is correct because an array cannot have more than `usizeSz` elements in our runtime.
|
||||
This is similar to the `Array` version.
|
||||
|
||||
TODO: avoid code duplication in the future after we improve the compiler.
|
||||
-/
|
||||
-- TODO: avoid code duplication in the future after we improve the compiler.
|
||||
@[inline] unsafe def forInUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (as : FloatArray) (b : β) (f : Float → β → m (ForInStep β)) : m β :=
|
||||
let sz := USize.ofNat as.size
|
||||
let rec @[specialize] loop (i : USize) (b : β) : m β := do
|
||||
@@ -101,7 +102,7 @@ partial def toList (ds : FloatArray) : List Float :=
|
||||
pure b
|
||||
loop 0 b
|
||||
|
||||
/-- Reference implementation for `forIn` -/
|
||||
/- Reference implementation for `forIn` -/
|
||||
@[implementedBy FloatArray.forInUnsafe]
|
||||
protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : FloatArray) (b : β) (f : Float → β → m (ForInStep β)) : m β :=
|
||||
let rec loop (i : Nat) (h : i ≤ as.size) (b : β) : m β := do
|
||||
@@ -119,8 +120,10 @@ protected def forIn {β : Type v} {m : Type v → Type w} [Monad m] (as : FloatA
|
||||
instance : ForIn m FloatArray Float where
|
||||
forIn := FloatArray.forIn
|
||||
|
||||
/-- See comment at `forInUnsafe` -/
|
||||
-- TODO: avoid code duplication.
|
||||
/-
|
||||
See comment at forInUnsafe
|
||||
TODO: avoid code duplication.
|
||||
-/
|
||||
@[inline]
|
||||
unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β → Float → m β) (init : β) (as : FloatArray) (start := 0) (stop := as.size) : m β :=
|
||||
let rec @[specialize] fold (i : USize) (stop : USize) (b : β) : m β := do
|
||||
@@ -136,7 +139,7 @@ unsafe def foldlMUnsafe {β : Type v} {m : Type v → Type w} [Monad m] (f : β
|
||||
else
|
||||
pure init
|
||||
|
||||
/-- Reference implementation for `foldlM` -/
|
||||
/- Reference implementation for `foldlM` -/
|
||||
@[implementedBy foldlMUnsafe]
|
||||
def foldlM {β : Type v} {m : Type v → Type w} [Monad m] (f : β → Float → m β) (init : β) (as : FloatArray) (start := 0) (stop := as.size) : m β :=
|
||||
let fold (stop : Nat) (h : stop ≤ as.size) :=
|
||||
|
||||
@@ -10,63 +10,25 @@ import Init.Data.String.Basic
|
||||
|
||||
namespace Std
|
||||
|
||||
/-- Determines how groups should have linebreaks inserted when the
|
||||
text would overfill its remaining space.
|
||||
|
||||
- `allOrNone` will make a linebreak on every `Format.line` in the group or none of them.
|
||||
```
|
||||
[1,
|
||||
2,
|
||||
3]
|
||||
```
|
||||
- `fill` will only make linebreaks on as few `Format.line`s as possible:
|
||||
```
|
||||
[1, 2,
|
||||
3]
|
||||
```
|
||||
-/
|
||||
inductive Format.FlattenBehavior where
|
||||
| allOrNone
|
||||
| fill
|
||||
deriving Inhabited, BEq
|
||||
|
||||
open Format in
|
||||
/-- A string with pretty-printing information for rendering in a column-width-aware way.
|
||||
|
||||
The pretty-printing algorithm is based on Wadler's paper
|
||||
[_A Prettier Printer_](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf). -/
|
||||
inductive Format where
|
||||
| /-- The empty format. -/
|
||||
nil : Format
|
||||
| /-- A position where a newline may be inserted
|
||||
if the current group does not fit within the allotted column width. -/
|
||||
line : Format
|
||||
| /-- A node containing a plain string. -/
|
||||
text : String → Format
|
||||
| /-- `nest n f` tells the formatter that `f` is nested inside something with length `n`
|
||||
so that it is pretty-printed with the correct indentation on a line break.
|
||||
For example, we can define a formatter for list `l : List Format` as:
|
||||
|
||||
```
|
||||
let f := join <| l.intersperse <| ", " ++ Format.line
|
||||
group (nest 1 <| "[" ++ f ++ "]")
|
||||
```
|
||||
|
||||
This will be written all on one line, but if the text is too large,
|
||||
the formatter will put in linebreaks after the commas and indent later lines by 1.
|
||||
-/
|
||||
nest (indent : Int) : Format → Format
|
||||
| /-- Concatenation of two Formats. -/
|
||||
append : Format → Format → Format
|
||||
| /-- Creates a new flattening group for the given inner format. -/
|
||||
group : Format → (behavior : FlattenBehavior := FlattenBehavior.allOrNone) → Format
|
||||
| /-- Used for associating auxiliary information (e.g. `Expr`s) with `Format` objects. -/
|
||||
tag : Nat → Format → Format
|
||||
| nil : Format
|
||||
| line : Format
|
||||
| text : String → Format
|
||||
| nest (indent : Int) : Format → Format
|
||||
| append : Format → Format → Format
|
||||
| group : Format → (behavior : FlattenBehavior := FlattenBehavior.allOrNone) → Format
|
||||
/- Used for associating auxiliary information (e.g. `Expr`s) with `Format` objects. -/
|
||||
| tag : Nat → Format → Format
|
||||
deriving Inhabited
|
||||
|
||||
namespace Format
|
||||
|
||||
/-- Check whether the given format contains no characters. -/
|
||||
def isEmpty : Format → Bool
|
||||
| nil => true
|
||||
| line => false
|
||||
@@ -76,7 +38,6 @@ def isEmpty : Format → Bool
|
||||
| group f _ => f.isEmpty
|
||||
| tag _ f => f.isEmpty
|
||||
|
||||
/-- Alias for a group with `FlattenBehavior` set to `fill`. -/
|
||||
def fill (f : Format) : Format :=
|
||||
group f (behavior := FlattenBehavior.fill)
|
||||
|
||||
@@ -221,43 +182,31 @@ private partial def be (w : Nat) [Monad m] [MonadPrettyFormat m] : List WorkGrou
|
||||
else
|
||||
pushGroup flb [{ i with f }] (gs' is) w >>= be w
|
||||
|
||||
/-- Render the given `f : Format` with a line width of `w`.
|
||||
`indent` is the starting amount to indent each line by. -/
|
||||
def prettyM (f : Format) (w : Nat) (indent : Nat := 0) [Monad m] [MonadPrettyFormat m] : m Unit :=
|
||||
be w [{ flb := FlattenBehavior.allOrNone, flatten := false, items := [{ f := f, indent, activeTags := 0 }]}]
|
||||
|
||||
/-- Create a format `l ++ f ++ r` with a flatten group.
|
||||
FlattenBehaviour is `allOrNone`; for `fill` use `bracketFill`. -/
|
||||
@[inline] def bracket (l : String) (f : Format) (r : String) : Format :=
|
||||
group (nest l.length $ l ++ f ++ r)
|
||||
|
||||
/-- Creates the format `"(" ++ f ++ ")"` with a flattening group.-/
|
||||
@[inline] def paren (f : Format) : Format :=
|
||||
bracket "(" f ")"
|
||||
|
||||
/-- Creates the format `"[" ++ f ++ "]"` with a flattening group.-/
|
||||
@[inline] def sbracket (f : Format) : Format :=
|
||||
bracket "[" f "]"
|
||||
|
||||
/-- Same as `bracket` except uses the `fill` flattening behaviour. -/
|
||||
@[inline] def bracketFill (l : String) (f : Format) (r : String) : Format :=
|
||||
fill (nest l.length $ l ++ f ++ r)
|
||||
|
||||
/-- Default indentation. -/
|
||||
def defIndent := 2
|
||||
def defUnicode := true
|
||||
/-- Default width of the targeted output pane. -/
|
||||
def defWidth := 120
|
||||
|
||||
/-- Nest with the default indentation amount.-/
|
||||
def nestD (f : Format) : Format :=
|
||||
nest defIndent f
|
||||
|
||||
/-- Insert a newline and then `f`, all nested by the default indent amount. -/
|
||||
def indentD (f : Format) : Format :=
|
||||
nestD (Format.line ++ f)
|
||||
|
||||
/-- State for formatting a pretty string. -/
|
||||
private structure State where
|
||||
out : String := ""
|
||||
column : Nat := 0
|
||||
@@ -278,8 +227,6 @@ def pretty (f : Format) (w : Nat := defWidth) : String :=
|
||||
|
||||
end Format
|
||||
|
||||
/-- Class for converting a given type α to a `Format` object for pretty-printing.
|
||||
See also `Repr`, which also outputs a `Format` object. -/
|
||||
class ToFormat (α : Type u) where
|
||||
format : α → Format
|
||||
|
||||
@@ -292,18 +239,15 @@ instance : ToFormat Format where
|
||||
instance : ToFormat String where
|
||||
format s := Format.text s
|
||||
|
||||
/-- Intersperse the given list (each item printed with `format`) with the given `sep` format. -/
|
||||
def Format.joinSep {α : Type u} [ToFormat α] : List α → Format → Format
|
||||
| [], _ => nil
|
||||
| [a], _ => format a
|
||||
| a::as, sep => format a ++ sep ++ joinSep as sep
|
||||
|
||||
/-- Format each item in `items` and prepend prefix `pre`. -/
|
||||
def Format.prefixJoin {α : Type u} [ToFormat α] (pre : Format) : List α → Format
|
||||
| [] => nil
|
||||
| a::as => pre ++ format a ++ prefixJoin pre as
|
||||
|
||||
/-- Format each item in `items` and append `suffix`. -/
|
||||
def Format.joinSuffix {α : Type u} [ToFormat α] : List α → Format → Format
|
||||
| [], _ => nil
|
||||
| a::as, suffix => format a ++ suffix ++ joinSuffix as suffix
|
||||
|
||||
@@ -38,9 +38,6 @@ partial def formatStxAux (maxDepth : Option Nat) (showInfo : Bool) : Nat → Syn
|
||||
if args.size > 0 && depth > maxDepth.getD depth then [".."] else args.toList.map (formatStxAux maxDepth showInfo depth);
|
||||
paren $ joinSep (header :: body) line
|
||||
|
||||
/-- Pretty print the given syntax `stx` as a `Format`.
|
||||
Nodes deeper than `maxDepth` are omitted.
|
||||
Setting the `showInfo` flag will also print the `SourceInfo` for each node. -/
|
||||
def formatStx (stx : Syntax) (maxDepth : Option Nat := none) (showInfo := false) : Format :=
|
||||
formatStxAux maxDepth showInfo 0 stx
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import Init.Data.Nat.Div
|
||||
import Init.Data.List.Basic
|
||||
open Nat
|
||||
|
||||
/-! # the Type, coercions, and notation -/
|
||||
/- the Type, coercions, and notation -/
|
||||
|
||||
inductive Int : Type where
|
||||
| ofNat : Nat → Int
|
||||
@@ -62,7 +62,7 @@ protected def mul (m n : @& Int) : Int :=
|
||||
| negSucc m, ofNat n => negOfNat (succ m * n)
|
||||
| negSucc m, negSucc n => ofNat (succ m * succ n)
|
||||
|
||||
/--
|
||||
/-
|
||||
The `Neg Int` default instance must have priority higher than `low` since
|
||||
the default instance `OfNat Nat n` has `low` priority.
|
||||
```
|
||||
@@ -134,6 +134,9 @@ def natAbs (m : @& Int) : Nat :=
|
||||
| ofNat m => m
|
||||
| negSucc m => m.succ
|
||||
|
||||
instance : OfNat Int n where
|
||||
ofNat := Int.ofNat n
|
||||
|
||||
@[extern "lean_int_div"]
|
||||
def div : (@& Int) → (@& Int) → Int
|
||||
| ofNat m, ofNat n => ofNat (m / n)
|
||||
|
||||
@@ -143,7 +143,7 @@ theorem reverseAux_eq_append (as bs : List α) : reverseAux as bs = reverseAux a
|
||||
| nil => simp
|
||||
| cons a as ih => simp [ih]; rw [append_assoc]
|
||||
|
||||
theorem mapTRAux_eq (f : α → β) (as : List α) (bs : List β) : mapTRAux f as bs = bs.reverse ++ map f as := by
|
||||
theorem mapTRAux_eq (f : α → β) (as : List α) (bs : List β) : mapTRAux f as bs = bs.reverse ++ map f as := by
|
||||
induction as generalizing bs with
|
||||
| nil => simp [mapTRAux, map]
|
||||
| cons a as ih =>
|
||||
@@ -151,9 +151,9 @@ theorem mapTRAux_eq (f : α → β) (as : List α) (bs : List β) : mapTRAux f a
|
||||
rw [ih (f a :: bs), reverse_cons, append_assoc]
|
||||
rfl
|
||||
|
||||
@[csimp] theorem map_eq_mapTR : @map = @mapTR :=
|
||||
funext fun α => funext fun β => funext fun f => funext fun as => by
|
||||
simp [mapTR, mapTRAux_eq]
|
||||
@[csimp] theorem map_eq_mapTR : @map = @mapTR := by
|
||||
apply funext; intro α; apply funext; intro β; apply funext; intro f; apply funext; intro as
|
||||
simp [mapTR, mapTRAux_eq]
|
||||
|
||||
@[specialize] def map₂ (f : α → β → γ) : List α → List β → List γ
|
||||
| [], _ => []
|
||||
|
||||
@@ -11,7 +11,7 @@ import Init.Util
|
||||
universe u
|
||||
|
||||
namespace List
|
||||
/-! The following functions can't be defined at `Init.Data.List.Basic`, because they depend on `Init.Util`,
|
||||
/- The following functions can't be defined at `Init.Data.List.Basic`, because they depend on `Init.Util`,
|
||||
and `Init.Util` depends on `Init.Data.List.Basic`. -/
|
||||
|
||||
def get! [Inhabited α] : List α → Nat → α
|
||||
@@ -121,9 +121,6 @@ theorem sizeOf_lt_of_mem [SizeOf α] {as : List α} (h : a ∈ as) : sizeOf a <
|
||||
| head => simp_arith
|
||||
| tail _ _ ih => exact Nat.lt_trans ih (by simp_arith)
|
||||
|
||||
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that
|
||||
`sizeOf a < sizeOf as` when `a ∈ as`, which is useful for well founded recursions
|
||||
over a nested inductive like `inductive T | mk : List T → T`. -/
|
||||
macro "sizeOf_list_dec" : tactic =>
|
||||
`(first
|
||||
| apply sizeOf_lt_of_mem; assumption; done
|
||||
|
||||
@@ -10,7 +10,7 @@ import Init.Data.List.Basic
|
||||
namespace List
|
||||
universe u v w u₁ u₂
|
||||
|
||||
/-!
|
||||
/-
|
||||
Remark: we can define `mapM`, `mapM₂` and `forM` using `Applicative` instead of `Monad`.
|
||||
Example:
|
||||
```
|
||||
@@ -193,7 +193,4 @@ instance : ForM m (List α) α where
|
||||
@[simp] theorem forM_cons [Monad m] (f : α → m PUnit) (a : α) (as : List α) : forM (a::as) f = f a >>= fun _ => forM as f :=
|
||||
rfl
|
||||
|
||||
instance : Functor List where
|
||||
map := List.map
|
||||
|
||||
end List
|
||||
|
||||
@@ -9,47 +9,31 @@ universe u
|
||||
|
||||
namespace Nat
|
||||
|
||||
@[specialize] def fold {α : Type u} (f : Nat → α → α) : (n : Nat) → (init : α) → α
|
||||
@[specialize] def foldAux {α : Type u} (f : Nat → α → α) (s : Nat) : Nat → α → α
|
||||
| 0, a => a
|
||||
| succ n, a => f n (fold f n a)
|
||||
| succ n, a => foldAux f s n (f (s - (succ n)) a)
|
||||
|
||||
@[inline] def foldTR {α : Type u} (f : Nat → α → α) (n : Nat) (init : α) : α :=
|
||||
@[inline] def fold {α : Type u} (f : Nat → α → α) (n : Nat) (init : α) : α :=
|
||||
foldAux f n n init
|
||||
|
||||
@[inline] def foldRev {α : Type u} (f : Nat → α → α) (n : Nat) (init : α) : α :=
|
||||
let rec @[specialize] loop
|
||||
| 0, a => a
|
||||
| succ m, a => loop m (f (n - succ m) a)
|
||||
| succ n, a => loop n (f n a)
|
||||
loop n init
|
||||
|
||||
@[specialize] def foldRev {α : Type u} (f : Nat → α → α) : (n : Nat) → (init : α) → α
|
||||
| 0, a => a
|
||||
| succ n, a => foldRev f n (f n a)
|
||||
|
||||
/-- `any f n = true` iff there is `i in [0, n-1]` s.t. `f i = true` -/
|
||||
@[specialize] def any (f : Nat → Bool) : Nat → Bool
|
||||
@[specialize] def anyAux (f : Nat → Bool) (s : Nat) : Nat → Bool
|
||||
| 0 => false
|
||||
| succ n => any f n || f n
|
||||
| succ n => f (s - (succ n)) || anyAux f s n
|
||||
|
||||
@[inline] def anyTR (f : Nat → Bool) (n : Nat) : Bool :=
|
||||
let rec @[specialize] loop : Nat → Bool
|
||||
| 0 => false
|
||||
| succ m => f (n - succ m) || loop m
|
||||
loop n
|
||||
/- `any f n = true` iff there is `i in [0, n-1]` s.t. `f i = true` -/
|
||||
@[inline] def any (f : Nat → Bool) (n : Nat) : Bool :=
|
||||
anyAux f n n
|
||||
|
||||
/-- `all f n = true` iff every `i in [0, n-1]` satisfies `f i = true` -/
|
||||
@[specialize] def all (f : Nat → Bool) : Nat → Bool
|
||||
| 0 => true
|
||||
| succ n => all f n && f n
|
||||
@[inline] def all (f : Nat → Bool) (n : Nat) : Bool :=
|
||||
!any (fun i => !f i) n
|
||||
|
||||
@[inline] def allTR (f : Nat → Bool) (n : Nat) : Bool :=
|
||||
let rec @[specialize] loop : Nat → Bool
|
||||
| 0 => true
|
||||
| succ m => f (n - succ m) && loop m
|
||||
loop n
|
||||
|
||||
@[specialize] def repeat {α : Type u} (f : α → α) : (n : Nat) → (a : α) → α
|
||||
| 0, a => a
|
||||
| succ n, a => f (repeat f n a)
|
||||
|
||||
@[inline] def repeatTR {α : Type u} (f : α → α) (n : Nat) (a : α) : α :=
|
||||
@[inline] def repeat {α : Type u} (f : α → α) (n : Nat) (a : α) : α :=
|
||||
let rec @[specialize] loop
|
||||
| 0, a => a
|
||||
| succ n, a => loop n (f a)
|
||||
@@ -60,7 +44,7 @@ def blt (a b : Nat) : Bool :=
|
||||
|
||||
attribute [simp] Nat.zero_le
|
||||
|
||||
/-! # Helper "packing" theorems -/
|
||||
/- Helper "packing" theorems -/
|
||||
|
||||
@[simp] theorem zero_eq : Nat.zero = 0 := rfl
|
||||
@[simp] theorem add_eq : Nat.add x y = x + y := rfl
|
||||
@@ -69,7 +53,7 @@ attribute [simp] Nat.zero_le
|
||||
@[simp] theorem lt_eq : Nat.lt x y = (x < y) := rfl
|
||||
@[simp] theorem le_eq : Nat.le x y = (x ≤ y) := rfl
|
||||
|
||||
/-! # Helper Bool relation theorems -/
|
||||
/- Helper Bool relation theorems -/
|
||||
|
||||
@[simp] theorem beq_refl (a : Nat) : Nat.beq a a = true := by
|
||||
induction a with simp [Nat.beq]
|
||||
@@ -91,7 +75,7 @@ instance : LawfulBEq Nat where
|
||||
have : ¬ ((a == b) = true) := fun h' => absurd (eq_of_beq h') h
|
||||
by simp [this])
|
||||
|
||||
/-! # Nat.add theorems -/
|
||||
/- Nat.add theorems -/
|
||||
|
||||
@[simp] protected theorem zero_add : ∀ (n : Nat), 0 + n = n
|
||||
| 0 => rfl
|
||||
@@ -136,7 +120,7 @@ protected theorem add_right_cancel {n m k : Nat} (h : n + m = k + m) : n = k :=
|
||||
rw [Nat.add_comm n m, Nat.add_comm k m] at h
|
||||
apply Nat.add_left_cancel h
|
||||
|
||||
/-! # Nat.mul theorems -/
|
||||
/- Nat.mul theorems -/
|
||||
|
||||
@[simp] protected theorem mul_zero (n : Nat) : n * 0 = 0 :=
|
||||
rfl
|
||||
@@ -184,7 +168,7 @@ protected theorem mul_assoc : ∀ (n m k : Nat), (n * m) * k = n * (m * k)
|
||||
protected theorem mul_left_comm (n m k : Nat) : n * (m * k) = m * (n * k) := by
|
||||
rw [← Nat.mul_assoc, Nat.mul_comm n m, Nat.mul_assoc]
|
||||
|
||||
/-! # Inequalities -/
|
||||
/- Inequalities -/
|
||||
|
||||
attribute [simp] Nat.le_refl
|
||||
|
||||
@@ -395,7 +379,7 @@ protected theorem le_of_add_le_add_right {a b c : Nat} : a + b ≤ c + b → a
|
||||
rw [Nat.add_comm _ b, Nat.add_comm _ b]
|
||||
apply Nat.le_of_add_le_add_left
|
||||
|
||||
/-! # Basic theorems for comparing numerals -/
|
||||
/- Basic theorems for comparing numerals -/
|
||||
|
||||
theorem ctor_eq_zero : Nat.zero = 0 :=
|
||||
rfl
|
||||
@@ -409,7 +393,7 @@ protected theorem zero_ne_one : 0 ≠ (1 : Nat) :=
|
||||
theorem succ_ne_zero (n : Nat) : succ n ≠ 0 :=
|
||||
fun h => Nat.noConfusion h
|
||||
|
||||
/-! # mul + order -/
|
||||
/- mul + order -/
|
||||
|
||||
theorem mul_le_mul_left {n m : Nat} (k : Nat) (h : n ≤ m) : k * n ≤ k * m :=
|
||||
match le.dest h with
|
||||
@@ -445,7 +429,7 @@ protected theorem eq_of_mul_eq_mul_left {m k n : Nat} (hn : 0 < n) (h : n * m =
|
||||
theorem eq_of_mul_eq_mul_right {n m k : Nat} (hm : 0 < m) (h : n * m = k * m) : n = k := by
|
||||
rw [Nat.mul_comm n m, Nat.mul_comm k m] at h; exact Nat.eq_of_mul_eq_mul_left hm h
|
||||
|
||||
/-! # power -/
|
||||
/- power -/
|
||||
|
||||
theorem pow_succ (n m : Nat) : n^(succ m) = n^m * n :=
|
||||
rfl
|
||||
@@ -471,7 +455,7 @@ theorem pow_le_pow_of_le_right {n : Nat} (hx : n > 0) {i : Nat} : ∀ {j}, i ≤
|
||||
theorem pos_pow_of_pos {n : Nat} (m : Nat) (h : 0 < n) : 0 < n^m :=
|
||||
pow_le_pow_of_le_right h (Nat.zero_le _)
|
||||
|
||||
/-! # min/max -/
|
||||
/- min/max -/
|
||||
|
||||
protected def min (n m : Nat) : Nat :=
|
||||
if n ≤ m then n else m
|
||||
@@ -479,7 +463,7 @@ protected def min (n m : Nat) : Nat :=
|
||||
protected def max (n m : Nat) : Nat :=
|
||||
if n ≤ m then m else n
|
||||
|
||||
/-! # Auxiliary theorems for well-founded recursion -/
|
||||
/- Auxiliary theorems for well-founded recursion -/
|
||||
|
||||
theorem not_eq_zero_of_lt (h : b < a) : a ≠ 0 := by
|
||||
cases a
|
||||
@@ -489,7 +473,7 @@ theorem not_eq_zero_of_lt (h : b < a) : a ≠ 0 := by
|
||||
theorem pred_lt' {n m : Nat} (h : m < n) : pred n < n :=
|
||||
pred_lt (not_eq_zero_of_lt h)
|
||||
|
||||
/-! # sub/pred theorems -/
|
||||
/- sub/pred theorems -/
|
||||
|
||||
theorem add_sub_self_left (a b : Nat) : (a + b) - a = b := by
|
||||
induction a with
|
||||
@@ -672,7 +656,7 @@ protected theorem mul_sub_right_distrib (n m k : Nat) : (n - m) * k = n * k - m
|
||||
protected theorem mul_sub_left_distrib (n m k : Nat) : n * (m - k) = n * m - n * k := by
|
||||
rw [Nat.mul_comm, Nat.mul_sub_right_distrib, Nat.mul_comm m n, Nat.mul_comm n k]
|
||||
|
||||
/-! # Helper normalization theorems -/
|
||||
/- Helper normalization theorems -/
|
||||
|
||||
theorem not_le_eq (a b : Nat) : (¬ (a ≤ b)) = (b + 1 ≤ a) :=
|
||||
propext <| Iff.intro (fun h => Nat.gt_of_not_le h) (fun h => Nat.not_le_of_gt h)
|
||||
@@ -686,51 +670,17 @@ theorem not_lt_eq (a b : Nat) : (¬ (a < b)) = (b ≤ a) :=
|
||||
theorem not_gt_eq (a b : Nat) : (¬ (a > b)) = (a ≤ b) :=
|
||||
not_lt_eq b a
|
||||
|
||||
/-! # csimp theorems -/
|
||||
|
||||
@[csimp] theorem fold_eq_foldTR : @fold = @foldTR :=
|
||||
funext fun α => funext fun f => funext fun n => funext fun init =>
|
||||
let rec go : ∀ m n, foldTR.loop f (m + n) m (fold f n init) = fold f (m + n) init
|
||||
| 0, n => by simp [foldTR.loop]
|
||||
| succ m, n => by rw [foldTR.loop, add_sub_self_left, succ_add]; exact go m (succ n)
|
||||
(go n 0).symm
|
||||
|
||||
@[csimp] theorem any_eq_anyTR : @any = @anyTR :=
|
||||
funext fun f => funext fun n =>
|
||||
let rec go : ∀ m n, (any f n || anyTR.loop f (m + n) m) = any f (m + n)
|
||||
| 0, n => by simp [anyTR.loop]
|
||||
| succ m, n => by
|
||||
rw [anyTR.loop, add_sub_self_left, ← Bool.or_assoc, succ_add]
|
||||
exact go m (succ n)
|
||||
(go n 0).symm
|
||||
|
||||
@[csimp] theorem all_eq_allTR : @all = @allTR :=
|
||||
funext fun f => funext fun n =>
|
||||
let rec go : ∀ m n, (all f n && allTR.loop f (m + n) m) = all f (m + n)
|
||||
| 0, n => by simp [allTR.loop]
|
||||
| succ m, n => by
|
||||
rw [allTR.loop, add_sub_self_left, ← Bool.and_assoc, succ_add]
|
||||
exact go m (succ n)
|
||||
(go n 0).symm
|
||||
|
||||
@[csimp] theorem repeat_eq_repeatTR : @repeat = @repeatTR :=
|
||||
funext fun α => funext fun f => funext fun n => funext fun init =>
|
||||
let rec go : ∀ m n, repeatTR.loop f m (repeat f n init) = repeat f (m + n) init
|
||||
| 0, n => by simp [repeatTR.loop]
|
||||
| succ m, n => by rw [repeatTR.loop, succ_add]; exact go m (succ n)
|
||||
(go n 0).symm
|
||||
|
||||
end Nat
|
||||
|
||||
namespace Prod
|
||||
|
||||
@[inline] def foldI {α : Type u} (f : Nat → α → α) (i : Nat × Nat) (a : α) : α :=
|
||||
Nat.foldTR.loop f i.2 (i.2 - i.1) a
|
||||
Nat.foldAux f i.2 (i.2 - i.1) a
|
||||
|
||||
@[inline] def anyI (f : Nat → Bool) (i : Nat × Nat) : Bool :=
|
||||
Nat.anyTR.loop f i.2 (i.2 - i.1)
|
||||
Nat.anyAux f i.2 (i.2 - i.1)
|
||||
|
||||
@[inline] def allI (f : Nat → Bool) (i : Nat × Nat) : Bool :=
|
||||
Nat.allTR.loop f i.2 (i.2 - i.1)
|
||||
Nat.anyAux (fun a => !f a) i.2 (i.2 - i.1)
|
||||
|
||||
end Prod
|
||||
|
||||
@@ -13,7 +13,7 @@ import Init.Data.Prod
|
||||
|
||||
namespace Nat.Linear
|
||||
|
||||
/-!
|
||||
/--!
|
||||
Helper definitions and theorems for constructing linear arithmetic proofs.
|
||||
-/
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import Init.Meta
|
||||
import Init.Data.Float
|
||||
import Init.Data.Nat
|
||||
|
||||
/-- For decimal and scientific numbers (e.g., `1.23`, `3.12e10`).
|
||||
/- For decimal and scientific numbers (e.g., `1.23`, `3.12e10`).
|
||||
Examples:
|
||||
- `OfScientific.ofScientific 123 true 2` represents `1.23`
|
||||
- `OfScientific.ofScientific 121 false 100` represents `121e100`
|
||||
@@ -31,8 +31,8 @@ protected opaque Float.ofScientific (m : Nat) (s : Bool) (e : Nat) : Float :=
|
||||
else
|
||||
Float.ofBinaryScientific (m * 5^e) e
|
||||
|
||||
/--
|
||||
The `OfScientific Float` must have priority higher than `mid` since
|
||||
/-
|
||||
The `OfScientifi Float` must have priority higher than `mid` since
|
||||
the default instance `Neg Int` has `mid` priority.
|
||||
```
|
||||
#check -42.0 -- must be Float
|
||||
|
||||
@@ -8,27 +8,27 @@ import Init.System.IO
|
||||
import Init.Data.Int
|
||||
universe u
|
||||
|
||||
/-!
|
||||
/-
|
||||
Basic random number generator support based on the one
|
||||
available on the Haskell library
|
||||
-/
|
||||
|
||||
/-- Interface for random number generators. -/
|
||||
/- Interface for random number generators. -/
|
||||
class RandomGen (g : Type u) where
|
||||
/-- `range` returns the range of values returned by
|
||||
/- `range` returns the range of values returned by
|
||||
the generator. -/
|
||||
range : g → Nat × Nat
|
||||
/-- `next` operation returns a natural number that is uniformly distributed
|
||||
/- `next` operation returns a natural number that is uniformly distributed
|
||||
the range returned by `range` (including both end points),
|
||||
and a new generator. -/
|
||||
next : g → Nat × g
|
||||
/--
|
||||
/-
|
||||
The 'split' operation allows one to obtain two distinct random number
|
||||
generators. This is very useful in functional programs (for example, when
|
||||
passing a random number generator down to recursive calls). -/
|
||||
split : g → g × g
|
||||
|
||||
/-- "Standard" random number generator. -/
|
||||
/- "Standard" random number generator. -/
|
||||
structure StdGen where
|
||||
s1 : Nat
|
||||
s2 : Nat
|
||||
@@ -76,7 +76,7 @@ def mkStdGen (s : Nat := 0) : StdGen :=
|
||||
let s2 := q % 2147483398
|
||||
⟨s1 + 1, s2 + 1⟩
|
||||
|
||||
/--
|
||||
/-
|
||||
Auxiliary function for randomNatVal.
|
||||
Generate random values until we exceed the target magnitude.
|
||||
`genLo` and `genMag` are the generator lower bound and magnitude.
|
||||
|
||||
@@ -27,7 +27,7 @@ abbrev reprStr [Repr α] (a : α) : String :=
|
||||
abbrev reprArg [Repr α] (a : α) : Format :=
|
||||
reprPrec a max_prec
|
||||
|
||||
/-- Auxiliary class for marking types that should be considered atomic by `Repr` methods.
|
||||
/- Auxiliary class for marking types that should be considered atomic by `Repr` methods.
|
||||
We use it at `Repr (List α)` to decide whether `bracketFill` should be used or not. -/
|
||||
class ReprAtom (α : Type u)
|
||||
|
||||
@@ -64,19 +64,15 @@ instance [Repr α] : Repr (ULift.{v} α) where
|
||||
instance : Repr Unit where
|
||||
reprPrec _ _ := "()"
|
||||
|
||||
protected def Option.repr [Repr α] : Option α → Nat → Format
|
||||
| none, _ => "none"
|
||||
| some a, prec => Repr.addAppParen ("some " ++ reprArg a) prec
|
||||
|
||||
instance [Repr α] : Repr (Option α) where
|
||||
reprPrec := Option.repr
|
||||
|
||||
protected def Sum.repr [Repr α] [Repr β] : Sum α β → Nat → Format
|
||||
| Sum.inl a, prec => Repr.addAppParen ("Sum.inl " ++ reprArg a) prec
|
||||
| Sum.inr b, prec => Repr.addAppParen ("Sum.inr " ++ reprArg b) prec
|
||||
reprPrec
|
||||
| none, _ => "none"
|
||||
| some a, prec => Repr.addAppParen ("some " ++ reprArg a) prec
|
||||
|
||||
instance [Repr α] [Repr β] : Repr (Sum α β) where
|
||||
reprPrec := Sum.repr
|
||||
reprPrec
|
||||
| Sum.inl a, prec => Repr.addAppParen ("Sum.inl " ++ reprArg a) prec
|
||||
| Sum.inr b, prec => Repr.addAppParen ("Sum.inr " ++ reprArg b) prec
|
||||
|
||||
class ReprTuple (α : Type u) where
|
||||
reprTuple : α → List Format → List Format
|
||||
@@ -89,11 +85,8 @@ instance [Repr α] : ReprTuple α where
|
||||
instance [Repr α] [ReprTuple β] : ReprTuple (α × β) where
|
||||
reprTuple | (a, b), xs => reprTuple b (repr a :: xs)
|
||||
|
||||
protected def Prod.repr [Repr α] [ReprTuple β] : α × β → Nat → Format
|
||||
| (a, b), _ => Format.bracket "(" (Format.joinSep (reprTuple b [repr a]).reverse ("," ++ Format.line)) ")"
|
||||
|
||||
instance [Repr α] [ReprTuple β] : Repr (α × β) where
|
||||
reprPrec := Prod.repr
|
||||
reprPrec | (a, b), _ => Format.bracket "(" (Format.joinSep (reprTuple b [repr a]).reverse ("," ++ Format.line)) ")"
|
||||
|
||||
instance {β : α → Type v} [Repr α] [(x : α) → Repr (β x)] : Repr (Sigma β) where
|
||||
reprPrec | ⟨a, b⟩, _ => Format.bracket "⟨" (repr a ++ ", " ++ repr b) "⟩"
|
||||
@@ -177,19 +170,19 @@ instance : Repr Int where
|
||||
def hexDigitRepr (n : Nat) : String :=
|
||||
String.singleton <| Nat.digitChar n
|
||||
|
||||
def charToHex (c : Char) : String :=
|
||||
let n := Char.toNat c;
|
||||
let d2 := n / 16;
|
||||
let d1 := n % 16;
|
||||
hexDigitRepr d2 ++ hexDigitRepr d1
|
||||
|
||||
def Char.quoteCore (c : Char) : String :=
|
||||
if c = '\n' then "\\n"
|
||||
else if c = '\t' then "\\t"
|
||||
else if c = '\\' then "\\\\"
|
||||
else if c = '\"' then "\\\""
|
||||
else if c.toNat <= 31 ∨ c = '\x7f' then "\\x" ++ smallCharToHex c
|
||||
else if c.toNat <= 31 ∨ c = '\x7f' then "\\x" ++ charToHex c
|
||||
else String.singleton c
|
||||
where
|
||||
smallCharToHex (c : Char) : String :=
|
||||
let n := Char.toNat c;
|
||||
let d2 := n / 16;
|
||||
let d1 := n % 16;
|
||||
hexDigitRepr d2 ++ hexDigitRepr d1
|
||||
|
||||
def Char.quote (c : Char) : String :=
|
||||
"'" ++ Char.quoteCore c ++ "'"
|
||||
@@ -234,23 +227,19 @@ instance : Repr UInt64 where
|
||||
instance : Repr USize where
|
||||
reprPrec n _ := repr n.toNat
|
||||
|
||||
protected def List.repr [Repr α] (a : List α) (n : Nat) : Format :=
|
||||
let _ : ToFormat α := ⟨repr⟩
|
||||
match a, n with
|
||||
| [], _ => "[]"
|
||||
| as, _ => Format.bracket "[" (Format.joinSep as ("," ++ Format.line)) "]"
|
||||
|
||||
instance [Repr α] : Repr (List α) where
|
||||
reprPrec := List.repr
|
||||
|
||||
protected def List.repr' [Repr α] [ReprAtom α] (a : List α) (n : Nat) : Format :=
|
||||
let _ : ToFormat α := ⟨repr⟩
|
||||
match a, n with
|
||||
| [], _ => "[]"
|
||||
| as, _ => Format.bracketFill "[" (Format.joinSep as ("," ++ Format.line)) "]"
|
||||
reprPrec a n :=
|
||||
let _ : ToFormat α := ⟨repr⟩
|
||||
match a, n with
|
||||
| [], _ => "[]"
|
||||
| as, _ => Format.bracket "[" (Format.joinSep as ("," ++ Format.line)) "]"
|
||||
|
||||
instance [Repr α] [ReprAtom α] : Repr (List α) where
|
||||
reprPrec := List.repr'
|
||||
reprPrec a n :=
|
||||
let _ : ToFormat α := ⟨repr⟩
|
||||
match a, n with
|
||||
| [], _ => "[]"
|
||||
| as, _ => Format.bracketFill "[" (Format.joinSep as ("," ++ Format.line)) "]"
|
||||
|
||||
instance : ReprAtom Bool := ⟨⟩
|
||||
instance : ReprAtom Nat := ⟨⟩
|
||||
|
||||
@@ -7,7 +7,7 @@ prelude
|
||||
import Init.Data.Array.Subarray
|
||||
import Init.Data.Range
|
||||
|
||||
/-!
|
||||
/-
|
||||
Remark: we considered using the following alternative design
|
||||
```
|
||||
structure Stream (α : Type u) where
|
||||
@@ -22,7 +22,7 @@ The key problem is that the type `Stream α` "lives" in a universe higher than `
|
||||
This is a problem because we want to use `Stream`s in monadic code.
|
||||
-/
|
||||
|
||||
/--
|
||||
/-
|
||||
Streams are used to implement parallel `for` statements.
|
||||
Example:
|
||||
```
|
||||
|
||||
@@ -227,7 +227,6 @@ where go (acc : String) (s : String) : List String → String
|
||||
| a :: as => go (acc ++ s ++ a) s as
|
||||
| [] => acc
|
||||
|
||||
/-- Iterator for `String`. That is, a `String` and a position in that string. -/
|
||||
structure Iterator where
|
||||
s : String
|
||||
i : Pos
|
||||
|
||||
@@ -13,9 +13,6 @@ import Init.WFTactics
|
||||
|
||||
namespace String
|
||||
|
||||
/-- Interpret the string as the decimal representation of a natural number.
|
||||
|
||||
Panics if the string is not a string of digits. -/
|
||||
def toNat! (s : String) : Nat :=
|
||||
if s.isNat then
|
||||
s.foldl (fun n c => n*10 + (c.toNat - '0'.toNat)) 0
|
||||
@@ -23,13 +20,11 @@ def toNat! (s : String) : Nat :=
|
||||
panic! "Nat expected"
|
||||
|
||||
/--
|
||||
Convert a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded `ByteArray` string to `String`.
|
||||
The result is unspecified if `a` is not properly UTF-8 encoded.
|
||||
-/
|
||||
Convert a UTF-8 encoded `ByteArray` string to `String`.
|
||||
The result is unspecified if `a` is not properly UTF-8 encoded. -/
|
||||
@[extern "lean_string_from_utf8_unchecked"]
|
||||
opaque fromUTF8Unchecked (a : @& ByteArray) : String
|
||||
|
||||
/-- Convert the given `String` to a [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded byte array. -/
|
||||
@[extern "lean_string_to_utf8"]
|
||||
opaque toUTF8 (a : @& String) : ByteArray
|
||||
|
||||
@@ -71,22 +66,4 @@ theorem Iterator.sizeOf_next_lt_of_atEnd (i : String.Iterator) (h : ¬ i.atEnd =
|
||||
|
||||
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply String.Iterator.sizeOf_next_lt_of_atEnd; assumption)
|
||||
|
||||
namespace Iterator
|
||||
|
||||
/-- Advance the given iterator until the predicate returns true or the end of the string is reached. -/
|
||||
@[specialize] def find (it : Iterator) (p : Char → Bool) : Iterator :=
|
||||
if it.atEnd then it
|
||||
else if p it.curr then it
|
||||
else find it.next p
|
||||
|
||||
@[specialize] def foldUntil (it : Iterator) (init : α) (f : α → Char → Option α) : α × Iterator :=
|
||||
if it.atEnd then
|
||||
(init, it)
|
||||
else if let some a := f init it.curr then
|
||||
foldUntil it.next a f
|
||||
else
|
||||
(init, it)
|
||||
|
||||
end Iterator
|
||||
|
||||
end String
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Authors: Gabriel Ebner
|
||||
-/
|
||||
prelude
|
||||
import Init.Core
|
||||
|
||||
open Lean
|
||||
|
||||
-- Implementation detail of TypeName, since classes cannot be opaque
|
||||
private opaque TypeNameData (α : Type u) : NonemptyType.{0} :=
|
||||
⟨Name, inferInstance⟩
|
||||
|
||||
/--
|
||||
Dynamic type name information.
|
||||
Types with an instance of `TypeName` can be stored in an `Dynamic`.
|
||||
The type class contains the declaration name of the type,
|
||||
which must not have any universe parameters
|
||||
and be of type `Sort ..` (i.e., monomorphic).
|
||||
|
||||
The preferred way to declare instances of this type is using the derive
|
||||
handler, which will internally use the unsafe `TypeName.mk` function.
|
||||
|
||||
Morally, this is the same as:
|
||||
```lean
|
||||
class TypeName (α : Type) where unsafe mk ::
|
||||
typeName : Name
|
||||
```
|
||||
-/
|
||||
@[nospecialize]
|
||||
class TypeName (α : Type u) where private mk' ::
|
||||
private data : (TypeNameData α).type
|
||||
|
||||
instance : Nonempty (TypeName α) := (TypeNameData α).property.elim (⟨⟨·⟩⟩)
|
||||
|
||||
/--
|
||||
Creates a `TypeName` instance.
|
||||
|
||||
For safety, it is required that the constant `typeName` is definitionally equal
|
||||
to `α`.
|
||||
-/
|
||||
unsafe def TypeName.mk (α : Type u) (typeName : Name) : TypeName α :=
|
||||
⟨unsafeCast typeName⟩
|
||||
|
||||
private unsafe def TypeName.typeNameImpl (α) [TypeName α] : Name :=
|
||||
unsafeCast (@TypeName.data α _)
|
||||
|
||||
/--
|
||||
Returns a declaration name of the type.
|
||||
-/
|
||||
@[implementedBy TypeName.typeNameImpl]
|
||||
opaque TypeName.typeName (α) [TypeName α] : Name
|
||||
|
||||
private opaque DynamicPointed : NonemptyType.{0} :=
|
||||
⟨Name × NonScalar, inferInstance⟩
|
||||
|
||||
/--
|
||||
Type-tagged union that can store any type with a `TypeName` instance.
|
||||
|
||||
This is roughly equivalent to `(α : Type) × TypeName α × α` but without the
|
||||
universe bump.
|
||||
-/
|
||||
def Dynamic : Type := DynamicPointed.type
|
||||
|
||||
instance : Nonempty Dynamic := DynamicPointed.property
|
||||
|
||||
private unsafe def Dynamic.typeNameImpl (any : Dynamic) : Name :=
|
||||
(unsafeCast any : Name × NonScalar).1
|
||||
|
||||
/--
|
||||
The name of the type of the value stored in the `Dynamic`.
|
||||
-/
|
||||
@[implementedBy Dynamic.typeNameImpl]
|
||||
opaque Dynamic.typeName (any : Dynamic) : Name
|
||||
|
||||
private unsafe def Dynamic.get?Impl (α) (any : Dynamic) [TypeName α] : Option α :=
|
||||
let ((typeName, obj) : Name × NonScalar) := unsafeCast any
|
||||
if typeName == TypeName.typeName α then
|
||||
some (unsafeCast obj)
|
||||
else
|
||||
none
|
||||
|
||||
/--
|
||||
Retrieves the value stored in the `Dynamic`.
|
||||
Returns `some a` if the value has the right type, and `none` otherwise.
|
||||
-/
|
||||
@[implementedBy Dynamic.get?Impl]
|
||||
opaque Dynamic.get? (α) (any : Dynamic) [TypeName α] : Option α
|
||||
|
||||
private unsafe def Dynamic.mkImpl [TypeName α] (obj : α) : Dynamic :=
|
||||
unsafeCast (TypeName.typeName α, (unsafeCast obj : NonScalar))
|
||||
|
||||
@[implementedBy Dynamic.mkImpl]
|
||||
opaque Dynamic.mk [TypeName α] (obj : α) : Dynamic
|
||||
@@ -64,7 +64,7 @@ def toolchain :=
|
||||
@[extern c inline "LEAN_IS_STAGE0"]
|
||||
opaque Internal.isStage0 (u : Unit) : Bool
|
||||
|
||||
/-- Valid identifier names -/
|
||||
/- Valid identifier names -/
|
||||
def isGreek (c : Char) : Bool :=
|
||||
0x391 ≤ c.val && c.val ≤ 0x3dd
|
||||
|
||||
@@ -158,6 +158,8 @@ protected def reprPrec (n : Name) (prec : Nat) : Std.Format :=
|
||||
instance : Repr Name where
|
||||
reprPrec := Name.reprPrec
|
||||
|
||||
deriving instance Repr for Syntax
|
||||
|
||||
def capitalize : Name → Name
|
||||
| .str p s => .str p s.capitalize
|
||||
| n => n
|
||||
@@ -255,9 +257,6 @@ instance monadNameGeneratorLift (m n : Type → Type) [MonadLift m n] [MonadName
|
||||
|
||||
namespace Syntax
|
||||
|
||||
deriving instance Repr for Syntax.Preresolved
|
||||
deriving instance Repr for Syntax
|
||||
|
||||
abbrev Term := TSyntax `term
|
||||
abbrev Command := TSyntax `command
|
||||
protected abbrev Level := TSyntax `level
|
||||
@@ -326,9 +325,6 @@ end TSyntax
|
||||
|
||||
namespace Syntax
|
||||
|
||||
deriving instance BEq for Syntax.Preresolved
|
||||
|
||||
/-- Compare syntax structures modulo source info. -/
|
||||
partial def structEq : Syntax → Syntax → Bool
|
||||
| Syntax.missing, Syntax.missing => true
|
||||
| Syntax.node _ k args, Syntax.node _ k' args' => k == k' && args.isEqv args' structEq
|
||||
@@ -452,7 +448,7 @@ end Syntax
|
||||
@[inline] def mkNode (k : SyntaxNodeKind) (args : Array Syntax) : TSyntax k :=
|
||||
⟨Syntax.node SourceInfo.none k args⟩
|
||||
|
||||
/-- Syntax objects for a Lean module. -/
|
||||
/- Syntax objects for a Lean module. -/
|
||||
structure Module where
|
||||
header : Syntax
|
||||
commands : Array Syntax
|
||||
@@ -483,20 +479,19 @@ structure Module where
|
||||
syntatic categories are expanded by `expandMacros`.
|
||||
-/
|
||||
partial def expandMacros (stx : Syntax) (p : SyntaxNodeKind → Bool := fun k => k != `Lean.Parser.Term.byTactic) : MacroM Syntax :=
|
||||
withRef stx do
|
||||
match stx with
|
||||
| .node info k args => do
|
||||
if p k then
|
||||
match (← expandMacro? stx) with
|
||||
| some stxNew => expandMacros stxNew
|
||||
| none => do
|
||||
let args ← Macro.withIncRecDepth stx <| args.mapM expandMacros
|
||||
return .node info k args
|
||||
else
|
||||
return stx
|
||||
| stx => return stx
|
||||
match stx with
|
||||
| .node info k args => do
|
||||
if p k then
|
||||
match (← expandMacro? stx) with
|
||||
| some stxNew => expandMacros stxNew
|
||||
| none => do
|
||||
let args ← Macro.withIncRecDepth stx <| args.mapM expandMacros
|
||||
return .node info k args
|
||||
else
|
||||
return stx
|
||||
| stx => return stx
|
||||
|
||||
/-! # Helper functions for processing Syntax programmatically -/
|
||||
/- Helper functions for processing Syntax programmatically -/
|
||||
|
||||
/--
|
||||
Create an identifier copying the position from `src`.
|
||||
@@ -514,7 +509,7 @@ def mkIdentFromRef [Monad m] [MonadRef m] (val : Name) : m Ident := do
|
||||
def mkCIdentFrom (src : Syntax) (c : Name) : Ident :=
|
||||
-- Remark: We use the reserved macro scope to make sure there are no accidental collision with our frontend
|
||||
let id := addMacroScope `_internal c reservedMacroScope
|
||||
⟨Syntax.ident (SourceInfo.fromRef src) (toString id).toSubstring id [.decl c []]⟩
|
||||
⟨Syntax.ident (SourceInfo.fromRef src) (toString id).toSubstring id [(c, [])]⟩
|
||||
|
||||
def mkCIdentFromRef [Monad m] [MonadRef m] (c : Name) : m Syntax := do
|
||||
return mkCIdentFrom (← getRef) c
|
||||
@@ -593,7 +588,7 @@ def mkScientificLit (val : String) (info := SourceInfo.none) : TSyntax scientifi
|
||||
def mkNameLit (val : String) (info := SourceInfo.none) : NameLit :=
|
||||
mkLit nameLitKind val info
|
||||
|
||||
/-! Recall that we don't have special Syntax constructors for storing numeric and string atoms.
|
||||
/- Recall that we don't have special Syntax constructors for storing numeric and string atoms.
|
||||
The idea is to have an extensible approach where embedded DSLs may have new kind of atoms and/or
|
||||
different ways of representing them. So, our atoms contain just the parsed string.
|
||||
The main Lean parser uses the kind `numLitKind` for storing natural numbers that can be encoded
|
||||
@@ -675,10 +670,6 @@ def isNatLit? (s : Syntax) : Option Nat :=
|
||||
def isFieldIdx? (s : Syntax) : Option Nat :=
|
||||
isNatLitAux fieldIdxKind s
|
||||
|
||||
/-- Decodes a 'scientific number' string which is consumed by the `OfScientific` class.
|
||||
Takes as input a string such as `123`, `123.456e7` and returns a triple `(n, sign, e)` with value given by
|
||||
`n * 10^-e` if `sign` else `n * 10^e`.
|
||||
-/
|
||||
partial def decodeScientificLitVal? (s : String) : Option (Nat × Bool × Nat) :=
|
||||
let len := s.length
|
||||
if len == 0 then none
|
||||
@@ -704,12 +695,9 @@ where
|
||||
none
|
||||
|
||||
decodeExp (i : String.Pos) (val : Nat) (e : Nat) : Option (Nat × Bool × Nat) :=
|
||||
if s.atEnd i then none else
|
||||
let c := s.get i
|
||||
if c == '-' then
|
||||
decodeAfterExp (s.next i) val e true 0
|
||||
else if c == '+' then
|
||||
decodeAfterExp (s.next i) val e false 0
|
||||
else
|
||||
decodeAfterExp i val e false 0
|
||||
|
||||
@@ -978,7 +966,7 @@ instance Option.hasQuote {α : Type} [Quote α `term] : Quote (Option α) `term
|
||||
| (some x) => Syntax.mkCApp ``some #[quote x]
|
||||
|
||||
|
||||
/-- Evaluator for `prec` DSL -/
|
||||
/- Evaluator for `prec` DSL -/
|
||||
def evalPrec (stx : Syntax) : MacroM Nat :=
|
||||
Macro.withIncRecDepth stx do
|
||||
let stx ← expandMacros stx
|
||||
@@ -994,7 +982,7 @@ macro_rules
|
||||
|
||||
macro "eval_prec " p:prec:max : term => return quote (k := `term) (← evalPrec p)
|
||||
|
||||
/-- Evaluator for `prio` DSL -/
|
||||
/- Evaluator for `prio` DSL -/
|
||||
def evalPrio (stx : Syntax) : MacroM Nat :=
|
||||
Macro.withIncRecDepth stx do
|
||||
let stx ← expandMacros stx
|
||||
@@ -1073,18 +1061,6 @@ def SepArray.getElems (sa : SepArray sep) : Array Syntax :=
|
||||
def TSepArray.getElems (sa : TSepArray k sep) : TSyntaxArray k :=
|
||||
.mk sa.elemsAndSeps.getSepElems
|
||||
|
||||
def TSepArray.push (sa : TSepArray k sep) (e : TSyntax k) : TSepArray k sep :=
|
||||
if sa.elemsAndSeps.isEmpty then
|
||||
{ elemsAndSeps := #[e] }
|
||||
else
|
||||
{ elemsAndSeps := sa.elemsAndSeps.push (mkAtom sep) |>.push e }
|
||||
|
||||
instance : EmptyCollection (SepArray sep) where
|
||||
emptyCollection := ⟨∅⟩
|
||||
|
||||
instance : EmptyCollection (TSepArray sep k) where
|
||||
emptyCollection := ⟨∅⟩
|
||||
|
||||
/-
|
||||
We use `CoeTail` here instead of `Coe` to avoid a "loop" when computing `CoeTC`.
|
||||
The "loop" is interrupted using the maximum instance size threshold, but it is a performance bottleneck.
|
||||
@@ -1118,8 +1094,7 @@ set_option linter.unusedVariables.funArgs false in
|
||||
For example, the tactic will *not* be invoked during type class resolution. -/
|
||||
abbrev autoParam.{u} (α : Sort u) (tactic : Lean.Syntax) : Sort u := α
|
||||
|
||||
/-! # Helper functions for manipulating interpolated strings -/
|
||||
|
||||
/- Helper functions for manipulating interpolated strings -/
|
||||
namespace Lean.Syntax
|
||||
|
||||
private def decodeInterpStrQuotedChar (s : String) (i : String.Pos) : Option (Char × String.Pos) := do
|
||||
@@ -1264,61 +1239,38 @@ end Meta
|
||||
|
||||
namespace Parser.Tactic
|
||||
|
||||
/-- `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 loc:(location)? : tactic =>
|
||||
`(rw (config := { transparency := .default }) $s $(loc)?)
|
||||
`(rw (config := { transparency := Lean.Meta.TransparencyMode.default }) $s:rwRuleSeq $[$loc:location]?)
|
||||
|
||||
syntax simpAllKind := atomic("(" &"all") " := " &"true" ")"
|
||||
syntax dsimpKind := atomic("(" &"dsimp") " := " &"true" ")"
|
||||
|
||||
macro (name := declareSimpLikeTactic) doc?:(docComment)? "declare_simp_like_tactic" opt:((simpAllKind <|> dsimpKind)?) tacName:ident tacToken:str updateCfg:term : command => do
|
||||
macro "declare_simp_like_tactic" opt:((simpAllKind <|> dsimpKind)?) tacName:ident tacToken:str updateCfg:term : command => do
|
||||
let (kind, tkn, stx) ←
|
||||
if opt.raw.isNone then
|
||||
pure (← `(``simp), ← `("simp "), ← `($[$doc?:docComment]? syntax (name := $tacName) $tacToken:str (config)? (discharger)? (&"only ")? ("[" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic))
|
||||
pure (← `(``simp), ← `("simp "), ← `(syntax (name := $tacName:ident) $tacToken:str (config)? (discharger)? (&"only ")? ("[" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic))
|
||||
else if opt.raw[0].getKind == ``simpAllKind then
|
||||
pure (← `(``simpAll), ← `("simp_all "), ← `($[$doc?:docComment]? syntax (name := $tacName) $tacToken:str (config)? (discharger)? (&"only ")? ("[" (simpErase <|> simpLemma),* "]")? : tactic))
|
||||
pure (← `(``simpAll), ← `("simp_all "), ← `(syntax (name := $tacName:ident) $tacToken:str (config)? (discharger)? (&"only ")? ("[" (simpErase <|> simpLemma),* "]")? : tactic))
|
||||
else
|
||||
pure (← `(``dsimp), ← `("dsimp "), ← `($[$doc?:docComment]? syntax (name := $tacName) $tacToken:str (config)? (discharger)? (&"only ")? ("[" (simpErase <|> simpLemma),* "]")? (location)? : tactic))
|
||||
pure (← `(``dsimp), ← `("dsimp "), ← `(syntax (name := $tacName:ident) $tacToken:str (config)? (discharger)? (&"only ")? ("[" (simpErase <|> simpLemma),* "]")? (location)? : tactic))
|
||||
`($stx:command
|
||||
@[macro $tacName] def expandSimp : Macro := fun s => do
|
||||
@[macro $tacName:ident] def expandSimp : Macro := fun s => do
|
||||
let c ← match s[1][0] with
|
||||
| `(config| (config := $$c)) => `(config| (config := $updateCfg $$c))
|
||||
| _ => `(config| (config := $updateCfg {}))
|
||||
let s := s.setKind $kind
|
||||
let s := s.setArg 0 (mkAtomFrom s[0] $tkn)
|
||||
| `(config| (config := $$c:term)) => `(config| (config := $updateCfg:term $$c))
|
||||
| _ => `(config| (config := $updateCfg:term {}))
|
||||
let s := s.setKind $kind:term
|
||||
let s := s.setArg 0 (mkAtomFrom s[0] $tkn:term)
|
||||
let r := s.setArg 1 (mkNullNode #[c])
|
||||
return r)
|
||||
|
||||
/-- `simp!` is shorthand for `simp` with `autoUnfold := true`.
|
||||
This will rewrite with all equation lemmas, which can be used to
|
||||
partially evaluate many definitions. -/
|
||||
declare_simp_like_tactic simpAutoUnfold "simp! " fun (c : Lean.Meta.Simp.Config) => { c with autoUnfold := true }
|
||||
|
||||
/-- `simp_arith` is shorthand for `simp` with `arith := true`.
|
||||
This enables the use of normalization by linear arithmetic. -/
|
||||
declare_simp_like_tactic simpArith "simp_arith " fun (c : Lean.Meta.Simp.Config) => { c with arith := true }
|
||||
|
||||
/-- `simp_arith!` is shorthand for `simp_arith` with `autoUnfold := true`.
|
||||
This will rewrite with all equation lemmas, which can be used to
|
||||
partially evaluate many definitions. -/
|
||||
declare_simp_like_tactic simpArithAutoUnfold "simp_arith! " fun (c : Lean.Meta.Simp.Config) => { c with arith := true, autoUnfold := true }
|
||||
|
||||
/-- `simp_all!` is shorthand for `simp_all` with `autoUnfold := true`.
|
||||
This will rewrite with all equation lemmas, which can be used to
|
||||
partially evaluate many definitions. -/
|
||||
declare_simp_like_tactic (all := true) simpAllAutoUnfold "simp_all! " fun (c : Lean.Meta.Simp.ConfigCtx) => { c with autoUnfold := true }
|
||||
|
||||
/-- `simp_all_arith` combines the effects of `simp_all` and `simp_arith`. -/
|
||||
declare_simp_like_tactic (all := true) simpAllArith "simp_all_arith " fun (c : Lean.Meta.Simp.ConfigCtx) => { c with arith := true }
|
||||
|
||||
/-- `simp_all_arith!` combines the effects of `simp_all`, `simp_arith` and `simp!`. -/
|
||||
declare_simp_like_tactic (all := true) simpAllArithAutoUnfold "simp_all_arith! " fun (c : Lean.Meta.Simp.ConfigCtx) => { c with arith := true, autoUnfold := true }
|
||||
|
||||
/-- `dsimp!` is shorthand for `dsimp` with `autoUnfold := true`.
|
||||
This will rewrite with all equation lemmas, which can be used to
|
||||
partially evaluate many definitions. -/
|
||||
declare_simp_like_tactic (dsimp := true) dsimpAutoUnfold "dsimp! " fun (c : Lean.Meta.DSimp.Config) => { c with autoUnfold := true }
|
||||
|
||||
end Parser.Tactic
|
||||
|
||||
@@ -1,102 +1,27 @@
|
||||
/-
|
||||
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Mario Carneiro
|
||||
Authors: Leonardo de Moura
|
||||
|
||||
Notation for operators defined at Prelude.lean
|
||||
-/
|
||||
prelude
|
||||
import Init.Prelude
|
||||
import Init.Coe
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
|
||||
namespace Lean
|
||||
-- DSL for specifying parser precedences and priorities
|
||||
|
||||
/--
|
||||
Auxiliary type used to represent syntax categories. We mainly use auxiliary
|
||||
definitions with this type to attach doc strings to syntax categories.
|
||||
-/
|
||||
structure Parser.Category
|
||||
namespace Lean.Parser.Syntax
|
||||
|
||||
namespace Parser.Category
|
||||
|
||||
/-- `command` is the syntax category for things that appear at the top level
|
||||
of a lean file. For example, `def foo := 1` is a `command`, as is
|
||||
`namespace Foo` and `end Foo`. Commands generally have an effect on the state of
|
||||
adding something to the environment (like a new definition), as well as
|
||||
commands like `variable` which modify future commands within a scope. -/
|
||||
def command : Category := {}
|
||||
|
||||
/-- `term` is the builtin syntax category for terms. A term denotes an expression
|
||||
in lean's type theory, for example `2 + 2` is a term. The difference between
|
||||
`Term` and `Expr` is that the former is a kind of syntax, while the latter is
|
||||
the result of elaboration. For example `by simp` is also a `Term`, but it elaborates
|
||||
to different `Expr`s depending on the context. -/
|
||||
def term : Category := {}
|
||||
|
||||
/-- `tactic` is the builtin syntax category for tactics. These appear after
|
||||
`by` in proofs, and they are programs that take in the proof context
|
||||
(the hypotheses in scope plus the type of the term to synthesize) and construct
|
||||
a term of the expected type. For example, `simp` is a tactic, used in:
|
||||
```
|
||||
example : 2 + 2 = 4 := by simp
|
||||
```
|
||||
-/
|
||||
def tactic : Category := {}
|
||||
|
||||
/-- `doElem` is a builtin syntax category for elements that can appear in the `do` notation.
|
||||
For example, `let x ← e` is a `doElem`, and a `do` block consists of a list of `doElem`s. -/
|
||||
def doElem : Category := {}
|
||||
|
||||
/-- `level` is a builtin syntax category for universe levels.
|
||||
This is the `u` in `Sort u`: it can contain `max` and `imax`, addition with
|
||||
constants, and variables. -/
|
||||
def level : Category := {}
|
||||
|
||||
/-- `attr` is a builtin syntax category for attributes.
|
||||
Declarations can be annotated with attributes using the `@[...]` notation. -/
|
||||
def attr : Category := {}
|
||||
|
||||
/-- `stx` is a builtin syntax category for syntax. This is the abbreviated
|
||||
parser notation used inside `syntax` and `macro` declarations. -/
|
||||
def stx : Category := {}
|
||||
|
||||
/-- `prio` is a builtin syntax category for priorities.
|
||||
Priorities are used in many different attributes.
|
||||
Higher numbers denote higher priority, and for example typeclass search will
|
||||
try high priority instances before low priority.
|
||||
In addition to literals like `37`, you can also use `low`, `mid`, `high`, as well as
|
||||
add and subtract priorities. -/
|
||||
def prio : Category := {}
|
||||
|
||||
/-- `prec` is a builtin syntax category for precedences. A precedence is a value
|
||||
that expresses how tightly a piece of syntax binds: for example `1 + 2 * 3` is
|
||||
parsed as `1 + (2 * 3)` because `*` has a higher pr0ecedence than `+`.
|
||||
Higher numbers denote higher precedence.
|
||||
In addition to literals like `37`, there are some special named priorities:
|
||||
* `arg` for the precedence of function arguments
|
||||
* `max` for the highest precedence used in term parsers (not actually the maximum possible value)
|
||||
* `lead` for the precedence of terms not supposed to be used as arguments
|
||||
and you can also add and subtract precedences. -/
|
||||
def prec : Category := {}
|
||||
|
||||
end Parser.Category
|
||||
|
||||
namespace Parser.Syntax
|
||||
|
||||
/-! DSL for specifying parser precedences and priorities -/
|
||||
|
||||
/-- Addition of precedences. This is normally used only for offseting, e.g. `max + 1`. -/
|
||||
syntax:65 (name := addPrec) prec " + " prec:66 : prec
|
||||
/-- Subtraction of precedences. This is normally used only for offseting, e.g. `max - 1`. -/
|
||||
syntax:65 (name := subPrec) prec " - " prec:66 : prec
|
||||
|
||||
/-- Addition of priorities. This is normally used only for offseting, e.g. `default + 1`. -/
|
||||
syntax:65 (name := addPrio) prio " + " prio:66 : prio
|
||||
/-- Subtraction of priorities. This is normally used only for offseting, e.g. `default - 1`. -/
|
||||
syntax:65 (name := subPrio) prio " - " prio:66 : prio
|
||||
|
||||
end Parser.Syntax
|
||||
end Lean.Parser.Syntax
|
||||
|
||||
namespace Lean
|
||||
|
||||
instance : CoeHead (TSyntax ks) Syntax where
|
||||
coe stx := stx.raw
|
||||
@@ -106,85 +31,28 @@ instance : Coe SyntaxNodeKind SyntaxNodeKinds where
|
||||
|
||||
end Lean
|
||||
|
||||
/--
|
||||
Maximum precedence used in term parsers, in particular for terms in
|
||||
function position (`ident`, `paren`, ...)
|
||||
-/
|
||||
macro "max" : prec => `(1024)
|
||||
/-- Precedence used for application arguments (`do`, `by`, ...). -/
|
||||
macro "arg" : prec => `(1023)
|
||||
/-- Precedence used for terms not supposed to be used as arguments (`let`, `have`, ...). -/
|
||||
macro "lead" : prec => `(1022)
|
||||
/-- Parentheses are used for grouping precedence expressions. -/
|
||||
macro "max" : prec => `(1024) -- maximum precedence used in term parsers, in particular for terms in function position (`ident`, `paren`, ...)
|
||||
macro "arg" : prec => `(1023) -- precedence used for application arguments (`do`, `by`, ...)
|
||||
macro "lead" : prec => `(1022) -- precedence used for terms not supposed to be used as arguments (`let`, `have`, ...)
|
||||
macro "(" p:prec ")" : prec => return p
|
||||
/-- Minimum precedence used in term parsers. -/
|
||||
macro "min" : prec => `(10)
|
||||
/-- `(min+1)` (we can only write `min+1` after `Meta.lean`) -/
|
||||
macro "min1" : prec => `(11)
|
||||
/--
|
||||
`max:prec` as a term. It is equivalent to `eval_prec max` for `eval_prec` defined at `Meta.lean`.
|
||||
We use `max_prec` to workaround bootstrapping issues.
|
||||
-/
|
||||
macro "min" : prec => `(10) -- minimum precedence used in term parsers
|
||||
macro "min1" : prec => `(11) -- `(min+1) we can only `min+1` after `Meta.lean`
|
||||
/-
|
||||
`max:prec` as a term. It is equivalent to `eval_prec max` for `eval_prec` defined at `Meta.lean`.
|
||||
We use `max_prec` to workaround bootstrapping issues. -/
|
||||
macro "max_prec" : term => `(1024)
|
||||
|
||||
/-- The default priority `default = 1000`, which is used when no priority is set. -/
|
||||
macro "default" : prio => `(1000)
|
||||
/-- The standardized "low" priority `low = 100`, for things that should be lower than default priority. -/
|
||||
macro "low" : prio => `(100)
|
||||
/-- The standardized "medium" priority `med = 1000`. This is the same as `default`. -/
|
||||
macro "mid" : prio => `(1000)
|
||||
/-- The standardized "high" priority `high = 10000`, for things that should be higher than default priority. -/
|
||||
macro "high" : prio => `(10000)
|
||||
/-- Parentheses are used for grouping priority expressions. -/
|
||||
macro "(" p:prio ")" : prio => return p
|
||||
|
||||
-- Basic notation for defining parsers
|
||||
-- NOTE: precedence must be at least `arg` to be used in `macro` without parentheses
|
||||
|
||||
/--
|
||||
`p+` is shorthand for `many1(p)`. It uses parser `p` 1 or more times, and produces a
|
||||
`nullNode` containing the array of parsed results. This parser has arity 1.
|
||||
|
||||
If `p` has arity more than 1, it is auto-grouped in the items generated by the parser.
|
||||
-/
|
||||
syntax:arg stx:max "+" : stx
|
||||
|
||||
/--
|
||||
`p*` is shorthand for `many(p)`. It uses parser `p` 0 or more times, and produces a
|
||||
`nullNode` containing the array of parsed results. This parser has arity 1.
|
||||
|
||||
If `p` has arity more than 1, it is auto-grouped in the items generated by the parser.
|
||||
-/
|
||||
syntax:arg stx:max "*" : stx
|
||||
|
||||
/--
|
||||
`(p)?` is shorthand for `optional(p)`. It uses parser `p` 0 or 1 times, and produces a
|
||||
`nullNode` containing the array of parsed results. This parser has arity 1.
|
||||
|
||||
`p` is allowed to have arity n > 1 (in which case the node will have either 0 or n children),
|
||||
but if it has arity 0 then the result will be ambiguous.
|
||||
|
||||
Because `?` is an identifier character, `ident?` will not work as intended.
|
||||
You have to write either `ident ?` or `(ident)?` for it to parse as the `?` combinator
|
||||
applied to the `ident` parser.
|
||||
-/
|
||||
syntax:arg stx:max "?" : stx
|
||||
|
||||
/--
|
||||
`p1 <|> p2` is shorthand for `orelse(p1, p2)`, and parses either `p1` or `p2`.
|
||||
It does not backtrack, meaning that if `p1` consumes at least one token then
|
||||
`p2` will not be tried. Therefore, the parsers should all differ in their first
|
||||
token. The `atomic(p)` parser combinator can be used to locally backtrack a parser.
|
||||
(For full backtracking, consider using extensible syntax classes instead.)
|
||||
|
||||
On success, if the inner parser does not generate exactly one node, it will be
|
||||
automatically wrapped in a `group` node, so the result will always be arity 1.
|
||||
|
||||
The `<|>` combinator does not generate a node of its own, and in particular
|
||||
does not tag the inner parsers to distinguish them, which can present a problem
|
||||
when reconstructing the parse. A well formed `<|>` parser should use disjoint
|
||||
node kinds for `p1` and `p2`.
|
||||
-/
|
||||
syntax:2 stx:2 " <|> " stx:1 : stx
|
||||
|
||||
macro_rules
|
||||
@@ -193,80 +61,35 @@ macro_rules
|
||||
| `(stx| $p ?) => `(stx| optional($p))
|
||||
| `(stx| $p₁ <|> $p₂) => `(stx| orelse($p₁, $p₂))
|
||||
|
||||
/--
|
||||
`p,*` is shorthand for `sepBy(p, ",")`. It parses 0 or more occurrences of
|
||||
`p` separated by `,`, that is: `empty | p | p,p | p,p,p | ...`.
|
||||
|
||||
It produces a `nullNode` containing a `SepArray` with the interleaved parser
|
||||
results. It has arity 1, and auto-groups its component parser if needed.
|
||||
-/
|
||||
/- Comma-separated sequence. -/
|
||||
macro:arg x:stx:max ",*" : stx => `(stx| sepBy($x, ",", ", "))
|
||||
/--
|
||||
`p,+` is shorthand for `sepBy(p, ",")`. It parses 1 or more occurrences of
|
||||
`p` separated by `,`, that is: `p | p,p | p,p,p | ...`.
|
||||
|
||||
It produces a `nullNode` containing a `SepArray` with the interleaved parser
|
||||
results. It has arity 1, and auto-groups its component parser if needed.
|
||||
-/
|
||||
macro:arg x:stx:max ",+" : stx => `(stx| sepBy1($x, ",", ", "))
|
||||
|
||||
/--
|
||||
`p,*,?` is shorthand for `sepBy(p, ",", allowTrailingSep)`.
|
||||
It parses 0 or more occurrences of `p` separated by `,`, possibly including
|
||||
a trailing `,`, that is: `empty | p | p, | p,p | p,p, | p,p,p | ...`.
|
||||
|
||||
It produces a `nullNode` containing a `SepArray` with the interleaved parser
|
||||
results. It has arity 1, and auto-groups its component parser if needed.
|
||||
-/
|
||||
/- Comma-separated sequence with optional trailing comma. -/
|
||||
macro:arg x:stx:max ",*,?" : stx => `(stx| sepBy($x, ",", ", ", allowTrailingSep))
|
||||
|
||||
/--
|
||||
`p,+,?` is shorthand for `sepBy1(p, ",", allowTrailingSep)`.
|
||||
It parses 1 or more occurrences of `p` separated by `,`, possibly including
|
||||
a trailing `,`, that is: `p | p, | p,p | p,p, | p,p,p | ...`.
|
||||
|
||||
It produces a `nullNode` containing a `SepArray` with the interleaved parser
|
||||
results. It has arity 1, and auto-groups its component parser if needed.
|
||||
-/
|
||||
macro:arg x:stx:max ",+,?" : stx => `(stx| sepBy1($x, ",", ", ", allowTrailingSep))
|
||||
|
||||
/--
|
||||
`!p` parses the negation of `p`. That is, it fails if `p` succeeds, and
|
||||
otherwise parses nothing. It has arity 0.
|
||||
-/
|
||||
macro:arg "!" x:stx:max : stx => `(stx| notFollowedBy($x))
|
||||
|
||||
/--
|
||||
The `nat_lit n` macro constructs "raw numeric literals". This corresponds to the
|
||||
`Expr.lit (.natVal n)` constructor in the `Expr` data type.
|
||||
|
||||
Normally, when you write a numeral like `#check 37`, the parser turns this into
|
||||
an application of `OfNat.ofNat` to the raw literal `37` to cast it into the
|
||||
target type, even if this type is `Nat` (so the cast is the identity function).
|
||||
But sometimes it is necessary to talk about the raw numeral directly,
|
||||
especially when proving properties about the `ofNat` function itself.
|
||||
-/
|
||||
syntax (name := rawNatLit) "nat_lit " num : term
|
||||
|
||||
@[inheritDoc] infixr:90 " ∘ " => Function.comp
|
||||
@[inheritDoc] infixr:35 " × " => Prod
|
||||
infixr:90 " ∘ " => Function.comp
|
||||
infixr:35 " × " => Prod
|
||||
|
||||
@[inheritDoc] infixl:55 " ||| " => HOr.hOr
|
||||
@[inheritDoc] infixl:58 " ^^^ " => HXor.hXor
|
||||
@[inheritDoc] infixl:60 " &&& " => HAnd.hAnd
|
||||
@[inheritDoc] infixl:65 " + " => HAdd.hAdd
|
||||
@[inheritDoc] infixl:65 " - " => HSub.hSub
|
||||
@[inheritDoc] infixl:70 " * " => HMul.hMul
|
||||
@[inheritDoc] infixl:70 " / " => HDiv.hDiv
|
||||
@[inheritDoc] infixl:70 " % " => HMod.hMod
|
||||
@[inheritDoc] infixl:75 " <<< " => HShiftLeft.hShiftLeft
|
||||
@[inheritDoc] infixl:75 " >>> " => HShiftRight.hShiftRight
|
||||
@[inheritDoc] infixr:80 " ^ " => HPow.hPow
|
||||
@[inheritDoc] infixl:65 " ++ " => HAppend.hAppend
|
||||
@[inheritDoc] prefix:100 "-" => Neg.neg
|
||||
@[inheritDoc] prefix:100 "~~~" => Complement.complement
|
||||
|
||||
/-!
|
||||
infixl:55 " ||| " => HOr.hOr
|
||||
infixl:58 " ^^^ " => HXor.hXor
|
||||
infixl:60 " &&& " => HAnd.hAnd
|
||||
infixl:65 " + " => HAdd.hAdd
|
||||
infixl:65 " - " => HSub.hSub
|
||||
infixl:70 " * " => HMul.hMul
|
||||
infixl:70 " / " => HDiv.hDiv
|
||||
infixl:70 " % " => HMod.hMod
|
||||
infixl:75 " <<< " => HShiftLeft.hShiftLeft
|
||||
infixl:75 " >>> " => HShiftRight.hShiftRight
|
||||
infixr:80 " ^ " => HPow.hPow
|
||||
infixl:65 " ++ " => HAppend.hAppend
|
||||
prefix:100 "-" => Neg.neg
|
||||
prefix:100 "~~~" => Complement.complement
|
||||
/-
|
||||
Remark: the infix commands above ensure a delaborator is generated for each relations.
|
||||
We redefine the macros below to be able to use the auxiliary `binop%` elaboration helper for binary operators.
|
||||
It addresses issue #382. -/
|
||||
@@ -282,15 +105,15 @@ macro_rules | `($x ^ $y) => `(binop% HPow.hPow $x $y)
|
||||
macro_rules | `($x ++ $y) => `(binop% HAppend.hAppend $x $y)
|
||||
|
||||
-- declare ASCII alternatives first so that the latter Unicode unexpander wins
|
||||
@[inheritDoc] infix:50 " <= " => LE.le
|
||||
@[inheritDoc] infix:50 " ≤ " => LE.le
|
||||
@[inheritDoc] infix:50 " < " => LT.lt
|
||||
@[inheritDoc] infix:50 " >= " => GE.ge
|
||||
@[inheritDoc] infix:50 " ≥ " => GE.ge
|
||||
@[inheritDoc] infix:50 " > " => GT.gt
|
||||
@[inheritDoc] infix:50 " = " => Eq
|
||||
@[inheritDoc] infix:50 " == " => BEq.beq
|
||||
/-!
|
||||
infix:50 " <= " => LE.le
|
||||
infix:50 " ≤ " => LE.le
|
||||
infix:50 " < " => LT.lt
|
||||
infix:50 " >= " => GE.ge
|
||||
infix:50 " ≥ " => GE.ge
|
||||
infix:50 " > " => GT.gt
|
||||
infix:50 " = " => Eq
|
||||
infix:50 " == " => BEq.beq
|
||||
/-
|
||||
Remark: the infix commands above ensure a delaborator is generated for each relations.
|
||||
We redefine the macros below to be able to use the auxiliary `binrel%` elaboration helper for binary relations.
|
||||
It has better support for applying coercions. For example, suppose we have `binrel% Eq n i` where `n : Nat` and
|
||||
@@ -305,136 +128,89 @@ macro_rules | `($x ≥ $y) => `(binrel% GE.ge $x $y)
|
||||
macro_rules | `($x = $y) => `(binrel% Eq $x $y)
|
||||
macro_rules | `($x == $y) => `(binrel_no_prop% BEq.beq $x $y)
|
||||
|
||||
@[inheritDoc] infixr:35 " /\\ " => And
|
||||
@[inheritDoc] infixr:35 " ∧ " => And
|
||||
@[inheritDoc] infixr:30 " \\/ " => Or
|
||||
@[inheritDoc] infixr:30 " ∨ " => Or
|
||||
@[inheritDoc] notation:max "¬" p:40 => Not p
|
||||
infixr:35 " /\\ " => And
|
||||
infixr:35 " ∧ " => And
|
||||
infixr:30 " \\/ " => Or
|
||||
infixr:30 " ∨ " => Or
|
||||
notation:max "¬" p:40 => Not p
|
||||
|
||||
@[inheritDoc] infixl:35 " && " => and
|
||||
@[inheritDoc] infixl:30 " || " => or
|
||||
@[inheritDoc] notation:max "!" b:40 => not b
|
||||
infixl:35 " && " => and
|
||||
infixl:30 " || " => or
|
||||
notation:max "!" b:40 => not b
|
||||
|
||||
@[inheritDoc] infix:50 " ∈ " => Membership.mem
|
||||
/-- `a ∉ b` is negated elementhood. It is notation for `¬ (a ∈ b)`. -/
|
||||
infix:50 " ∈ " => Membership.mem
|
||||
notation:50 a:50 " ∉ " b:50 => ¬ (a ∈ b)
|
||||
|
||||
@[inheritDoc] infixr:67 " :: " => List.cons
|
||||
@[inheritDoc HOrElse.hOrElse] syntax:20 term:21 " <|> " term:20 : term
|
||||
@[inheritDoc HAndThen.hAndThen] syntax:60 term:61 " >> " term:60 : term
|
||||
@[inheritDoc] infixl:55 " >>= " => Bind.bind
|
||||
@[inheritDoc] notation:60 a:60 " <*> " b:61 => Seq.seq a fun _ : Unit => b
|
||||
@[inheritDoc] notation:60 a:60 " <* " b:61 => SeqLeft.seqLeft a fun _ : Unit => b
|
||||
@[inheritDoc] notation:60 a:60 " *> " b:61 => SeqRight.seqRight a fun _ : Unit => b
|
||||
@[inheritDoc] infixr:100 " <$> " => Functor.map
|
||||
infixr:67 " :: " => List.cons
|
||||
syntax:20 term:21 " <|> " term:20 : term
|
||||
syntax:60 term:61 " >> " term:60 : term
|
||||
infixl:55 " >>= " => Bind.bind
|
||||
notation:60 a:60 " <*> " b:61 => Seq.seq a fun _ : Unit => b
|
||||
notation:60 a:60 " <* " b:61 => SeqLeft.seqLeft a fun _ : Unit => b
|
||||
notation:60 a:60 " *> " b:61 => SeqRight.seqRight a fun _ : Unit => b
|
||||
infixr:100 " <$> " => Functor.map
|
||||
|
||||
macro_rules | `($x <|> $y) => `(binop_lazy% HOrElse.hOrElse $x $y)
|
||||
macro_rules | `($x >> $y) => `(binop_lazy% HAndThen.hAndThen $x $y)
|
||||
|
||||
@[inheritDoc dite] syntax (name := termDepIfThenElse)
|
||||
syntax (name := termDepIfThenElse)
|
||||
ppRealGroup(ppRealFill(ppIndent("if " ident " : " term " then") ppSpace term)
|
||||
ppDedent(ppSpace) ppRealFill("else " term)) : term
|
||||
|
||||
macro_rules
|
||||
| `(if $h : $c then $t else $e) => do
|
||||
let mvar ← Lean.withRef c `(?m)
|
||||
`(let_mvar% ?m := $c; wait_if_type_mvar% ?m; dite $mvar (fun $h:ident => $t) (fun $h:ident => $e))
|
||||
| `(if $h : $c then $t else $e) => `(let_mvar% ?m := $c; wait_if_type_mvar% ?m; dite ?m (fun $h:ident => $t) (fun $h:ident => $e))
|
||||
|
||||
@[inheritDoc ite] syntax (name := termIfThenElse)
|
||||
syntax (name := termIfThenElse)
|
||||
ppRealGroup(ppRealFill(ppIndent("if " term " then") ppSpace term)
|
||||
ppDedent(ppSpace) ppRealFill("else " term)) : term
|
||||
|
||||
macro_rules
|
||||
| `(if $c then $t else $e) => do
|
||||
let mvar ← Lean.withRef c `(?m)
|
||||
`(let_mvar% ?m := $c; wait_if_type_mvar% ?m; ite $mvar $t $e)
|
||||
| `(if $c then $t else $e) => `(let_mvar% ?m := $c; wait_if_type_mvar% ?m; ite ?m $t $e)
|
||||
|
||||
/--
|
||||
`if let pat := d then t else e` is a shorthand syntax for:
|
||||
```
|
||||
match d with
|
||||
| pat => t
|
||||
| _ => e
|
||||
```
|
||||
It matches `d` against the pattern `pat` and the bindings are available in `t`.
|
||||
If the pattern does not match, it returns `e` instead.
|
||||
-/
|
||||
macro "if " "let " pat:term " := " d:term " then " t:term " else " e:term : term =>
|
||||
`(match $d:term with | $pat => $t | _ => $e)
|
||||
|
||||
@[inheritDoc cond] syntax (name := boolIfThenElse)
|
||||
syntax (name := boolIfThenElse)
|
||||
ppRealGroup(ppRealFill(ppIndent("bif " term " then") ppSpace term)
|
||||
ppDedent(ppSpace) ppRealFill("else " term)) : term
|
||||
|
||||
macro_rules
|
||||
| `(bif $c then $t else $e) => `(cond $c $t $e)
|
||||
|
||||
/--
|
||||
Haskell-like pipe operator `<|`. `f <| x` means the same as the same as `f x`,
|
||||
except that it parses `x` with lower precedence, which means that `f <| g <| x`
|
||||
is interpreted as `f (g x)` rather than `(f g) x`.
|
||||
-/
|
||||
syntax:min term " <| " term:min : term
|
||||
|
||||
macro_rules
|
||||
| `($f $args* <| $a) => `($f $args* $a)
|
||||
| `($f <| $a) => `($f $a)
|
||||
|
||||
/--
|
||||
Haskell-like pipe operator `|>`. `x |> f` means the same as the same as `f x`,
|
||||
and it chains such that `x |> f |> g` is interpreted as `g (f x)`.
|
||||
-/
|
||||
syntax:min term " |> " term:min1 : term
|
||||
|
||||
macro_rules
|
||||
| `($a |> $f $args*) => `($f $args* $a)
|
||||
| `($a |> $f) => `($f $a)
|
||||
|
||||
/--
|
||||
Alternative syntax for `<|`. `f $ x` means the same as the same as `f x`,
|
||||
except that it parses `x` with lower precedence, which means that `f $ g $ x`
|
||||
is interpreted as `f (g x)` rather than `(f g) x`.
|
||||
-/
|
||||
-- Note that we have a whitespace after `$` to avoid an ambiguity with antiquotations.
|
||||
-- Haskell-like pipe <|
|
||||
-- Note that we have a whitespace after `$` to avoid an ambiguity with the antiquotations.
|
||||
syntax:min term atomic(" $" ws) term:min : term
|
||||
|
||||
macro_rules
|
||||
| `($f $args* $ $a) => `($f $args* $a)
|
||||
| `($f $ $a) => `($f $a)
|
||||
|
||||
@[inheritDoc Subtype] syntax "{ " ident (" : " term)? " // " term " }" : term
|
||||
syntax "{ " ident (" : " term)? " // " term " }" : term
|
||||
|
||||
macro_rules
|
||||
| `({ $x : $type // $p }) => ``(Subtype (fun ($x:ident : $type) => $p))
|
||||
| `({ $x // $p }) => ``(Subtype (fun ($x:ident : _) => $p))
|
||||
|
||||
/--
|
||||
`without_expected_type t` instructs Lean to elaborate `t` without an expected type.
|
||||
Recall that terms such as `match ... with ...` and `⟨...⟩` will postpone elaboration until
|
||||
expected type is known. So, `without_expected_type` is not effective in this case.
|
||||
-/
|
||||
/-
|
||||
`without_expected_type t` instructs Lean to elaborate `t` without an expected type.
|
||||
Recall that terms such as `match ... with ...` and `⟨...⟩` will postpone elaboration until
|
||||
expected type is known. So, `without_expected_type` is not effective in this case. -/
|
||||
macro "without_expected_type " x:term : term => `(let aux := $x; aux)
|
||||
|
||||
/--
|
||||
The syntax `[a, b, c]` is shorthand for `a :: b :: c :: []`, or
|
||||
`List.cons a (List.cons b (List.cons c List.nil))`. It allows conveniently constructing
|
||||
list literals.
|
||||
|
||||
For lists of length at least 64, an alternative desugaring strategy is used
|
||||
which uses let bindings as intermediates as in
|
||||
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
|
||||
Note that this changes the order of evaluation, although it should not be observable
|
||||
unless you use side effecting operations like `dbg_trace`.
|
||||
-/
|
||||
syntax "[" term,* "]" : term
|
||||
|
||||
/--
|
||||
Auxiliary syntax for implementing `[$elem,*]` list literal syntax.
|
||||
The syntax `%[a,b,c|tail]` constructs a value equivalent to `a::b::c::tail`.
|
||||
It uses binary partitioning to construct a tree of intermediate let bindings as in
|
||||
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
|
||||
-/
|
||||
syntax "%[" term,* "|" term "]" : term
|
||||
syntax "%[" term,* "|" term "]" : term -- auxiliary notation for creating big list literals
|
||||
|
||||
namespace Lean
|
||||
|
||||
@@ -452,34 +228,14 @@ macro_rules
|
||||
`(%[ $elems,* | List.nil ])
|
||||
|
||||
-- Declare `this` as a keyword that unhygienically binds to a scope-less `this` assumption (or other binding).
|
||||
-- The keyword prevents declaring a `this` binding except through metaprogramming, as is done by `have`/`show`.
|
||||
-- The keyword prevents declaring a `this` binding except through metapgrogramming, as is done by `have`/`show`.
|
||||
/-- Special identifier introduced by "anonymous" `have : ...`, `suffices p ...` etc. -/
|
||||
macro tk:"this" : term => return Syntax.ident tk.getHeadInfo "this".toSubstring `this []
|
||||
|
||||
/--
|
||||
Category for carrying raw syntax trees between macros; any content is printed as is by the pretty printer.
|
||||
The only accepted parser for this category is an antiquotation.
|
||||
-/
|
||||
/-
|
||||
Category for carrying raw syntax trees between macros; any content is printed as is by the pretty printer.
|
||||
The only accepted parser for this category is an antiquotation. -/
|
||||
declare_syntax_cat rawStx
|
||||
|
||||
instance : Coe Syntax (TSyntax `rawStx) where
|
||||
coe stx := ⟨stx⟩
|
||||
|
||||
/-- `with_annotate_term stx e` annotates the lexical range of `stx : Syntax` with term info for `e`. -/
|
||||
scoped syntax (name := withAnnotateTerm) "with_annotate_term " rawStx ppSpace term : term
|
||||
|
||||
/--
|
||||
The attribute `@[deprecated]` on a declaration indicates that the declaration
|
||||
is discouraged for use in new code, and/or should be migrated away from in
|
||||
existing code. It may be removed in a future version of the library.
|
||||
|
||||
`@[deprecated myBetterDef]` means that `myBetterDef` is the suggested replacement.
|
||||
-/
|
||||
syntax (name := deprecated) "deprecated " (ident)? : attr
|
||||
|
||||
/--
|
||||
When `parent_dir` contains the current Lean file, `include_str "path" / "to" / "file"` becomes
|
||||
a string literal with the contents of the file at `"parent_dir" / "path" / "to" / "file"`. If this
|
||||
file cannot be read, elaboration fails.
|
||||
-/
|
||||
syntax (name := includeStr) "include_str" term : term
|
||||
|
||||
@@ -29,7 +29,7 @@ def expandExplicitBindersAux (combinator : Syntax) (idents : Array Syntax) (type
|
||||
let ident := idents[i]![0]
|
||||
let acc ← match ident.isIdent, type? with
|
||||
| true, none => `($combinator fun $ident => $acc)
|
||||
| true, some type => `($combinator fun $ident : $type => $acc)
|
||||
| true, some type => `($combinator fun $ident:ident : $type => $acc)
|
||||
| false, none => `($combinator fun _ => $acc)
|
||||
| false, some type => `($combinator fun _ : $type => $acc)
|
||||
loop i acc
|
||||
@@ -64,15 +64,15 @@ def expandBrackedBinders (combinatorDeclName : Name) (bracketedExplicitBinders :
|
||||
syntax unifConstraint := term (" =?= " <|> " ≟ ") term
|
||||
syntax unifConstraintElem := colGe unifConstraint ", "?
|
||||
|
||||
syntax (docComment)? attrKind "unif_hint " (ident)? bracketedBinder* " where " withPosition(unifConstraintElem*) ("|-" <|> "⊢ ") unifConstraint : command
|
||||
syntax attrKind "unif_hint " (ident)? bracketedBinder* " where " withPosition(unifConstraintElem*) ("|-" <|> "⊢ ") unifConstraint : command
|
||||
|
||||
macro_rules
|
||||
| `($[$doc?:docComment]? $kind:attrKind unif_hint $(n)? $bs* where $[$cs₁ ≟ $cs₂]* |- $t₁ ≟ $t₂) => do
|
||||
| `($kind:attrKind unif_hint $(n)? $bs:bracketedBinder* where $[$cs₁:term ≟ $cs₂]* |- $t₁:term ≟ $t₂) => do
|
||||
let mut body ← `($t₁ = $t₂)
|
||||
for (c₁, c₂) in cs₁.zip cs₂ |>.reverse do
|
||||
body ← `($c₁ = $c₂ → $body)
|
||||
let hint : Ident ← `(hint)
|
||||
`($[$doc?:docComment]? @[$kind unificationHint] def $(n.getD hint) $bs* : Sort _ := $body)
|
||||
`(@[$kind:attrKind unificationHint] def $(n.getD hint):ident $bs:bracketedBinder* : Sort _ := $body)
|
||||
end Lean
|
||||
|
||||
open Lean
|
||||
@@ -86,55 +86,17 @@ macro:35 xs:bracketedExplicitBinders " ×' " b:term:35 : term => expandBrackedBi
|
||||
|
||||
-- enforce indentation of calc steps so we know when to stop parsing them
|
||||
syntax calcStep := ppIndent(colGe term " := " withPosition(term))
|
||||
syntax (name := calc) "calc" ppLine withPosition((calcStep ppLine)+) : term
|
||||
|
||||
/-- Step-wise reasoning over transitive relations.
|
||||
```
|
||||
calc
|
||||
a = b := pab
|
||||
b = c := pbc
|
||||
...
|
||||
y = z := pyz
|
||||
```
|
||||
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
|
||||
relation implementing the typeclass `Trans`. Instead of repeating the right-
|
||||
hand sides, subsequent left-hand sides can be replaced with `_`.
|
||||
|
||||
`calc` has term mode and tactic mode variants. This is the term mode variant.
|
||||
|
||||
See [Theorem Proving in Lean 4][tpil4] for more information.
|
||||
|
||||
[tpil4]: https://leanprover.github.io/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
|
||||
-/
|
||||
syntax (name := calc) "calc" ppLine withPosition(calcStep) ppLine withPosition((calcStep ppLine)*) : term
|
||||
|
||||
/-- Step-wise reasoning over transitive relations.
|
||||
```
|
||||
calc
|
||||
a = b := pab
|
||||
b = c := pbc
|
||||
...
|
||||
y = z := pyz
|
||||
```
|
||||
proves `a = z` from the given step-wise proofs. `=` can be replaced with any
|
||||
relation implementing the typeclass `Trans`. Instead of repeating the right-
|
||||
hand sides, subsequent left-hand sides can be replaced with `_`.
|
||||
|
||||
`calc` has term mode and tactic mode variants. This is the tactic mode variant,
|
||||
which supports an additional feature: it works even if the goal is `a = z'`
|
||||
for some other `z'`; in this case it will not close the goal but will instead
|
||||
leave a subgoal proving `z = z'`.
|
||||
|
||||
See [Theorem Proving in Lean 4][tpil4] for more information.
|
||||
|
||||
[tpil4]: https://leanprover.github.io/theorem_proving_in_lean4/quantifiers_and_equality.html#calculational-proofs
|
||||
-/
|
||||
syntax (name := calcTactic) "calc" ppLine withPosition(calcStep) ppLine withPosition((calcStep ppLine)*) : tactic
|
||||
macro "calc " steps:withPosition(calcStep+) : tactic => `(exact calc $steps*)
|
||||
|
||||
@[appUnexpander Unit.unit] def unexpandUnit : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_)) => `(())
|
||||
| _ => throw ()
|
||||
|
||||
@[appUnexpander List.nil] def unexpandListNil : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_)) => `([])
|
||||
| _ => throw ()
|
||||
|
||||
@[appUnexpander List.cons] def unexpandListCons : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_) $x []) => `([$x])
|
||||
@@ -199,19 +161,19 @@ syntax (name := calcTactic) "calc" ppLine withPosition(calcStep) ppLine withPosi
|
||||
| _ => throw ()
|
||||
|
||||
@[appUnexpander GetElem.getElem] def unexpandGetElem : Lean.PrettyPrinter.Unexpander
|
||||
| `($_ $array $index $_) => `($array[$index])
|
||||
| `(getElem $array $index $_) => `($array[$index])
|
||||
| _ => throw ()
|
||||
|
||||
@[appUnexpander getElem!] def unexpandGetElem! : Lean.PrettyPrinter.Unexpander
|
||||
| `($_ $array $index) => `($array[$index]!)
|
||||
| `(getElem! $array $index) => `($array[$index]!)
|
||||
| _ => throw ()
|
||||
|
||||
@[appUnexpander getElem?] def unexpandGetElem? : Lean.PrettyPrinter.Unexpander
|
||||
| `($_ $array $index) => `($array[$index]?)
|
||||
| `(getElem? $array $index) => `($array[$index]?)
|
||||
| _ => throw ()
|
||||
|
||||
@[appUnexpander getElem'] def unexpandGetElem' : Lean.PrettyPrinter.Unexpander
|
||||
| `($_ $array $index $h) => `($array[$index]'$h)
|
||||
| `(getElem' $array $index $h) => `($array[$index]'$h)
|
||||
| _ => throw ()
|
||||
|
||||
/--
|
||||
@@ -245,7 +207,7 @@ macro_rules
|
||||
`(let y := %[ $[$y],* | $k ]
|
||||
%[ $[$z],* | y ])
|
||||
|
||||
/--
|
||||
/-
|
||||
Expands
|
||||
```
|
||||
class abbrev C <params> := D_1, ..., D_n
|
||||
@@ -256,27 +218,19 @@ macro_rules
|
||||
attribute [instance] C.mk
|
||||
```
|
||||
-/
|
||||
syntax (name := Lean.Parser.Command.classAbbrev)
|
||||
declModifiers "class " "abbrev " declId bracketedBinder* (":" term)?
|
||||
syntax declModifiers "class " "abbrev " declId bracketedBinder* (":" term)?
|
||||
":=" withPosition(group(colGe term ","?)*) : command
|
||||
|
||||
macro_rules
|
||||
| `($mods:declModifiers class abbrev $id $params* $[: $ty]? := $[ $parents $[,]? ]*) =>
|
||||
let ctor := mkIdentFrom id <| id.raw[0].getId.modifyBase (. ++ `mk)
|
||||
`($mods:declModifiers class $id $params* extends $parents,* $[: $ty]?
|
||||
`($mods:declModifiers class $id $params* extends $[$parents:term],* $[: $ty]?
|
||||
attribute [instance] $ctor)
|
||||
|
||||
section
|
||||
open Lean.Parser.Tactic
|
||||
/-- `· tac` focuses on the main goal and tries to solve it using `tac`, or else fails. -/
|
||||
syntax ("·" <|> ".") ppHardSpace many1Indent(tactic ";"? ppLine) : tactic
|
||||
macro_rules
|
||||
| `(tactic| ·%$dot $[$tacs $[;%$sc]?]*) => do
|
||||
let tacs ← tacs.zip sc |>.mapM fun
|
||||
| (tac, none) => pure tac
|
||||
| (tac, some sc) => `(tactic| ($tac; with_annotate_state $sc skip))
|
||||
`(tactic| { with_annotate_state $dot skip; $[$tacs]* })
|
||||
end
|
||||
| `(tactic| ·%$dot $[$tacs:tactic $[;%$sc]?]*) => `(tactic| {%$dot $[$tacs:tactic $[;%$sc]?]*})
|
||||
|
||||
/--
|
||||
Similar to `first`, but succeeds only if one the given tactics solves the current goal.
|
||||
@@ -287,7 +241,7 @@ macro_rules
|
||||
| `(tactic| solve $[| $ts]* ) => `(tactic| focus first $[| ($ts); done]*)
|
||||
|
||||
namespace Lean
|
||||
/-! # `repeat` and `while` notation -/
|
||||
/- `repeat` and `while` notation -/
|
||||
|
||||
inductive Loop where
|
||||
| mk
|
||||
@@ -308,11 +262,6 @@ syntax "repeat " doSeq : doElem
|
||||
macro_rules
|
||||
| `(doElem| repeat $seq) => `(doElem| for _ in Loop.mk do $seq)
|
||||
|
||||
syntax "while " ident " : " termBeforeDo " do " doSeq : doElem
|
||||
|
||||
macro_rules
|
||||
| `(doElem| while $h : $cond do $seq) => `(doElem| repeat if $h : $cond then $seq else break)
|
||||
|
||||
syntax "while " termBeforeDo " do " doSeq : doElem
|
||||
|
||||
macro_rules
|
||||
@@ -321,7 +270,7 @@ macro_rules
|
||||
syntax "repeat " doSeq " until " term : doElem
|
||||
|
||||
macro_rules
|
||||
| `(doElem| repeat $seq until $cond) => `(doElem| repeat do $seq:doSeq; if $cond then break)
|
||||
| `(doElem| repeat $seq until $cond) => `(doElem| repeat do $seq; if $cond then break)
|
||||
|
||||
macro:50 e:term:51 " matches " p:sepBy1(term:51, "|") : term =>
|
||||
`(((match $e:term with | $[$p:term]|* => true | _ => false) : Bool))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,104 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Mario Carneiro
|
||||
-/
|
||||
prelude
|
||||
import Init.Util
|
||||
|
||||
namespace ShareCommon
|
||||
/-
|
||||
The max sharing primitives are implemented internally.
|
||||
They use maps and sets of Lean objects. We have two versions:
|
||||
one using `HashMap` and `HashSet`, and another using
|
||||
`PersistentHashMap` and `PersistentHashSet`.
|
||||
These maps and sets are "instantiated here using the "unsafe"
|
||||
primitives `Object.eq`, `Object.hash`, and `ptrAddrUnsafe`. -/
|
||||
abbrev Object : Type := NonScalar
|
||||
|
||||
unsafe def Object.ptrEq (a b : Object) : Bool :=
|
||||
ptrAddrUnsafe a == ptrAddrUnsafe b
|
||||
|
||||
unsafe abbrev Object.ptrHash (a : Object) : UInt64 :=
|
||||
ptrAddrUnsafe a |>.toUInt64
|
||||
|
||||
structure StateFactoryImpl where
|
||||
(Map Set : Type)
|
||||
mkState : Unit → Map × Set
|
||||
mapFind? (m : Map) (k : Object) : Option Object
|
||||
mapInsert (m : Map) (k v : Object) : Map
|
||||
setFind? (m : Set) (k : Object) : Option Object
|
||||
setInsert (m : Set) (o : Object) : Set
|
||||
|
||||
opaque StateFactoryPointed : NonemptyType
|
||||
abbrev StateFactory : Type := StateFactoryPointed.type
|
||||
instance : Nonempty StateFactory := StateFactoryPointed.property
|
||||
|
||||
@[extern "lean_sharecommon_eq"]
|
||||
unsafe opaque Object.eq (a b : @& Object) : Bool
|
||||
|
||||
@[extern "lean_sharecommon_hash"]
|
||||
unsafe opaque Object.hash (a : @& Object) : UInt64
|
||||
|
||||
structure StateFactoryBuilder where
|
||||
Map (α _β : Type) [BEq α] [Hashable α] : Type
|
||||
mkMap {α β : Type} [BEq α] [Hashable α] (capacity : Nat) : Map α β
|
||||
mapFind? {α β : Type} [BEq α] [Hashable α] : Map α β → α → Option β
|
||||
mapInsert {α β : Type} [BEq α] [Hashable α] : Map α β → α → β → Map α β
|
||||
Set (α : Type) [BEq α] [Hashable α] : Type
|
||||
mkSet {α : Type} [BEq α] [Hashable α] (capacity : Nat) : Set α
|
||||
setFind? {α : Type} [BEq α] [Hashable α] : Set α → α → Option α
|
||||
setInsert {α : Type} [BEq α] [Hashable α] : Set α → α → Set α
|
||||
|
||||
unsafe def StateFactory.mkImpl : StateFactoryBuilder → StateFactory
|
||||
| { Map, mkMap, mapFind?, mapInsert, Set, mkSet, setFind?, setInsert } =>
|
||||
unsafeCast {
|
||||
Map := @Map Object Object ⟨Object.ptrEq⟩ ⟨Object.ptrHash⟩
|
||||
Set := @Set Object ⟨Object.eq⟩ ⟨Object.hash⟩
|
||||
mkState := fun _ => (
|
||||
@mkMap Object Object ⟨Object.ptrEq⟩ ⟨Object.ptrHash⟩ 1024,
|
||||
@mkSet Object ⟨Object.eq⟩ ⟨Object.hash⟩ 1024)
|
||||
mapFind? := @mapFind? Object Object ⟨Object.ptrEq⟩ ⟨Object.ptrHash⟩
|
||||
mapInsert := @mapInsert Object Object ⟨Object.ptrEq⟩ ⟨Object.ptrHash⟩
|
||||
setFind? := @setFind? Object ⟨Object.eq⟩ ⟨Object.hash⟩
|
||||
setInsert := @setInsert Object ⟨Object.eq⟩ ⟨Object.hash⟩
|
||||
: StateFactoryImpl }
|
||||
|
||||
@[implementedBy StateFactory.mkImpl]
|
||||
opaque StateFactory.mk : StateFactoryBuilder → StateFactory
|
||||
|
||||
unsafe def StateFactory.get : StateFactory → StateFactoryImpl := unsafeCast
|
||||
|
||||
/-- Internally `State` is implemented as a pair `ObjectMap` and `ObjectSet` -/
|
||||
opaque StatePointed (σ : StateFactory) : NonemptyType
|
||||
abbrev State (σ : StateFactory) : Type u := (StatePointed σ).type
|
||||
instance : Nonempty (State σ) := (StatePointed σ).property
|
||||
|
||||
unsafe def mkStateImpl (σ : StateFactory) : State σ := unsafeCast (σ.get.mkState ())
|
||||
@[implementedBy mkStateImpl] opaque State.mk (σ : StateFactory) : State σ
|
||||
instance : Inhabited (State σ) := ⟨.mk σ⟩
|
||||
|
||||
@[extern "lean_state_sharecommon"]
|
||||
def State.shareCommon {σ : @& StateFactory} (s : State σ) (a : α) : α × State σ := (a, s)
|
||||
|
||||
end ShareCommon
|
||||
|
||||
class MonadShareCommon (m : Type u → Type v) where
|
||||
withShareCommon {α : Type u} : α → m α
|
||||
|
||||
abbrev withShareCommon := @MonadShareCommon.withShareCommon
|
||||
|
||||
abbrev shareCommonM [MonadShareCommon m] (a : α) : m α :=
|
||||
withShareCommon a
|
||||
|
||||
abbrev ShareCommonT (σ) (m : Type u → Type v) := StateT (ShareCommon.State σ) m
|
||||
abbrev ShareCommonM (σ) := ShareCommonT σ Id
|
||||
|
||||
@[specialize] def ShareCommonT.withShareCommon [Monad m] (a : α) : ShareCommonT σ m α :=
|
||||
modifyGet fun s => s.shareCommon a
|
||||
|
||||
instance ShareCommonT.monadShareCommon [Monad m] : MonadShareCommon (ShareCommonT σ m) where
|
||||
withShareCommon := ShareCommonT.withShareCommon
|
||||
|
||||
@[inline] def ShareCommonT.run [Monad m] (x : ShareCommonT σ m α) : m α := x.run' default
|
||||
@[inline] def ShareCommonM.run (x : ShareCommonM σ α) : α := ShareCommonT.run x
|
||||
@@ -7,117 +7,130 @@ notation, basic datatypes and type classes
|
||||
-/
|
||||
prelude
|
||||
import Init.Core
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
|
||||
theorem of_eq_true (h : p = True) : p := h ▸ trivial
|
||||
@[simp] theorem eq_self (a : α) : (a = a) = True :=
|
||||
propext <| Iff.intro (fun _ => trivial) (fun _ => rfl)
|
||||
|
||||
theorem of_eq_true (h : p = True) : p :=
|
||||
h ▸ trivial
|
||||
|
||||
theorem eq_true (h : p) : p = True :=
|
||||
propext ⟨fun _ => trivial, fun _ => h⟩
|
||||
propext <| Iff.intro (fun _ => trivial) (fun _ => h)
|
||||
|
||||
theorem eq_false (h : ¬ p) : p = False :=
|
||||
propext ⟨fun h' => absurd h' h, fun h' => False.elim h'⟩
|
||||
propext <| Iff.intro (fun h' => absurd h' h) (fun h' => False.elim h')
|
||||
|
||||
theorem eq_false' (h : p → False) : p = False := eq_false h
|
||||
theorem eq_false' (h : p → False) : p = False :=
|
||||
propext <| Iff.intro (fun h' => absurd h' h) (fun h' => False.elim h')
|
||||
|
||||
theorem eq_true_of_decide {p : Prop} {_ : Decidable p} (h : decide p = true) : p = True :=
|
||||
eq_true (of_decide_eq_true h)
|
||||
propext <| Iff.intro (fun _ => trivial) (fun _ => of_decide_eq_true h)
|
||||
|
||||
theorem eq_false_of_decide {p : Prop} {_ : Decidable p} (h : decide p = false) : p = False :=
|
||||
eq_false (of_decide_eq_false h)
|
||||
|
||||
@[simp] theorem eq_self (a : α) : (a = a) = True := eq_true rfl
|
||||
propext <| Iff.intro (fun h' => absurd h' (of_decide_eq_false h)) (fun h => False.elim h)
|
||||
|
||||
theorem implies_congr {p₁ p₂ : Sort u} {q₁ q₂ : Sort v} (h₁ : p₁ = p₂) (h₂ : q₁ = q₂) : (p₁ → q₁) = (p₂ → q₂) :=
|
||||
h₁ ▸ h₂ ▸ rfl
|
||||
|
||||
theorem implies_dep_congr_ctx {p₁ p₂ q₁ : Prop} (h₁ : p₁ = p₂) {q₂ : p₂ → Prop} (h₂ : (h : p₂) → q₁ = q₂ h) : (p₁ → q₁) = ((h : p₂) → q₂ h) :=
|
||||
propext ⟨
|
||||
fun hl hp₂ => (h₂ hp₂).mp (hl (h₁.mpr hp₂)),
|
||||
fun hr hp₁ => (h₂ (h₁.mp hp₁)).mpr (hr (h₁.mp hp₁))⟩
|
||||
|
||||
theorem implies_congr_ctx {p₁ p₂ q₁ q₂ : Prop} (h₁ : p₁ = p₂) (h₂ : p₂ → q₁ = q₂) : (p₁ → q₁) = (p₂ → q₂) :=
|
||||
implies_dep_congr_ctx h₁ h₂
|
||||
propext <| Iff.intro
|
||||
(fun h hp₂ =>
|
||||
have : p₁ := h₁ ▸ hp₂
|
||||
have : q₁ := h this
|
||||
h₂ hp₂ ▸ this)
|
||||
(fun h hp₁ =>
|
||||
have hp₂ : p₂ := h₁ ▸ hp₁
|
||||
have : q₂ := h hp₂
|
||||
h₂ hp₂ ▸ this)
|
||||
|
||||
theorem forall_congr {α : Sort u} {p q : α → Prop} (h : ∀ a, p a = q a) : (∀ a, p a) = (∀ a, q a) :=
|
||||
(funext h : p = q) ▸ rfl
|
||||
theorem implies_dep_congr_ctx {p₁ p₂ q₁ : Prop} (h₁ : p₁ = p₂) {q₂ : p₂ → Prop} (h₂ : (h : p₂) → q₁ = q₂ h) : (p₁ → q₁) = ((h : p₂) → q₂ h) :=
|
||||
propext <| Iff.intro
|
||||
(fun hl hp₂ => Eq.mp (h₂ hp₂) (hl (Eq.mpr h₁ hp₂)))
|
||||
(fun hr hp₁ => Eq.mpr (h₂ (Eq.mp h₁ hp₁)) (hr (Eq.mp h₁ hp₁)))
|
||||
|
||||
theorem let_congr {α : Sort u} {β : Sort v} {a a' : α} {b b' : α → β}
|
||||
(h₁ : a = a') (h₂ : ∀ x, b x = b' x) : (let x := a; b x) = (let x := a'; b' x) :=
|
||||
h₁ ▸ (funext h₂ : b = b') ▸ rfl
|
||||
theorem forall_congr {α : Sort u} {p q : α → Prop} (h : ∀ a, (p a = q a)) : (∀ a, p a) = (∀ a, q a) :=
|
||||
have : p = q := funext h
|
||||
this ▸ rfl
|
||||
|
||||
theorem let_val_congr {α : Sort u} {β : Sort v} {a a' : α}
|
||||
(b : α → β) (h : a = a') : (let x := a; b x) = (let x := a'; b x) := h ▸ rfl
|
||||
theorem let_congr {α : Sort u} {β : Sort v} {a a' : α} {b b' : α → β} (h₁ : a = a') (h₂ : ∀ x, b x = b' x) :
|
||||
(let x := a; b x) = (let x := a'; b' x) := by
|
||||
subst h₁
|
||||
have : b = b' := funext h₂
|
||||
subst this
|
||||
rfl
|
||||
|
||||
theorem let_body_congr {α : Sort u} {β : α → Sort v} {b b' : (a : α) → β a}
|
||||
(a : α) (h : ∀ x, b x = b' x) : (let x := a; b x) = (let x := a; b' x) :=
|
||||
(funext h : b = b') ▸ rfl
|
||||
theorem let_val_congr {α : Sort u} {β : Sort v} {a a' : α} (b : α → β) (h : a = a') :
|
||||
(let x := a; b x) = (let x := a'; b x) := by
|
||||
subst h
|
||||
rfl
|
||||
|
||||
theorem let_body_congr {α : Sort u} {β : α → Sort v} {b b' : (a : α) → β a} (a : α) (h : ∀ x, b x = b' x) :
|
||||
(let x := a; b x) = (let x := a; b' x) := by
|
||||
have : b = b' := funext h
|
||||
subst this
|
||||
rfl
|
||||
|
||||
@[congr]
|
||||
theorem ite_congr {x y u v : α} {s : Decidable b} [Decidable c]
|
||||
(h₁ : b = c) (h₂ : c → x = u) (h₃ : ¬ c → y = v) : ite b x y = ite c u v := by
|
||||
theorem ite_congr {x y u v : α} {s : Decidable b} [Decidable c] (h₁ : b = c) (h₂ : c → x = u) (h₃ : ¬ c → y = v) : ite b x y = ite c u v := by
|
||||
cases Decidable.em c with
|
||||
| inl h => rw [if_pos h]; subst b; rw [if_pos h]; exact h₂ h
|
||||
| inr h => rw [if_neg h]; subst b; rw [if_neg h]; exact h₃ h
|
||||
| inl h => rw [if_pos h]; subst b; rw[if_pos h]; exact h₂ h
|
||||
| inr h => rw [if_neg h]; subst b; rw[if_neg h]; exact h₃ h
|
||||
|
||||
theorem Eq.mpr_prop {p q : Prop} (h₁ : p = q) (h₂ : q) : p := h₁ ▸ h₂
|
||||
theorem Eq.mpr_not {p q : Prop} (h₁ : p = q) (h₂ : ¬q) : ¬p := h₁ ▸ h₂
|
||||
theorem Eq.mpr_prop {p q : Prop} (h₁ : p = q) (h₂ : q) : p :=
|
||||
h₁ ▸ h₂
|
||||
|
||||
theorem Eq.mpr_not {p q : Prop} (h₁ : p = q) (h₂ : ¬q) : ¬p :=
|
||||
h₁ ▸ h₂
|
||||
|
||||
@[congr]
|
||||
theorem dite_congr {_ : Decidable b} [Decidable c]
|
||||
{x : b → α} {u : c → α} {y : ¬b → α} {v : ¬c → α}
|
||||
(h₁ : b = c)
|
||||
(h₂ : (h : c) → x (h₁.mpr_prop h) = u h)
|
||||
(h₃ : (h : ¬c) → y (h₁.mpr_not h) = v h) :
|
||||
dite b x y = dite c u v := by
|
||||
theorem dite_congr {s : Decidable b} [Decidable c]
|
||||
{x : b → α} {u : c → α} {y : ¬b → α} {v : ¬c → α}
|
||||
(h₁ : b = c)
|
||||
(h₂ : (h : c) → x (Eq.mpr_prop h₁ h) = u h)
|
||||
(h₃ : (h : ¬c) → y (Eq.mpr_not h₁ h) = v h)
|
||||
: dite b x y = dite c u v := by
|
||||
cases Decidable.em c with
|
||||
| inl h => rw [dif_pos h]; subst b; rw [dif_pos h]; exact h₂ h
|
||||
| inr h => rw [dif_neg h]; subst b; rw [dif_neg h]; exact h₃ h
|
||||
|
||||
@[simp] theorem ne_eq (a b : α) : (a ≠ b) = ¬(a = b) := rfl
|
||||
@[simp] theorem ne_eq (a b : α) : (a ≠ b) = Not (a = b) := rfl
|
||||
@[simp] theorem ite_true (a b : α) : (if True then a else b) = a := rfl
|
||||
@[simp] theorem ite_false (a b : α) : (if False then a else b) = b := rfl
|
||||
@[simp] theorem dite_true {α : Sort u} {t : True → α} {e : ¬ True → α} : (dite True t e) = t True.intro := rfl
|
||||
@[simp] theorem dite_false {α : Sort u} {t : False → α} {e : ¬ False → α} : (dite False t e) = e not_false := rfl
|
||||
@[simp] theorem ite_self {α : Sort u} {c : Prop} {d : Decidable c} (a : α) : ite c a a = a := by cases d <;> rfl
|
||||
@[simp] theorem and_self (p : Prop) : (p ∧ p) = p := propext ⟨(·.1), fun h => ⟨h, h⟩⟩
|
||||
@[simp] theorem and_true (p : Prop) : (p ∧ True) = p := propext ⟨(·.1), (⟨·, trivial⟩)⟩
|
||||
@[simp] theorem true_and (p : Prop) : (True ∧ p) = p := propext ⟨(·.2), (⟨trivial, ·⟩)⟩
|
||||
@[simp] theorem and_false (p : Prop) : (p ∧ False) = False := eq_false (·.2)
|
||||
@[simp] theorem false_and (p : Prop) : (False ∧ p) = False := eq_false (·.1)
|
||||
@[simp] theorem or_self (p : Prop) : (p ∨ p) = p := propext ⟨fun | .inl h | .inr h => h, .inl⟩
|
||||
@[simp] theorem or_true (p : Prop) : (p ∨ True) = True := eq_true (.inr trivial)
|
||||
@[simp] theorem true_or (p : Prop) : (True ∨ p) = True := eq_true (.inl trivial)
|
||||
@[simp] theorem or_false (p : Prop) : (p ∨ False) = p := propext ⟨fun (.inl h) => h, .inl⟩
|
||||
@[simp] theorem false_or (p : Prop) : (False ∨ p) = p := propext ⟨fun (.inr h) => h, .inr⟩
|
||||
@[simp] theorem iff_self (p : Prop) : (p ↔ p) = True := eq_true .rfl
|
||||
@[simp] theorem iff_true (p : Prop) : (p ↔ True) = p := propext ⟨(·.2 trivial), fun h => ⟨fun _ => trivial, fun _ => h⟩⟩
|
||||
@[simp] theorem true_iff (p : Prop) : (True ↔ p) = p := propext ⟨(·.1 trivial), fun h => ⟨fun _ => h, fun _ => trivial⟩⟩
|
||||
@[simp] theorem iff_false (p : Prop) : (p ↔ False) = ¬p := propext ⟨(·.1), (⟨·, False.elim⟩)⟩
|
||||
@[simp] theorem false_iff (p : Prop) : (False ↔ p) = ¬p := propext ⟨(·.2), (⟨False.elim, ·⟩)⟩
|
||||
@[simp] theorem false_implies (p : Prop) : (False → p) = True := eq_true False.elim
|
||||
@[simp] theorem implies_true (α : Sort u) : (α → True) = True := eq_true fun _ => trivial
|
||||
@[simp] theorem true_implies (p : Prop) : (True → p) = p := propext ⟨(· trivial), (fun _ => ·)⟩
|
||||
@[simp] theorem ite_self {α : Sort u} {c : Prop} {d : Decidable c} (a : α) : ite c a a = a := by cases d <;> rfl
|
||||
@[simp] theorem and_self (p : Prop) : (p ∧ p) = p := propext <| Iff.intro (fun h => h.1) (fun h => ⟨h, h⟩)
|
||||
@[simp] theorem and_true (p : Prop) : (p ∧ True) = p := propext <| Iff.intro (fun h => h.1) (fun h => ⟨h, trivial⟩)
|
||||
@[simp] theorem true_and (p : Prop) : (True ∧ p) = p := propext <| Iff.intro (fun h => h.2) (fun h => ⟨trivial, h⟩)
|
||||
@[simp] theorem and_false (p : Prop) : (p ∧ False) = False := propext <| Iff.intro (fun h => h.2) (fun h => False.elim h)
|
||||
@[simp] theorem false_and (p : Prop) : (False ∧ p) = False := propext <| Iff.intro (fun h => h.1) (fun h => False.elim h)
|
||||
@[simp] theorem or_self (p : Prop) : (p ∨ p) = p := propext <| Iff.intro (fun | Or.inl h => h | Or.inr h => h) (fun h => Or.inl h)
|
||||
@[simp] theorem or_true (p : Prop) : (p ∨ True) = True := propext <| Iff.intro (fun _ => trivial) (fun _ => Or.inr trivial)
|
||||
@[simp] theorem true_or (p : Prop) : (True ∨ p) = True := propext <| Iff.intro (fun _ => trivial) (fun _ => Or.inl trivial)
|
||||
@[simp] theorem or_false (p : Prop) : (p ∨ False) = p := propext <| Iff.intro (fun | Or.inl h => h | Or.inr h => False.elim h) (fun h => Or.inl h)
|
||||
@[simp] theorem false_or (p : Prop) : (False ∨ p) = p := propext <| Iff.intro (fun | Or.inr h => h | Or.inl h => False.elim h) (fun h => Or.inr h)
|
||||
@[simp] theorem iff_self (p : Prop) : (p ↔ p) = True := propext <| Iff.intro (fun _ => trivial) (fun _ => Iff.intro id id)
|
||||
@[simp] theorem iff_true (p : Prop) : (p ↔ True) = p := propext <| Iff.intro (fun h => h.mpr trivial) (fun h => Iff.intro (fun _ => trivial) (fun _ => h))
|
||||
@[simp] theorem true_iff (p : Prop) : (True ↔ p) = p := propext <| Iff.intro (fun h => h.mp trivial) (fun h => Iff.intro (fun _ => h) (fun _ => trivial))
|
||||
@[simp] theorem iff_false (p : Prop) : (p ↔ False) = ¬p := propext <| Iff.intro (fun h hp => h.mp hp) (fun h => Iff.intro h False.elim)
|
||||
@[simp] theorem false_iff (p : Prop) : (False ↔ p) = ¬p := propext <| Iff.intro (fun h hp => h.mpr hp) (fun h => Iff.intro False.elim h)
|
||||
@[simp] theorem false_implies (p : Prop) : (False → p) = True := propext <| Iff.intro (fun _ => trivial) (by intros; trivial)
|
||||
@[simp] theorem implies_true (α : Sort u) : (α → True) = True := propext <| Iff.intro (fun _ => trivial) (by intros; trivial)
|
||||
@[simp] theorem true_implies (p : Prop) : (True → p) = p := propext <| Iff.intro (fun h => h trivial) (by intros; trivial)
|
||||
|
||||
@[simp] theorem Bool.or_false (b : Bool) : (b || false) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.or_true (b : Bool) : (b || true) = true := by cases b <;> rfl
|
||||
@[simp] theorem Bool.false_or (b : Bool) : (false || b) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.true_or (b : Bool) : (true || b) = true := by cases b <;> rfl
|
||||
@[simp] theorem Bool.or_self (b : Bool) : (b || b) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.or_eq_true (a b : Bool) : ((a || b) = true) = (a = true ∨ b = true) := by
|
||||
cases a <;> cases b <;> decide
|
||||
@[simp] theorem Bool.or_eq_true (a b : Bool) : ((a || b) = true) = (a = true ∨ b = true) := by cases a <;> cases b <;> decide
|
||||
|
||||
@[simp] theorem Bool.and_false (b : Bool) : (b && false) = false := by cases b <;> rfl
|
||||
@[simp] theorem Bool.and_true (b : Bool) : (b && true) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.false_and (b : Bool) : (false && b) = false := by cases b <;> rfl
|
||||
@[simp] theorem Bool.true_and (b : Bool) : (true && b) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.and_self (b : Bool) : (b && b) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.and_eq_true (a b : Bool) : ((a && b) = true) = (a = true ∧ b = true) := by
|
||||
cases a <;> cases b <;> decide
|
||||
|
||||
theorem Bool.and_assoc (a b c : Bool) : (a && b && c) = (a && (b && c)) := by
|
||||
cases a <;> cases b <;> cases c <;> decide
|
||||
theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
|
||||
cases a <;> cases b <;> cases c <;> decide
|
||||
@[simp] theorem Bool.and_eq_true (a b : Bool) : ((a && b) = true) = (a = true ∧ b = true) := by cases a <;> cases b <;> decide
|
||||
|
||||
@[simp] theorem Bool.not_not (b : Bool) : (!!b) = b := by cases b <;> rfl
|
||||
@[simp] theorem Bool.not_true : (!true) = false := by decide
|
||||
@@ -125,13 +138,11 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
|
||||
@[simp] theorem Bool.not_beq_true (b : Bool) : (!(b == true)) = (b == false) := by cases b <;> rfl
|
||||
@[simp] theorem Bool.not_beq_false (b : Bool) : (!(b == false)) = (b == true) := by cases b <;> rfl
|
||||
|
||||
@[simp] theorem Bool.beq_to_eq (a b : Bool) :
|
||||
(a == b) = (a = b) := by cases a <;> cases b <;> decide
|
||||
@[simp] theorem Bool.not_beq_to_not_eq (a b : Bool) :
|
||||
(!(a == b)) = ¬(a = b) := by cases a <;> cases b <;> decide
|
||||
@[simp] theorem Bool.beq_to_eq (a b : Bool) : ((a == b) = true) = (a = b) := by cases a <;> cases b <;> decide
|
||||
@[simp] theorem Bool.not_beq_to_not_eq (a b : Bool) : ((!(a == b)) = true) = ¬(a = b) := by cases a <;> cases b <;> decide
|
||||
|
||||
@[simp] theorem Bool.not_eq_true (b : Bool) : (¬(b = true)) = (b = false) := by cases b <;> decide
|
||||
@[simp] theorem Bool.not_eq_false (b : Bool) : (¬(b = false)) = (b = true) := by cases b <;> decide
|
||||
@[simp] theorem Bool.not_eq_true (b : Bool) : (¬ (b = true)) = (b = false) := by cases b <;> decide
|
||||
@[simp] theorem Bool.not_eq_false (b : Bool) : (¬ (b = false)) = (b = true) := by cases b <;> decide
|
||||
|
||||
@[simp] theorem decide_eq_true_eq [Decidable p] : (decide p = true) = p := propext <| Iff.intro of_decide_eq_true decide_eq_true
|
||||
@[simp] theorem decide_not [h : Decidable p] : decide (¬ p) = !decide p := by cases h <;> rfl
|
||||
@@ -149,7 +160,7 @@ theorem Bool.or_assoc (a b c : Bool) : (a || b || c) = (a || (b || c)) := by
|
||||
@[simp] theorem bne_self_eq_false' [DecidableEq α] (a : α) : (a != a) = false := by simp [bne]
|
||||
|
||||
@[simp] theorem Nat.le_zero_eq (a : Nat) : (a ≤ 0) = (a = 0) :=
|
||||
propext ⟨fun h => Nat.le_antisymm h (Nat.zero_le ..), fun h => by simp [h]⟩
|
||||
propext <| Iff.intro (fun h => Nat.le_antisymm h (Nat.zero_le ..)) (fun h => by simp [h])
|
||||
|
||||
@[simp] theorem decide_False : decide False = false := rfl
|
||||
@[simp] theorem decide_True : decide True = true := rfl
|
||||
|
||||
@@ -1,43 +1,24 @@
|
||||
/-
|
||||
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Mario Carneiro
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Tactics
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
|
||||
/-! # SizeOf -/
|
||||
/- SizeOf -/
|
||||
|
||||
/--
|
||||
`SizeOf` is a typeclass automatically derived for every inductive type,
|
||||
which equips the type with a "size" function to `Nat`.
|
||||
The default instance defines each constructor to be `1` plus the sum of the
|
||||
sizes of all the constructor fields.
|
||||
|
||||
This is used for proofs by well-founded induction, since every field of the
|
||||
constructor has a smaller size than the constructor itself,
|
||||
and in many cases this will suffice to do the proof that a recursive function
|
||||
is only called on smaller values.
|
||||
If the default proof strategy fails, it is recommended to supply a custom
|
||||
size measure using the `termination_by` argument on the function definition.
|
||||
-/
|
||||
class SizeOf (α : Sort u) where
|
||||
/-- The "size" of an element, a natural number which decreases on fields of
|
||||
each inductive type. -/
|
||||
sizeOf : α → Nat
|
||||
|
||||
export SizeOf (sizeOf)
|
||||
|
||||
/-!
|
||||
Declare `SizeOf` instances and theorems for types declared before `SizeOf`.
|
||||
From now on, the inductive compiler will automatically generate `SizeOf` instances and theorems.
|
||||
/-
|
||||
Declare sizeOf instances and theorems for types declared before SizeOf.
|
||||
From now on, the inductive Compiler will automatically generate sizeOf instances and theorems.
|
||||
-/
|
||||
|
||||
/--
|
||||
Every type `α` has a default `SizeOf` instance that just returns `0`
|
||||
for every element of `α`.
|
||||
-/
|
||||
/- Every Type `α` has a default SizeOf instance that just returns 0 for every element of `α` -/
|
||||
protected def default.sizeOf (α : Sort u) : α → Nat
|
||||
| _ => 0
|
||||
|
||||
@@ -57,28 +38,22 @@ instance [SizeOf α] : SizeOf (Unit → α) where
|
||||
@[simp] theorem sizeOf_thunk [SizeOf α] (f : Unit → α) : sizeOf f = sizeOf (f ()) :=
|
||||
rfl
|
||||
|
||||
deriving instance SizeOf for PUnit
|
||||
deriving instance SizeOf for Prod
|
||||
deriving instance SizeOf for PProd
|
||||
deriving instance SizeOf for MProd
|
||||
deriving instance SizeOf for PUnit
|
||||
deriving instance SizeOf for Bool
|
||||
deriving instance SizeOf for Option
|
||||
deriving instance SizeOf for List
|
||||
deriving instance SizeOf for Array
|
||||
deriving instance SizeOf for Subtype
|
||||
deriving instance SizeOf for PLift
|
||||
deriving instance SizeOf for ULift
|
||||
deriving instance SizeOf for Decidable
|
||||
deriving instance SizeOf for Fin
|
||||
deriving instance SizeOf for USize
|
||||
deriving instance SizeOf for UInt8
|
||||
deriving instance SizeOf for UInt16
|
||||
deriving instance SizeOf for UInt32
|
||||
deriving instance SizeOf for UInt64
|
||||
deriving instance SizeOf for USize
|
||||
deriving instance SizeOf for Char
|
||||
deriving instance SizeOf for Option
|
||||
deriving instance SizeOf for List
|
||||
deriving instance SizeOf for String
|
||||
deriving instance SizeOf for String.Pos
|
||||
deriving instance SizeOf for Substring
|
||||
deriving instance SizeOf for Array
|
||||
deriving instance SizeOf for Except
|
||||
deriving instance SizeOf for EStateM.Result
|
||||
|
||||
@@ -87,10 +62,8 @@ deriving instance SizeOf for EStateM.Result
|
||||
|
||||
namespace Lean
|
||||
|
||||
/--
|
||||
We manually define the `Lean.Name` instance because we use
|
||||
an opaque function for computing the hashcode field.
|
||||
-/
|
||||
/- We manually define `Lean.Name` instance because we use
|
||||
an opaque function for computing the hashcode field. -/
|
||||
protected noncomputable def Name.sizeOf : Name → Nat
|
||||
| anonymous => 1
|
||||
| str p s => 1 + Name.sizeOf p + sizeOf s
|
||||
@@ -106,16 +79,6 @@ noncomputable instance : SizeOf Name where
|
||||
@[simp] theorem Name.num.sizeOf_spec (p : Name) (n : Nat) : sizeOf (num p n) = 1 + sizeOf p + sizeOf n :=
|
||||
rfl
|
||||
|
||||
deriving instance SizeOf for SourceInfo
|
||||
deriving instance SizeOf for Syntax
|
||||
deriving instance SizeOf for TSyntax
|
||||
deriving instance SizeOf for Syntax.SepArray
|
||||
deriving instance SizeOf for Syntax.TSepArray
|
||||
deriving instance SizeOf for ParserDescr
|
||||
deriving instance SizeOf for MacroScopesView
|
||||
deriving instance SizeOf for Macro.Context
|
||||
deriving instance SizeOf for Macro.Exception
|
||||
deriving instance SizeOf for Macro.State
|
||||
deriving instance SizeOf for Macro.Methods
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -6,6 +6,3 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.System.IO
|
||||
import Init.System.Platform
|
||||
import Init.System.Uri
|
||||
import Init.System.Mutex
|
||||
import Init.System.Promise
|
||||
|
||||
@@ -91,7 +91,7 @@ set_option compiler.extract_closed false in
|
||||
@[extern "lean_io_timeit"] opaque timeit (msg : @& String) (fn : IO α) : IO α
|
||||
@[extern "lean_io_allocprof"] opaque allocprof (msg : @& String) (fn : IO α) : IO α
|
||||
|
||||
/-- Programs can execute IO actions during initialization that occurs before
|
||||
/- Programs can execute IO actions during initialization that occurs before
|
||||
the `main` function is executed. The attribute `[init <action>]` specifies
|
||||
which IO action is executed to set the value of an opaque constant.
|
||||
|
||||
@@ -172,7 +172,7 @@ def lazyPure (fn : Unit → α) : IO α :=
|
||||
If `nBytes = 0`, return immediately with an empty buffer. -/
|
||||
@[extern "lean_io_get_random_bytes"] opaque getRandomBytes (nBytes : USize) : IO ByteArray
|
||||
|
||||
def sleep (ms : UInt32) : BaseIO Unit :=
|
||||
def sleep (ms : UInt32) : IO Unit :=
|
||||
-- TODO: add a proper primitive for IO.sleep
|
||||
fun s => dbgSleep ms fun _ => EStateM.Result.ok () s
|
||||
|
||||
@@ -205,13 +205,8 @@ def sleep (ms : UInt32) : BaseIO Unit :=
|
||||
@[extern "lean_io_wait"] opaque wait (t : Task α) : BaseIO α :=
|
||||
return t.get
|
||||
|
||||
local macro "nonempty_list" : tactic =>
|
||||
`(exact Nat.zero_lt_succ _)
|
||||
|
||||
/-- Wait until any of the tasks in the given list has finished, then return its result. -/
|
||||
@[extern "lean_io_wait_any"] opaque waitAny (tasks : @& List (Task α))
|
||||
(h : tasks.length > 0 := by nonempty_list) : BaseIO α :=
|
||||
return tasks[0].get
|
||||
@[extern "lean_io_wait_any"] opaque waitAny : @& List (Task α) → IO α
|
||||
|
||||
/-- Helper method for implementing "deterministic" timeouts. It is the number of "small" memory allocations performed by the current execution thread. -/
|
||||
@[extern "lean_io_get_num_heartbeats"] opaque getNumHeartbeats : BaseIO Nat
|
||||
@@ -225,19 +220,10 @@ opaque FS.Handle : Type := Unit
|
||||
A pure-Lean abstraction of POSIX streams. We use `Stream`s for the standard streams stdin/stdout/stderr so we can
|
||||
capture output of `#eval` commands into memory. -/
|
||||
structure FS.Stream where
|
||||
isEof : IO Bool
|
||||
flush : IO Unit
|
||||
/--
|
||||
Read up to the given number of bytes from the stream.
|
||||
If the returned array is empty, an end-of-file marker has been reached.
|
||||
Note that EOF does not actually close a stream, so further reads may block and return more data.
|
||||
-/
|
||||
read : USize → IO ByteArray
|
||||
write : ByteArray → IO Unit
|
||||
/--
|
||||
Read text up to (including) the next line break from the stream.
|
||||
If the returned string is empty, an end-of-file marker has been reached.
|
||||
Note that EOF does not actually close a stream, so further reads may block and return more data.
|
||||
-/
|
||||
getLine : IO String
|
||||
putStr : String → IO Unit
|
||||
deriving Inhabited
|
||||
@@ -280,20 +266,16 @@ private def fopenFlags (m : FS.Mode) (b : Bool) : String :=
|
||||
def mk (fn : FilePath) (Mode : Mode) (bin : Bool := true) : IO Handle :=
|
||||
mkPrim fn (fopenFlags Mode bin)
|
||||
|
||||
@[extern "lean_io_prim_handle_flush"] opaque flush (h : @& Handle) : IO Unit
|
||||
/--
|
||||
Read up to the given number of bytes from the handle.
|
||||
If the returned array is empty, an end-of-file marker has been reached.
|
||||
Note that EOF does not actually close a handle, so further reads may block and return more data.
|
||||
Returns whether the end of the file has been reached while reading a file.
|
||||
`h.isEof` returns true /after/ the first attempt at reading past the end of `h`.
|
||||
Once `h.isEof` is true, reading `h` will always return an empty array.
|
||||
-/
|
||||
@[extern "lean_io_prim_handle_read"] opaque read (h : @& Handle) (bytes : USize) : IO ByteArray
|
||||
@[extern "lean_io_prim_handle_is_eof"] opaque isEof (h : @& Handle) : BaseIO Bool
|
||||
@[extern "lean_io_prim_handle_flush"] opaque flush (h : @& Handle) : IO Unit
|
||||
@[extern "lean_io_prim_handle_read"] opaque read (h : @& Handle) (bytes : USize) : IO ByteArray
|
||||
@[extern "lean_io_prim_handle_write"] opaque write (h : @& Handle) (buffer : @& ByteArray) : IO Unit
|
||||
|
||||
/--
|
||||
Read text up to (including) the next line break from the handle.
|
||||
If the returned string is empty, an end-of-file marker has been reached.
|
||||
Note that EOF does not actually close a handle, so further reads may block and return more data.
|
||||
-/
|
||||
@[extern "lean_io_prim_handle_get_line"] opaque getLine (h : @& Handle) : IO String
|
||||
@[extern "lean_io_prim_handle_put_str"] opaque putStr (h : @& Handle) (s : @& String) : IO Unit
|
||||
|
||||
@@ -529,21 +511,21 @@ def Stdio.toHandleType : Stdio → Type
|
||||
| Stdio.null => Unit
|
||||
|
||||
structure StdioConfig where
|
||||
/-- Configuration for the process' stdin handle. -/
|
||||
/- Configuration for the process' stdin handle. -/
|
||||
stdin := Stdio.inherit
|
||||
/-- Configuration for the process' stdout handle. -/
|
||||
/- Configuration for the process' stdout handle. -/
|
||||
stdout := Stdio.inherit
|
||||
/-- Configuration for the process' stderr handle. -/
|
||||
/- Configuration for the process' stderr handle. -/
|
||||
stderr := Stdio.inherit
|
||||
|
||||
structure SpawnArgs extends StdioConfig where
|
||||
/-- Command name. -/
|
||||
/- Command name. -/
|
||||
cmd : String
|
||||
/-- Arguments for the process -/
|
||||
/- Arguments for the process -/
|
||||
args : Array String := #[]
|
||||
/-- Working directory for the process. Inherit from current process if `none`. -/
|
||||
/- Working directory for the process. Inherit from current process if `none`. -/
|
||||
cwd : Option FilePath := none
|
||||
/-- Add or remove environment variables for the process. -/
|
||||
/- Add or remove environment variables for the process. -/
|
||||
env : Array (String × Option String) := #[]
|
||||
|
||||
-- TODO(Sebastian): constructor must be private
|
||||
@@ -617,7 +599,7 @@ def FileRight.flags (acc : FileRight) : UInt32 :=
|
||||
def setAccessRights (filename : FilePath) (mode : FileRight) : IO Unit :=
|
||||
Prim.setAccessRights filename mode.flags
|
||||
|
||||
/-- References -/
|
||||
/- References -/
|
||||
abbrev Ref (α : Type) := ST.Ref IO.RealWorld α
|
||||
|
||||
instance : MonadLift (ST IO.RealWorld) BaseIO := ⟨id⟩
|
||||
@@ -630,6 +612,7 @@ namespace Stream
|
||||
|
||||
@[export lean_stream_of_handle]
|
||||
def ofHandle (h : Handle) : Stream := {
|
||||
isEof := Handle.isEof h,
|
||||
flush := Handle.flush h,
|
||||
read := Handle.read h,
|
||||
write := Handle.write h,
|
||||
@@ -642,6 +625,7 @@ structure Buffer where
|
||||
pos : Nat := 0
|
||||
|
||||
def ofBuffer (r : Ref Buffer) : Stream := {
|
||||
isEof := do let b ← r.get; pure <| b.pos >= b.data.size,
|
||||
flush := pure (),
|
||||
read := fun n => r.modifyGet fun b =>
|
||||
let data := b.data.extract b.pos (b.pos + n.toNat)
|
||||
@@ -702,11 +686,6 @@ instance [Eval α] : Eval (IO α) where
|
||||
let a ← x ()
|
||||
Eval.eval fun _ => a
|
||||
|
||||
instance [Eval α] : Eval (BaseIO α) where
|
||||
eval x _ := do
|
||||
let a ← x ()
|
||||
Eval.eval fun _ => a
|
||||
|
||||
@[noinline, nospecialize] def runEval [Eval α] (a : Unit → α) : IO (String × Except IO.Error Unit) :=
|
||||
IO.FS.withIsolatedStreams (Eval.eval a false |>.toBaseIO)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import Init.Data.UInt
|
||||
import Init.Data.ToString.Basic
|
||||
import Init.Data.String.Basic
|
||||
|
||||
/--
|
||||
/-
|
||||
Imitate the structure of IOErrorType in Haskell:
|
||||
https://hackage.haskell.org/package/base-4.12.0.0/docs/System-IO-Error.html#t:IOErrorType
|
||||
-/
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Gabriel Ebner
|
||||
-/
|
||||
prelude
|
||||
import Init.System.IO
|
||||
import Init.Control.StateRef
|
||||
|
||||
namespace IO
|
||||
|
||||
private opaque BaseMutexImpl : NonemptyType.{0}
|
||||
|
||||
/--
|
||||
Mutual exclusion primitive (a lock).
|
||||
|
||||
If you want to guard shared state, use `Mutex α` instead.
|
||||
-/
|
||||
def BaseMutex : Type := BaseMutexImpl.type
|
||||
|
||||
instance : Nonempty BaseMutex := BaseMutexImpl.property
|
||||
|
||||
/-- Creates a new `BaseMutex`. -/
|
||||
@[extern "lean_io_basemutex_new"]
|
||||
opaque BaseMutex.new : BaseIO BaseMutex
|
||||
|
||||
/--
|
||||
Locks a `BaseMutex`. Waits until no other thread has locked the mutex.
|
||||
|
||||
The current thread must not have already locked the mutex.
|
||||
Reentrant locking is undefined behavior (inherited from the C++ implementation).
|
||||
-/
|
||||
@[extern "lean_io_basemutex_lock"]
|
||||
opaque BaseMutex.lock (mutex : @& BaseMutex) : BaseIO Unit
|
||||
|
||||
/--
|
||||
Unlocks a `BaseMutex`.
|
||||
|
||||
The current thread must have already locked the mutex.
|
||||
Unlocking an unlocked mutex is undefined behavior (inherited from the C++ implementation).
|
||||
-/
|
||||
@[extern "lean_io_basemutex_unlock"]
|
||||
opaque BaseMutex.unlock (mutex : @& BaseMutex) : BaseIO Unit
|
||||
|
||||
private opaque CondvarImpl : NonemptyType.{0}
|
||||
|
||||
/-- Condition variable. -/
|
||||
def Condvar : Type := CondvarImpl.type
|
||||
|
||||
instance : Nonempty Condvar := CondvarImpl.property
|
||||
|
||||
/-- Creates a new condition variable. -/
|
||||
@[extern "lean_io_condvar_new"]
|
||||
opaque Condvar.new : BaseIO Condvar
|
||||
|
||||
/-- Waits until another thread calls `notifyOne` or `notifyAll`. -/
|
||||
@[extern "lean_io_condvar_wait"]
|
||||
opaque Condvar.wait (condvar : @& Condvar) (mutex : @& BaseMutex) : BaseIO Unit
|
||||
|
||||
/-- Wakes up a single other thread executing `wait`. -/
|
||||
@[extern "lean_io_condvar_notify_one"]
|
||||
opaque Condvar.notifyOne (condvar : @& Condvar) : BaseIO Unit
|
||||
|
||||
/-- Wakes up all other threads executing `wait`. -/
|
||||
@[extern "lean_io_condvar_notify_all"]
|
||||
opaque Condvar.notifyAll (condvar : @& Condvar) : BaseIO Unit
|
||||
|
||||
/-- Waits on the condition variable until the predicate is true. -/
|
||||
def Condvar.waitUntil [Monad m] [MonadLift BaseIO m]
|
||||
(condvar : Condvar) (mutex : BaseMutex) (pred : m Bool) : m Unit := do
|
||||
while !(← pred) do
|
||||
condvar.wait mutex
|
||||
|
||||
/--
|
||||
Mutual exclusion primitive (lock) guarding shared state of type `α`.
|
||||
|
||||
The type `Mutex α` is similar to `IO.Ref α`,
|
||||
except that concurrent accesses are guarded by a mutex
|
||||
instead of atomic pointer operations and busy-waiting.
|
||||
-/
|
||||
structure Mutex (α : Type) where private mk ::
|
||||
private ref : IO.Ref α
|
||||
mutex : BaseMutex
|
||||
|
||||
instance [Nonempty α] : Nonempty (Mutex α) :=
|
||||
let ⟨ref⟩ := inferInstanceAs (Nonempty _)
|
||||
let ⟨mutex⟩ := inferInstanceAs (Nonempty _)
|
||||
⟨{ref, mutex}⟩
|
||||
|
||||
instance : Coe (Mutex α) BaseMutex where coe := Mutex.mutex
|
||||
|
||||
/-- Creates a new mutex. -/
|
||||
def Mutex.new (a : α) : BaseIO (Mutex α) :=
|
||||
return { ref := ← mkRef a, mutex := ← BaseMutex.new }
|
||||
|
||||
/--
|
||||
`AtomicT α m` is the monad that can be atomically executed inside a `Mutex α`,
|
||||
with outside monad `m`.
|
||||
The action has access to the state `α` of the mutex (via `get` and `set`).
|
||||
-/
|
||||
abbrev AtomicT := StateRefT' IO.RealWorld
|
||||
|
||||
/-- `mutex.atomically k` runs `k` with access to the mutex's state while locking the mutex. -/
|
||||
def Mutex.atomically [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
|
||||
(mutex : Mutex α) (k : AtomicT α m β) : m β := do
|
||||
try
|
||||
mutex.mutex.lock
|
||||
k mutex.ref
|
||||
finally
|
||||
mutex.mutex.unlock
|
||||
|
||||
/--
|
||||
`mutex.atomicallyOnce condvar pred k` runs `k`,
|
||||
waiting on `condvar` until `pred` returns true.
|
||||
Both `k` and `pred` have access to the mutex's state.
|
||||
-/
|
||||
def Mutex.atomicallyOnce [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
|
||||
(mutex : Mutex α) (condvar : Condvar)
|
||||
(pred : AtomicT α m Bool) (k : AtomicT α m β) : m β :=
|
||||
let _ : MonadLift BaseIO (AtomicT α m) := ⟨liftM⟩
|
||||
mutex.atomically do
|
||||
condvar.waitUntil mutex pred
|
||||
k
|
||||
@@ -1,57 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Gabriel Ebner
|
||||
-/
|
||||
prelude
|
||||
import Init.System.IO
|
||||
|
||||
namespace IO
|
||||
|
||||
/-- Internally, a `Promise` is just a `Task` that is in the "Promised" or "Finished" state. -/
|
||||
private opaque PromiseImpl (α : Type) : { P : Type // Nonempty α ↔ Nonempty P } :=
|
||||
⟨Task α, fun ⟨_⟩ => ⟨⟨‹_›⟩⟩, fun ⟨⟨_⟩⟩ => ⟨‹_›⟩⟩
|
||||
|
||||
/--
|
||||
`Promise α` allows you to create a `Task α` whose value is provided later by calling `resolve`.
|
||||
|
||||
Typical usage is as follows:
|
||||
1. `let promise ← Promise.new` creates a promise
|
||||
2. `promise.result : Task α` can now be passed around
|
||||
3. `promise.result.get` blocks until the promise is resolved
|
||||
4. `promise.resolve a` resolves the promise
|
||||
5. `promise.result.get` now returns `a`
|
||||
|
||||
Every promise must eventually be resolved.
|
||||
Otherwise the memory used for the promise will be leaked,
|
||||
and any tasks depending on the promise's result will wait forever.
|
||||
-/
|
||||
def Promise (α : Type) : Type := (PromiseImpl α).1
|
||||
|
||||
instance [Nonempty α] : Nonempty (Promise α) :=
|
||||
(PromiseImpl α).2.1 inferInstance
|
||||
|
||||
/-- Creates a new `Promise`. -/
|
||||
@[extern "lean_io_promise_new"]
|
||||
opaque Promise.new [Nonempty α] : BaseIO (Promise α)
|
||||
|
||||
/--
|
||||
Resolves a `Promise`.
|
||||
|
||||
Only the first call to this function has an effect.
|
||||
-/
|
||||
@[extern "lean_io_promise_resolve"]
|
||||
opaque Promise.resolve (value : α) (promise : @& Promise α) : BaseIO Unit
|
||||
|
||||
private unsafe def Promise.resultImpl (promise : Promise α) : Task α :=
|
||||
unsafeCast promise
|
||||
|
||||
/--
|
||||
The result task of a `Promise`.
|
||||
|
||||
The task blocks until `Promise.resolve` is called.
|
||||
-/
|
||||
@[implementedBy Promise.resultImpl]
|
||||
opaque Promise.result (promise : Promise α) : Task α :=
|
||||
have : Nonempty α := (PromiseImpl α).2.2 ⟨promise⟩
|
||||
Classical.choice inferInstance
|
||||
@@ -41,7 +41,7 @@ instance {ε σ} : MonadLift (ST σ) (EST ε σ) := ⟨fun x s =>
|
||||
|
||||
namespace ST
|
||||
|
||||
/-- References -/
|
||||
/- References -/
|
||||
opaque RefPointed : NonemptyType.{0}
|
||||
|
||||
structure Ref (σ : Type) (α : Type) : Type where
|
||||
@@ -53,7 +53,7 @@ instance {σ α} [s : Nonempty α] : Nonempty (Ref σ α) :=
|
||||
|
||||
namespace Prim
|
||||
|
||||
/-- Auxiliary definition for showing that `ST σ α` is inhabited when we have a `Ref σ α` -/
|
||||
/- Auxiliary definition for showing that `ST σ α` is inhabited when we have a `Ref σ α` -/
|
||||
private noncomputable def inhabitedFromRef {σ α} (r : Ref σ α) : ST σ α :=
|
||||
let _ : Inhabited α := Classical.inhabited_of_nonempty r.h
|
||||
pure default
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Chris Lovett
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.String.Extra
|
||||
import Init.System.FilePath
|
||||
|
||||
namespace System
|
||||
namespace Uri
|
||||
namespace UriEscape
|
||||
|
||||
/- https://www.ietf.org/rfc/rfc3986.txt -/
|
||||
@[inline] def zero : UInt8 := '0'.toNat.toUInt8
|
||||
@[inline] def nine : UInt8 := '9'.toNat.toUInt8
|
||||
@[inline] def lettera : UInt8 := 'a'.toNat.toUInt8
|
||||
@[inline] def letterf : UInt8 := 'f'.toNat.toUInt8
|
||||
@[inline] def letterA : UInt8 := 'A'.toNat.toUInt8
|
||||
@[inline] def letterF : UInt8 := 'F'.toNat.toUInt8
|
||||
|
||||
/-- Decode %HH escapings in the given string. Note that sometimes a consecutive
|
||||
sequence of multiple escapings can represet a utf-8 encoded sequence for
|
||||
a single unicode code point and these will also be decoded correctly. -/
|
||||
def decodeUri (uri : String) : String := Id.run do
|
||||
let mut decoded : ByteArray := ByteArray.empty
|
||||
let rawBytes := uri.toUTF8
|
||||
let len := rawBytes.size
|
||||
let mut i := 0
|
||||
let percent := '%'.toNat.toUInt8
|
||||
while i < len do
|
||||
let c := rawBytes[i]!
|
||||
(decoded, i) := if c == percent && i + 1 < len then
|
||||
let h1 := rawBytes[i + 1]!
|
||||
if let some hd1 := hexDigitToUInt8? h1 then
|
||||
if i + 2 < len then
|
||||
let h2 := rawBytes[i + 2]!
|
||||
if let some hd2 := hexDigitToUInt8? h2 then
|
||||
-- decode the hex digits into a byte.
|
||||
(decoded.push (hd1 * 16 + hd2), i + 3)
|
||||
else
|
||||
-- not a valid second hex digit so keep the original bytes
|
||||
(((decoded.push c).push h1).push h2, i + 3)
|
||||
else
|
||||
-- hit end of string, there is no h2.
|
||||
((decoded.push c).push h1, i + 2)
|
||||
else
|
||||
-- not a valid hex digit so keep the original bytes
|
||||
((decoded.push c).push h1, i + 2)
|
||||
else
|
||||
(decoded.push c, i + 1)
|
||||
return String.fromUTF8Unchecked decoded
|
||||
where hexDigitToUInt8? (c : UInt8) : Option UInt8 :=
|
||||
if zero ≤ c ∧ c ≤ nine then some (c - zero)
|
||||
else if lettera ≤ c ∧ c ≤ letterf then some (c - lettera + 10)
|
||||
else if letterA ≤ c ∧ c ≤ letterF then some (c - letterA + 10)
|
||||
else none
|
||||
|
||||
def rfc3986ReservedChars : List Char := [ ';', ':', '?', '#', '[', ']', '@', '&', '=', '+', '$', ',', '!', '\'', '(', ')', '*', '%', ' ' ]
|
||||
|
||||
def uriEscapeAsciiChar (c : Char) : String :=
|
||||
if rfc3986ReservedChars.contains c || c < ' ' then
|
||||
"%" ++ uInt8ToHex c.toNat.toUInt8
|
||||
else if (Char.toNat c) < 127 then
|
||||
c.toString
|
||||
else
|
||||
c.toString.toUTF8.foldl (fun s b => s ++ "%" ++ (uInt8ToHex b)) ""
|
||||
where
|
||||
uInt8ToHex (c : UInt8) : String :=
|
||||
let d2 := c / 16;
|
||||
let d1 := c % 16;
|
||||
(hexDigitRepr d2.toNat ++ hexDigitRepr d1.toNat).toUpper
|
||||
end UriEscape
|
||||
|
||||
/-- Replaces special characters in the given Uri with %HH Uri escapings. -/
|
||||
def escapeUri (uri: String) : String :=
|
||||
uri.foldl (fun s c => s ++ UriEscape.uriEscapeAsciiChar c) ""
|
||||
|
||||
/-- Replaces all %HH Uri escapings in the given string with their
|
||||
corresponding unicode code points. Note that sometimes a consecutive
|
||||
sequence of multiple escapings can represet a utf-8 encoded sequence for
|
||||
a single unicode code point and these will also be decoded correctly. -/
|
||||
def unescapeUri (s: String) : String :=
|
||||
UriEscape.decodeUri s
|
||||
|
||||
/-- Convert the given FilePath to a "file:///encodedpath" Uri
|
||||
where the encoded path may contain %xx escaping when needed. -/
|
||||
def pathToUri (fname : System.FilePath) : String := Id.run do
|
||||
let mut uri := fname.normalize.toString
|
||||
if System.Platform.isWindows then
|
||||
uri := uri.map (fun c => if c == '\\' then '/' else c)
|
||||
uri := uri.foldl (fun s c => s ++ UriEscape.uriEscapeAsciiChar c) ""
|
||||
let result := if uri.startsWith "/" then "file://" ++ uri else "file:///" ++ uri
|
||||
result
|
||||
|
||||
/-- Convert the given uri to a FilePath stripping the 'file://' prefix,
|
||||
ignoring the optional host name and unescaping any %HH escaped chars.
|
||||
It is also careful to create correct paths on Windows that start with a drive letter. -/
|
||||
def fileUriToPath? (uri : String) : Option System.FilePath := Id.run do
|
||||
if !uri.startsWith "file://" then
|
||||
none
|
||||
else
|
||||
let mut p := (unescapeUri uri).drop "file://".length
|
||||
p := p.dropWhile (λ c => c != '/') -- drop the hostname.
|
||||
-- on windows the path "/c:/temp" needs to become "c:/temp"
|
||||
-- but only when it is a valid drive letter.
|
||||
if System.Platform.isWindows &&
|
||||
p.length > 3 &&
|
||||
"/" == (p.take 1) &&
|
||||
((p.drop 1).take 1).all Char.isAlpha &&
|
||||
":" == ((p.drop 2).take 1) then
|
||||
p := p.drop 1
|
||||
some p
|
||||
|
||||
end Uri
|
||||
end System
|
||||
@@ -1,37 +1,25 @@
|
||||
/-
|
||||
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura, Mario Carneiro
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Notation
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
|
||||
namespace Lean
|
||||
|
||||
/--
|
||||
`binderIdent` matches an `ident` or a `_`. It is used for identifiers in binding
|
||||
position, where `_` means that the value should be left unnamed and inaccessible.
|
||||
-/
|
||||
syntax binderIdent := ident <|> hole
|
||||
syntax binderIdent := ident <|> "_"
|
||||
|
||||
namespace Parser.Tactic
|
||||
/--
|
||||
`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 tactic : tactic
|
||||
/-- `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 tactic : tactic
|
||||
|
||||
/--
|
||||
Introduces one or more hypotheses, optionally naming and/or pattern-matching them.
|
||||
For each hypothesis to be introduced, the remaining main goal's target type must
|
||||
be a `let` or function type.
|
||||
|
||||
* `intro` by itself introduces one anonymous hypothesis, which can be accessed
|
||||
by e.g. `assumption`.
|
||||
* `intro x y` introduces two hypotheses and names them. Individual hypotheses
|
||||
can be anonymized via `_`, or matched against a pattern:
|
||||
Introduce one or more hypotheses, optionally naming and/or pattern-matching them.
|
||||
For each hypothesis to be introduced, the remaining main goal's target type must be a `let` or function type.
|
||||
* `intro` by itself introduces one anonymous hypothesis, which can be accessed by e.g. `assumption`.
|
||||
* `intro x y` introduces two hypotheses and names them. Individual hypotheses can be anonymized via `_`,
|
||||
or matched against a pattern:
|
||||
```lean
|
||||
-- ... ⊢ α × β → ...
|
||||
intro (a, b)
|
||||
@@ -45,49 +33,29 @@ be a `let` or function type.
|
||||
```
|
||||
-/
|
||||
syntax (name := intro) "intro " notFollowedBy("|") (colGt term:max)* : tactic
|
||||
|
||||
/-- `intros x...` behaves like `intro x...`, but then keeps introducing (anonymous) hypotheses until goal is not of a function type. -/
|
||||
syntax (name := intros) "intros " (colGt (ident <|> "_"))* : tactic
|
||||
/--
|
||||
`intros x...` behaves like `intro x...`, but then keeps introducing (anonymous)
|
||||
hypotheses until goal is not of a function type.
|
||||
-/
|
||||
syntax (name := intros) "intros " (colGt (ident <|> hole))* : tactic
|
||||
|
||||
/--
|
||||
`rename t => x` renames the most recent hypothesis whose type matches `t`
|
||||
(which may contain placeholders) to `x`, or fails if no such hypothesis could be found.
|
||||
-/
|
||||
`rename t => x` renames the most recent hypothesis whose type matches `t` (which may contain placeholders) to `x`,
|
||||
or fails if no such hypothesis could be found. -/
|
||||
syntax (name := rename) "rename " term " => " ident : tactic
|
||||
|
||||
/--
|
||||
`revert x...` is the inverse of `intro x...`: it moves the given hypotheses
|
||||
into the main goal's target type.
|
||||
-/
|
||||
/-- `revert x...` is the inverse of `intro x...`: it moves the given hypotheses into the main goal's target type. -/
|
||||
syntax (name := revert) "revert " (colGt term:max)+ : tactic
|
||||
|
||||
/--
|
||||
`clear x...` removes the given hypotheses, or fails if there are remaining
|
||||
references to a hypothesis.
|
||||
-/
|
||||
/-- `clear x...` removes the given hypotheses, or fails if there are remaining references to a hypothesis. -/
|
||||
syntax (name := clear) "clear " (colGt term:max)+ : tactic
|
||||
|
||||
/--
|
||||
`subst x...` substitutes each `x` with `e` in the goal if there is a hypothesis
|
||||
of type `x = e` or `e = x`.
|
||||
If `x` is itself a hypothesis of type `y = e` or `e = y`, `y` is substituted instead.
|
||||
-/
|
||||
`subst x...` substitutes each `x` with `e` in the goal if there is a hypothesis of type `x = e` or `e = x`.
|
||||
If `x` is itself a hypothesis of type `y = e` or `e = y`, `y` is substituted instead. -/
|
||||
syntax (name := subst) "subst " (colGt term:max)+ : tactic
|
||||
|
||||
/--
|
||||
Applies `subst` to all hypotheses of the form `h : x = t` or `h : t = x`.
|
||||
Apply `subst` to all hypotheses of the form `h : x = t` or `h : t = x`.
|
||||
-/
|
||||
syntax (name := substVars) "subst_vars" : tactic
|
||||
|
||||
/--
|
||||
`assumption` tries to solve the main goal using a hypothesis of compatible type, or else fails.
|
||||
Note also the `‹t›` term notation, which is a shorthand for `show t by assumption`.
|
||||
-/
|
||||
Note also the `‹t›` term notation, which is a shorthand for `show t by assumption`. -/
|
||||
syntax (name := assumption) "assumption" : tactic
|
||||
|
||||
/--
|
||||
`contradiction` closes the main goal if its hypotheses are "trivially contradictory".
|
||||
- Inductive type/family with no applicable constructors
|
||||
@@ -112,142 +80,73 @@ example (x : Nat) (h : x ≠ x) : p := by contradiction
|
||||
```
|
||||
-/
|
||||
syntax (name := contradiction) "contradiction" : tactic
|
||||
|
||||
/--
|
||||
`apply e` tries to match the current goal against the conclusion of `e`'s type.
|
||||
If it succeeds, then the tactic returns as many subgoals as the number of premises that
|
||||
have not been fixed by type inference or type class resolution.
|
||||
Non-dependent premises are added before dependent ones.
|
||||
|
||||
The `apply` tactic uses higher-order pattern matching, type class resolution,
|
||||
and first-order unification with dependent types.
|
||||
The `apply` tactic uses higher-order pattern matching, type class resolution, and first-order unification with dependent types.
|
||||
-/
|
||||
syntax (name := apply) "apply " term : tactic
|
||||
|
||||
/--
|
||||
`exact e` closes the main goal if its target type matches that of `e`.
|
||||
-/
|
||||
syntax (name := exact) "exact " term : tactic
|
||||
|
||||
/--
|
||||
`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`)
|
||||
holes in `e` that are not solved by unification with the main goal's target type
|
||||
are converted into new goals, using the hole's name, if any, as the goal case name.
|
||||
`refine e` behaves like `exact e`, except that named (`?x`) or unnamed (`?_`) holes in `e` that are not solved
|
||||
by unification with the main goal's target type are converted into new goals, using the hole's name, if any, as the goal case name.
|
||||
-/
|
||||
syntax (name := refine) "refine " term : tactic
|
||||
|
||||
/--
|
||||
`refine' e` behaves like `refine e`, except that unsolved placeholders (`_`)
|
||||
and implicit parameters are also converted into new goals.
|
||||
-/
|
||||
/-- `refine' e` behaves like `refine e`, except that unsolved placeholders (`_`) and implicit parameters are also converted into new goals. -/
|
||||
syntax (name := refine') "refine' " term : tactic
|
||||
|
||||
/--
|
||||
If the main goal's target type is an inductive type, `constructor` solves it with
|
||||
the first matching constructor, or else fails.
|
||||
-/
|
||||
/-- If the main goal's target type is an inductive type, `constructor` solves it with the first matching constructor, or else fails. -/
|
||||
syntax (name := constructor) "constructor" : tactic
|
||||
|
||||
/--
|
||||
* `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 => 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. -/
|
||||
syntax (name := case) "case " binderIdent binderIdent* " => " tacticSeq : tactic
|
||||
/--
|
||||
`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
|
||||
`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.
|
||||
-/
|
||||
syntax (name := case') "case' " binderIdent binderIdent* " => " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`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.
|
||||
-/
|
||||
`next => tac` focuses on the next goal 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:binderIdent* " => " tac:tacticSeq : tactic => `(tactic| case _ $args* => $tac)
|
||||
|
||||
/-- `all_goals tac` runs `tac` on each goal, concatenating the resulting goals, if any. -/
|
||||
syntax (name := allGoals) "all_goals " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`any_goals tac` applies the tactic `tac` to every goal, and succeeds if at
|
||||
least one application succeeds.
|
||||
-/
|
||||
/-- `any_goals tac` applies the tactic `tac` to every goal, and succeeds if at least one application succeeds. -/
|
||||
syntax (name := anyGoals) "any_goals " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`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.
|
||||
-/
|
||||
Usually `· tac`, which enforces that the goal is closed by `tac`, should be preferred. -/
|
||||
syntax (name := focus) "focus " tacticSeq : tactic
|
||||
|
||||
/-- `skip` does nothing. -/
|
||||
syntax (name := skip) "skip" : tactic
|
||||
|
||||
/-- `done` succeeds iff there are no remaining goals. -/
|
||||
syntax (name := done) "done" : tactic
|
||||
|
||||
/-- `trace_state` displays the current state in the info view. -/
|
||||
syntax (name := traceState) "trace_state" : tactic
|
||||
|
||||
/-- `trace msg` displays `msg` in the info view. -/
|
||||
syntax (name := traceMessage) "trace " str : tactic
|
||||
|
||||
/-- `fail_if_success t` fails if the tactic `t` succeeds. -/
|
||||
syntax (name := failIfSuccess) "fail_if_success " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`(tacs)` executes a list of tactics in sequence, without requiring that
|
||||
the goal be closed at the end like `· tacs`. Like `by` itself, the tactics
|
||||
can be either separated by newlines or `;`.
|
||||
-/
|
||||
syntax (name := paren) "(" tacticSeq ")" : tactic
|
||||
|
||||
/--
|
||||
`with_reducible tacs` excutes `tacs` using the reducible transparency setting.
|
||||
In this setting only definitions tagged as `[reducible]` are unfolded.
|
||||
-/
|
||||
syntax (name := withReducible) "with_reducible " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`with_reducible_and_instances tacs` excutes `tacs` using the `.instances` transparency setting.
|
||||
In this setting only definitions tagged as `[reducible]` or type class instances are unfolded.
|
||||
-/
|
||||
syntax (name := withReducibleAndInstances) "with_reducible_and_instances " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`with_unfolding_all tacs` excutes `tacs` using the `.all` transparency setting.
|
||||
In this setting all definitions that are not opaque are unfolded.
|
||||
-/
|
||||
syntax (name := withUnfoldingAll) "with_unfolding_all " tacticSeq : tactic
|
||||
|
||||
/-- `first | tac | ...` runs each `tac` until one succeeds, or else fails. -/
|
||||
syntax (name := first) "first " withPosition((colGe "|" tacticSeq)+) : tactic
|
||||
|
||||
/--
|
||||
`rotate_left n` rotates goals to the left by `n`. That is, `rotate_left 1`
|
||||
takes the main goal and puts it to the back of the subgoal list.
|
||||
If `n` is omitted, it defaults to `1`.
|
||||
-/
|
||||
syntax (name := rotateLeft) "rotate_left" (num)? : tactic
|
||||
|
||||
/--
|
||||
Rotate the goals to the right by `n`. That is, take the goal at the back
|
||||
and push it to the front `n` times. If `n` is omitted, it defaults to `1`.
|
||||
-/
|
||||
syntax (name := rotateRight) "rotate_right" (num)? : tactic
|
||||
|
||||
/-- `try tac` runs `tac` and succeeds even if `tac` failed. -/
|
||||
macro "try " t:tacticSeq : tactic => `(first | $t | skip)
|
||||
|
||||
/--
|
||||
`tac <;> tac'` runs `tac` on the main goal and `tac'` on each produced goal,
|
||||
concatenating all goals produced by `tac'`.
|
||||
-/
|
||||
/-- `tac <;> tac'` runs `tac` on the main goal and `tac'` on each produced goal, concatenating all goals produced by `tac'`. -/
|
||||
macro:1 x:tactic tk:" <;> " y:tactic:0 : tactic => `(tactic|
|
||||
focus
|
||||
$x:tactic
|
||||
@@ -266,341 +165,188 @@ for new reflexive relations.
|
||||
macro "rfl" : tactic => `(eq_refl)
|
||||
|
||||
/--
|
||||
`rfl'` is similar to `rfl`, but disables smart unfolding and unfolds all kinds of definitions,
|
||||
theorems included (relevant for declarations defined by well-founded recursion).
|
||||
-/
|
||||
`rfl'` is similar to `rfl`, but disables smart unfolding and unfolds all kinds of definitions,
|
||||
theorems included (relevant for declarations defined by well-founded recursion). -/
|
||||
macro "rfl'" : tactic => `(set_option smartUnfolding false in with_unfolding_all rfl)
|
||||
|
||||
/--
|
||||
`ac_rfl` proves equalities up to application of an associative and commutative operator.
|
||||
```
|
||||
instance : IsAssociative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : IsCommutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
|
||||
example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by ac_rfl
|
||||
```
|
||||
-/
|
||||
syntax (name := acRfl) "ac_rfl" : tactic
|
||||
|
||||
/--
|
||||
The `sorry` tactic closes the goal using `sorryAx`. This is intended for stubbing out incomplete
|
||||
parts of a proof while still having a syntactically correct proof skeleton. Lean will give
|
||||
a warning whenever a proof uses `sorry`, so you aren't likely to miss it, but
|
||||
you can double check if a theorem depends on `sorry` by using
|
||||
`#print axioms my_thm` and looking for `sorryAx` in the axiom list.
|
||||
-/
|
||||
macro "sorry" : tactic => `(exact @sorryAx _ false)
|
||||
syntax (name := ac_refl) "ac_refl " : tactic
|
||||
|
||||
/-- `admit` is a shorthand for `exact sorry`. -/
|
||||
macro "admit" : tactic => `(exact @sorryAx _ false)
|
||||
|
||||
/--
|
||||
`infer_instance` is an abbreviation for `exact inferInstance`.
|
||||
It synthesizes a value of any target type by typeclass inference.
|
||||
-/
|
||||
/-- The `sorry` tactic is a shorthand for `exact sorry`. -/
|
||||
macro "sorry" : tactic => `(exact @sorryAx _ false)
|
||||
/-- `infer_instance` is an abbreviation for `exact inferInstance` -/
|
||||
macro "infer_instance" : tactic => `(exact inferInstance)
|
||||
|
||||
/-- Optional configuration option for tactics -/
|
||||
syntax config := atomic("(" &"config") " := " term ")"
|
||||
|
||||
/-- The `*` location refers to all hypotheses and the goal. -/
|
||||
syntax locationWildcard := "*"
|
||||
syntax locationHyp := (colGt term:max)+ ("⊢" <|> "|-")?
|
||||
syntax location := withPosition(" at " (locationWildcard <|> locationHyp))
|
||||
|
||||
/--
|
||||
A hypothesis location specification consists of 1 or more hypothesis references
|
||||
and optionally `⊢` denoting the goal.
|
||||
-/
|
||||
syntax locationHyp := (colGt term:max)+ ("⊢" <|> "|-")?
|
||||
|
||||
/--
|
||||
Location specifications are used by many tactics that can operate on either the
|
||||
hypotheses or the goal. It can have one of the forms:
|
||||
* 'empty' is not actually present in this syntax, but most tactics use
|
||||
`(location)?` matchers. It means to target the goal only.
|
||||
* `at h₁ ... hₙ`: target the hypotheses `h₁`, ..., `hₙ`
|
||||
* `at h₁ h₂ ⊢`: target the hypotheses `h₁` and `h₂`, and the goal
|
||||
* `at *`: target all hypotheses and the goal
|
||||
-/
|
||||
syntax location := withPosition(" at " (locationWildcard <|> locationHyp))
|
||||
|
||||
/--
|
||||
* `change tgt'` will change the goal from `tgt` to `tgt'`,
|
||||
assuming these are definitionally equal.
|
||||
* `change t' at h` will change hypothesis `h : t` to have type `t'`, assuming
|
||||
assuming `t` and `t'` are definitionally equal.
|
||||
-/
|
||||
syntax (name := change) "change " term (location)? : tactic
|
||||
|
||||
/--
|
||||
* `change a with b` will change occurrences of `a` to `b` in the goal,
|
||||
assuming `a` and `b` are are definitionally equal.
|
||||
* `change a with b at h` similarly changes `a` to `b` in the type of hypothesis `h`.
|
||||
-/
|
||||
syntax (name := changeWith) "change " term " with " term (location)? : tactic
|
||||
|
||||
/--
|
||||
If `thm` is a theorem `a = b`, then as a rewrite rule,
|
||||
* `thm` means to replace `a` with `b`, and
|
||||
* `← thm` means to replace `b` with `a`.
|
||||
-/
|
||||
syntax rwRule := ("← " <|> "<- ")? term
|
||||
/-- A `rwRuleSeq` is a list of `rwRule` in brackets. -/
|
||||
syntax rwRuleSeq := "[" rwRule,*,? "]"
|
||||
|
||||
/--
|
||||
`rewrite [e]` applies identity `e` as a rewrite rule to the target of the main goal.
|
||||
If `e` is preceded by left arrow (`←` or `<-`), the rewrite is applied in the reverse direction.
|
||||
If `e` is a defined constant, then the equational theorems associated with `e` are used.
|
||||
This provides a convenient way to unfold `e`.
|
||||
If `e` is a defined constant, then the equational theorems associated with `e` are used. This provides a convenient way to unfold `e`.
|
||||
- `rewrite [e₁, ..., eₙ]` applies the given rules sequentially.
|
||||
- `rewrite [e] at l` rewrites `e` at location(s) `l`, where `l` is either `*` or a
|
||||
list of hypotheses in the local context. In the latter case, a turnstile `⊢` or `|-`
|
||||
can also be used, to signify the target of the goal.
|
||||
- `rewrite [e] at l` rewrites `e` at location(s) `l`, where `l` is either `*` or a list of hypotheses in the local context. In the latter case, a turnstile `⊢` or `|-` can also be used, to signify the target of the goal.
|
||||
-/
|
||||
syntax (name := rewriteSeq) "rewrite " (config)? rwRuleSeq (location)? : tactic
|
||||
|
||||
/--
|
||||
`rw` is like `rewrite`, but also tries to close the goal by "cheap" (reducible) `rfl` afterwards.
|
||||
-/
|
||||
macro (name := rwSeq) "rw " c:(config)? s:rwRuleSeq l:(location)? : tactic =>
|
||||
macro (name := rwSeq) rw:"rw " c:(config)? s:rwRuleSeq l:(location)? : tactic =>
|
||||
match s with
|
||||
| `(rwRuleSeq| [$rs,*]%$rbrak) =>
|
||||
| `(rwRuleSeq| [%$lbrak $rs:rwRule,* ]%$rbrak) =>
|
||||
-- We show the `rfl` state on `]`
|
||||
`(tactic| rewrite $(c)? [$rs,*] $(l)?; with_annotate_state $rbrak (try (with_reducible rfl)))
|
||||
`(tactic| rewrite%$rw $(c)? [%$lbrak $rs,*] $(l)?; try (with_reducible rfl%$rbrak))
|
||||
| _ => Macro.throwUnsupported
|
||||
|
||||
/--
|
||||
The `injection` tactic is based on the fact that constructors of inductive data
|
||||
types are injections.
|
||||
That means that if `c` is a constructor of an inductive datatype, and if `(c t₁)`
|
||||
and `(c t₂)` are two terms that are equal then `t₁` and `t₂` are equal too.
|
||||
If `q` is a proof of a statement of conclusion `t₁ = t₂`, then injection applies
|
||||
injectivity to derive the equality of all arguments of `t₁` and `t₂` placed in
|
||||
the same positions. For example, from `(a::b) = (c::d)` we derive `a=c` and `b=d`.
|
||||
To use this tactic `t₁` and `t₂` should be constructor applications of the same constructor.
|
||||
Given `h : a::b = c::d`, the tactic `injection h` adds two new hypothesis with types
|
||||
`a = c` and `b = d` to the main goal.
|
||||
The `injection` tactic is based on the fact that constructors of inductive data types are injections.
|
||||
That means that if `c` is a constructor of an inductive datatype, and if `(c t₁)` and `(c t₂)` are two terms that are equal then `t₁` and `t₂` are equal too.
|
||||
If `q` is a proof of a statement of conclusion `t₁ = t₂`, then injection applies injectivity to derive the equality of all arguments of `t₁` and `t₂`
|
||||
placed in the same positions. For example, from `(a::b) = (c::d)` we derive `a=c` and `b=d`. To use this tactic `t₁` and `t₂`
|
||||
should be constructor applications of the same constructor.
|
||||
Given `h : a::b = c::d`, the tactic `injection h` adds two new hypothesis with types `a = c` and `b = d` to the main goal.
|
||||
The tactic `injection h with h₁ h₂` uses the names `h₁` and `h₂` to name the new hypotheses.
|
||||
-/
|
||||
syntax (name := injection) "injection " term (" with " (colGt (ident <|> hole))+)? : tactic
|
||||
|
||||
/-- `injections` applies `injection` to all hypotheses recursively
|
||||
(since `injection` can produce new hypotheses). Useful for destructing nested
|
||||
constructor equalities like `(a::b::c) = (d::e::f)`. -/
|
||||
syntax (name := injection) "injection " term (" with " (colGt (ident <|> "_"))+)? : tactic
|
||||
-- TODO: add with
|
||||
syntax (name := injections) "injections" : tactic
|
||||
|
||||
/--
|
||||
The discharger clause of `simp` and related tactics.
|
||||
This is a tactic used to discharge the side conditions on conditional rewrite rules.
|
||||
-/
|
||||
syntax discharger := atomic("(" (&"discharger" <|> &"disch")) " := " tacticSeq ")"
|
||||
|
||||
/-- Use this rewrite rule before entering the subterms -/
|
||||
syntax simpPre := "↓"
|
||||
/-- Use this rewrite rule after entering the subterms -/
|
||||
syntax simpPost := "↑"
|
||||
/--
|
||||
A simp lemma specification is:
|
||||
* optional `↑` or `↓` to specify use before or after entering the subterm
|
||||
* optional `←` to use the lemma backward
|
||||
* `thm` for the theorem to rewrite with
|
||||
-/
|
||||
syntax simpLemma := (simpPre <|> simpPost)? ("← " <|> "<- ")? term
|
||||
/-- An erasure specification `-thm` says to remove `thm` from the simp set -/
|
||||
syntax simpErase := "-" term:max
|
||||
/-- The simp lemma specification `*` means to rewrite with all hypotheses -/
|
||||
syntax simpStar := "*"
|
||||
/--
|
||||
The `simp` tactic uses lemmas and hypotheses to simplify the main goal target or
|
||||
non-dependent hypotheses. It has many variants:
|
||||
The `simp` tactic uses lemmas and hypotheses to simplify the main goal target or non-dependent hypotheses. It has many variants.
|
||||
- `simp` simplifies the main goal target using lemmas tagged with the attribute `[simp]`.
|
||||
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged
|
||||
with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions.
|
||||
If an `hᵢ` is a defined constant `f`, then the equational lemmas associated with
|
||||
`f` are used. This provides a convenient way to unfold `f`.
|
||||
- `simp [*]` simplifies the main goal target using the lemmas tagged with the
|
||||
attribute `[simp]` and all hypotheses.
|
||||
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas.
|
||||
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged
|
||||
with the attribute `[simp]`, but removes the ones named `idᵢ`.
|
||||
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If
|
||||
the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis
|
||||
`hᵢ` is introduced, but the old one remains in the local context.
|
||||
- `simp [h₁, h₂, ..., hₙ]` simplifies the main goal target using the lemmas tagged with the attribute `[simp]` and the given `hᵢ`'s, where the `hᵢ`'s are expressions. If an `hᵢ` is a defined constant `f`, then the equational lemmas associated with `f` are used. This provides a convenient way to unfold `f`.
|
||||
- `simp [*]` simplifies the main goal target using the lemmas tagged with the attribute `[simp]` and all hypotheses.
|
||||
- `simp only [h₁, h₂, ..., hₙ]` is like `simp [h₁, h₂, ..., hₙ]` but does not use `[simp]` lemmas
|
||||
- `simp [-id₁, ..., -idₙ]` simplifies the main goal target using the lemmas tagged with the attribute `[simp]`, but removes the ones named `idᵢ`.
|
||||
- `simp at h₁ h₂ ... hₙ` simplifies the hypotheses `h₁ : T₁` ... `hₙ : Tₙ`. If the target or another hypothesis depends on `hᵢ`, a new simplified hypothesis `hᵢ` is introduced, but the old one remains in the local context.
|
||||
- `simp at *` simplifies all the hypotheses and the target.
|
||||
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the
|
||||
other hypotheses.
|
||||
- `simp [*] at *` simplifies target and all (propositional) hypotheses using the other hypotheses.
|
||||
-/
|
||||
syntax (name := simp) "simp " (config)? (discharger)? (&"only ")?
|
||||
("[" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic
|
||||
syntax (name := simp) "simp " (config)? (discharger)? (&"only ")? ("[" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic
|
||||
/--
|
||||
`simp_all` is a stronger version of `simp [*] at *` where the hypotheses and target
|
||||
are simplified multiple times until no simplication is applicable.
|
||||
`simp_all` is a stronger version of `simp [*] at *` where the hypotheses and target are simplified
|
||||
multiple times until no simplication is applicable.
|
||||
Only non-dependent propositional hypotheses are considered.
|
||||
-/
|
||||
syntax (name := simpAll) "simp_all " (config)? (discharger)? (&"only ")?
|
||||
("[" (simpErase <|> simpLemma),* "]")? : tactic
|
||||
syntax (name := simpAll) "simp_all " (config)? (discharger)? (&"only ")? ("[" (simpErase <|> simpLemma),* "]")? : tactic
|
||||
|
||||
/--
|
||||
The `dsimp` tactic is the definitional simplifier. It is similar to `simp` but only
|
||||
applies theorems that hold by reflexivity. Thus, the result is guaranteed to be
|
||||
definitionally equal to the input.
|
||||
The `dsimp` tactic is the definitional simplifier. It is similar to `simp` but only applies theorems that hold by
|
||||
reflexivity. Thus, the result is guaranteed to be definitionally equal to the input.
|
||||
-/
|
||||
syntax (name := dsimp) "dsimp " (config)? (discharger)? (&"only ")?
|
||||
("[" (simpErase <|> simpLemma),* "]")? (location)? : tactic
|
||||
syntax (name := dsimp) "dsimp " (config)? (discharger)? (&"only ")? ("[" (simpErase <|> simpLemma),* "]")? (location)? : tactic
|
||||
|
||||
/--
|
||||
`delta id` delta-expands the definition `id`.
|
||||
This is a low-level tactic, it will expose how recursive definitions have been
|
||||
compiled by Lean.
|
||||
-/
|
||||
`delta id` delta-expands the definition `id`.
|
||||
This is a low-level tactic, it will expose how recursive definitions have been compiled by Lean. -/
|
||||
syntax (name := delta) "delta " ident (location)? : tactic
|
||||
|
||||
/--
|
||||
`unfold id,+` unfolds definition `id`. For non-recursive definitions, this tactic
|
||||
is identical to `delta`.
|
||||
For definitions by pattern matching, it uses "equation lemmas" which are
|
||||
autogenerated for each match arm.
|
||||
-/
|
||||
`unfold id,+` unfolds definition `id`. For non-recursive definitions, this tactic is identical to `delta`.
|
||||
For recursive definitions, it hides the encoding tricks used by the Lean frontend to convince the
|
||||
kernel that the definition terminates. -/
|
||||
syntax (name := unfold) "unfold " ident,+ (location)? : tactic
|
||||
|
||||
/--
|
||||
Auxiliary macro for lifting have/suffices/let/...
|
||||
It makes sure the "continuation" `?_` is the main goal after refining.
|
||||
-/
|
||||
-- Auxiliary macro for lifting have/suffices/let/...
|
||||
-- It makes sure the "continuation" `?_` is the main goal after refining
|
||||
macro "refine_lift " e:term : tactic => `(focus (refine no_implicit_lambda% $e; rotate_right))
|
||||
|
||||
/--
|
||||
`have h : t := e` adds the hypothesis `h : t` to the current goal if `e` a term
|
||||
of type `t`.
|
||||
* If `t` is omitted, it will be inferred.
|
||||
* If `h` is omitted, the name `this` is used.
|
||||
* The variant `have pattern := e` is equivalent to `match e with | pattern => _`,
|
||||
and it is convenient for types that have only one applicable constructor.
|
||||
For example, given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the
|
||||
hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
|
||||
`have h : t := e` adds the hypothesis `h : t` to the current goal if `e` a term of type `t`. If `t` is omitted, it will be inferred.
|
||||
If `h` is omitted, the name `this` is used.
|
||||
The variant `have pattern := e` is equivalent to `match e with | pattern => _`, and it is convenient for types that have only applicable constructor.
|
||||
Example: given `h : p ∧ q ∧ r`, `have ⟨h₁, h₂, h₃⟩ := h` produces the hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
|
||||
-/
|
||||
macro "have " d:haveDecl : tactic => `(refine_lift have $d:haveDecl; ?_)
|
||||
|
||||
/--
|
||||
Given a main goal `ctx ⊢ t`, `suffices h : t' from e` replaces the main goal with `ctx ⊢ t'`,
|
||||
`have h := e` adds the hypothesis `h : t` if `e : t`.
|
||||
-/
|
||||
macro (priority := high) "have" x:ident " := " p:term : tactic => `(have $x:ident : _ := $p)
|
||||
/--
|
||||
Given a main goal `ctx |- t`, `suffices h : t' from e` replaces the main goal with `ctx |- t'`,
|
||||
`e` must have type `t` in the context `ctx, h : t'`.
|
||||
|
||||
The variant `suffices h : t' by tac` is a shorthand for `suffices h : t' from by tac`.
|
||||
If `h :` is omitted, the name `this` is used.
|
||||
-/
|
||||
macro "suffices " d:sufficesDecl : tactic => `(refine_lift suffices $d; ?_)
|
||||
macro "suffices " d:sufficesDecl : tactic => `(refine_lift suffices $d:sufficesDecl; ?_)
|
||||
/--
|
||||
`let h : t := e` adds the hypothesis `h : t := e` to the current goal if `e` a term of type `t`.
|
||||
If `t` is omitted, it will be inferred.
|
||||
The variant `let pattern := e` is equivalent to `match e with | pattern => _`,
|
||||
and it is convenient for types that have only applicable constructor.
|
||||
Example: given `h : p ∧ q ∧ r`, `let ⟨h₁, h₂, h₃⟩ := h` produces the hypotheses
|
||||
`h₁ : p`, `h₂ : q`, and `h₃ : r`.
|
||||
The variant `let pattern := e` is equivalent to `match e with | pattern => _`, and it is convenient for types that have only applicable constructor.
|
||||
Example: given `h : p ∧ q ∧ r`, `let ⟨h₁, h₂, h₃⟩ := h` produces the hypotheses `h₁ : p`, `h₂ : q`, and `h₃ : r`.
|
||||
-/
|
||||
macro "let " d:letDecl : tactic => `(refine_lift let $d:letDecl; ?_)
|
||||
/--
|
||||
`show t` finds the first goal whose target unifies with `t`. It makes that the main goal,
|
||||
performs the unification, and replaces the target with the unified version of `t`.
|
||||
-/
|
||||
macro "show " e:term : tactic => `(refine_lift show $e from ?_) -- TODO: fix, see comment
|
||||
/-- `let rec f : t := e` adds a recursive definition `f` to the current goal.
|
||||
The syntax is the same as term-mode `let rec`. -/
|
||||
macro "show " e:term : tactic => `(refine_lift show $e:term from ?_) -- TODO: fix, see comment
|
||||
syntax (name := letrec) withPosition(atomic("let " &"rec ") letRecDecls) : tactic
|
||||
macro_rules
|
||||
| `(tactic| let rec $d) => `(tactic| refine_lift let rec $d; ?_)
|
||||
| `(tactic| let rec $d:letRecDecls) => `(tactic| refine_lift let rec $d:letRecDecls; ?_)
|
||||
|
||||
/-- Similar to `refine_lift`, but using `refine'` -/
|
||||
-- Similar to `refineLift`, but using `refine'`
|
||||
macro "refine_lift' " e:term : tactic => `(focus (refine' no_implicit_lambda% $e; rotate_right))
|
||||
/-- Similar to `have`, but using `refine'` -/
|
||||
macro "have' " d:haveDecl : tactic => `(refine_lift' have $d:haveDecl; ?_)
|
||||
/-- Similar to `have`, but using `refine'` -/
|
||||
macro (priority := high) "have'" x:ident " := " p:term : tactic => `(have' $x : _ := $p)
|
||||
/-- Similar to `let`, but using `refine'` -/
|
||||
macro (priority := high) "have'" x:ident " := " p:term : tactic => `(have' $x:ident : _ := $p)
|
||||
macro "let' " d:letDecl : tactic => `(refine_lift' let $d:letDecl; ?_)
|
||||
|
||||
/--
|
||||
The left hand side of an induction arm, `| foo a b c` or `| @foo a b c`
|
||||
where `foo` is a constructor of the inductive type and `a b c` are the arguments
|
||||
to the contstructor.
|
||||
-/
|
||||
syntax inductionAltLHS := "| " (("@"? ident) <|> hole) (ident <|> hole)*
|
||||
/--
|
||||
In induction alternative, which can have 1 or more cases on the left
|
||||
and `_`, `?_`, or a tactic sequence after the `=>`.
|
||||
-/
|
||||
syntax inductionAltLHS := "| " (("@"? ident) <|> "_") (ident <|> "_")*
|
||||
syntax inductionAlt := ppDedent(ppLine) inductionAltLHS+ " => " (hole <|> syntheticHole <|> tacticSeq)
|
||||
syntax inductionAlts := "with " (tactic)? withPosition( (colGe inductionAlt)+)
|
||||
/--
|
||||
After `with`, there is an optional tactic that runs on all branches, and
|
||||
then a list of alternatives.
|
||||
-/
|
||||
syntax inductionAlts := "with " (tactic)? withPosition((colGe inductionAlt)+)
|
||||
|
||||
/--
|
||||
Assuming `x` is a variable in the local context with an inductive type,
|
||||
`induction x` applies induction on `x` to the main goal,
|
||||
producing one goal for each constructor of the inductive type,
|
||||
in which the target is replaced by a general instance of that constructor
|
||||
Assuming `x` is a variable in the local context with an inductive type, `induction x` applies induction on `x` to the main goal,
|
||||
producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor
|
||||
and an inductive hypothesis is added for each recursive argument to the constructor.
|
||||
If the type of an element in the local context depends on `x`,
|
||||
that element is reverted and reintroduced afterward,
|
||||
If the type of an element in the local context depends on `x`, that element is reverted and reintroduced afterward,
|
||||
so that the inductive hypothesis incorporates that hypothesis as well.
|
||||
|
||||
For example, given `n : Nat` and a goal with a hypothesis `h : P n` and target `Q n`,
|
||||
`induction n` produces one goal with hypothesis `h : P 0` and target `Q 0`,
|
||||
and one goal with hypotheses `h : P (Nat.succ a)` and `ih₁ : P a → Q a` and target `Q (Nat.succ a)`.
|
||||
Here the names `a` and `ih₁` are chosen automatically and are not accessible.
|
||||
You can use `with` to provide the variables names for each constructor.
|
||||
- `induction e`, where `e` is an expression instead of a variable,
|
||||
generalizes `e` in the goal, and then performs induction on the resulting variable.
|
||||
- `induction e using r` allows the user to specify the principle of induction that should be used.
|
||||
Here `r` should be a theorem whose result type must be of the form `C t`,
|
||||
where `C` is a bound variable and `t` is a (possibly empty) sequence of bound variables
|
||||
- `induction e generalizing z₁ ... zₙ`, where `z₁ ... zₙ` are variables in the local context,
|
||||
generalizes over `z₁ ... zₙ` before applying the induction but then introduces them in each goal.
|
||||
In other words, the net effect is that each inductive hypothesis is generalized.
|
||||
- Given `x : Nat`, `induction x with | zero => tac₁ | succ x' ih => tac₂`
|
||||
uses tactic `tac₁` for the `zero` case, and `tac₂` for the `succ` case.
|
||||
For example, given `n : Nat` and a goal with a hypothesis `h : P n` and target `Q n`, `induction n` produces one goal
|
||||
with hypothesis `h : P 0` and target `Q 0`, and one goal with hypotheses `h : P (Nat.succ a)` and `ih₁ : P a → Q a` and target `Q (Nat.succ a)`.
|
||||
Here the names `a` and `ih₁` are chosen automatically and are not accessible. You can use `with` to provide the variables names for each constructor.
|
||||
- `induction e`, where `e` is an expression instead of a variable, generalizes `e` in the goal, and then performs induction on the resulting variable.
|
||||
- `induction e using r` allows the user to specify the principle of induction that should be used. Here `r` should be a theorem whose result type must be of the form `C t`, where `C` is a bound variable and `t` is a (possibly empty) sequence of bound variables
|
||||
- `induction e generalizing z₁ ... zₙ`, where `z₁ ... zₙ` are variables in the local context, generalizes over `z₁ ... zₙ` before applying the induction but then introduces them in each goal. In other words, the net effect is that each inductive hypothesis is generalized.
|
||||
- Given `x : Nat`, `induction x with | zero => tac₁ | succ x' ih => tac₂` uses tactic `tac₁` for the `zero` case, and `tac₂` for the `succ` case.
|
||||
-/
|
||||
syntax (name := induction) "induction " term,+ (" using " ident)?
|
||||
("generalizing " (colGt term:max)+)? (inductionAlts)? : tactic
|
||||
syntax (name := induction) "induction " term,+ (" using " ident)? ("generalizing " (colGt term:max)+)? (inductionAlts)? : tactic
|
||||
|
||||
/-- A `generalize` argument, of the form `term = x` or `h : term = x`. -/
|
||||
syntax generalizeArg := atomic(ident " : ")? term:51 " = " ident
|
||||
|
||||
/--
|
||||
`generalize ([h :] e = x),+` replaces all occurrences `e`s in the main goal
|
||||
with a fresh hypothesis `x`s. If `h` is given, `h : e = x` is introduced as well.
|
||||
-/
|
||||
`generalize ([h :] e = x),+` replaces all occurrences `e`s in the main goal with a fresh hypothesis `x`s.
|
||||
If `h` is given, `h : e = x` is introduced as well. -/
|
||||
syntax (name := generalize) "generalize " generalizeArg,+ : tactic
|
||||
|
||||
/--
|
||||
A `cases` argument, of the form `e` or `h : e` (where `h` asserts that
|
||||
`e = cᵢ a b` for each constructor `cᵢ` of the inductive).
|
||||
-/
|
||||
syntax casesTarget := atomic(ident " : ")? term
|
||||
/--
|
||||
Assuming `x` is a variable in the local context with an inductive type,
|
||||
`cases x` splits the main goal, producing one goal for each constructor of the
|
||||
inductive type, in which the target is replaced by a general instance of that constructor.
|
||||
If the type of an element in the local context depends on `x`,
|
||||
that element is reverted and reintroduced afterward,
|
||||
so that the case split affects that hypothesis as well.
|
||||
`cases` detects unreachable cases and closes them automatically.
|
||||
|
||||
For example, given `n : Nat` and a goal with a hypothesis `h : P n` and target `Q n`,
|
||||
`cases n` produces one goal with hypothesis `h : P 0` and target `Q 0`,
|
||||
and one goal with hypothesis `h : P (Nat.succ a)` and target `Q (Nat.succ a)`.
|
||||
Here the name `a` is chosen automatically and is not accessible.
|
||||
You can use `with` to provide the variables names for each constructor.
|
||||
- `cases e`, where `e` is an expression instead of a variable, generalizes `e` in the goal,
|
||||
and then cases on the resulting variable.
|
||||
- Given `as : List α`, `cases as with | nil => tac₁ | cons a as' => tac₂`,
|
||||
uses tactic `tac₁` for the `nil` case, and `tac₂` for the `cons` case,
|
||||
and `a` and `as'` are used as names for the new variables introduced.
|
||||
- `cases h : e`, where `e` is a variable or an expression,
|
||||
performs cases on `e` as above, but also adds a hypothesis `h : e = ...` to each hypothesis,
|
||||
where `...` is the constructor instance for that particular case.
|
||||
Assuming `x` is a variable in the local context with an inductive type, `cases x` splits the main goal,
|
||||
producing one goal for each constructor of the inductive type, in which the target is replaced by a general instance of that constructor.
|
||||
If the type of an element in the local context depends on `x`, that element is reverted and reintroduced afterward,
|
||||
so that the case split affects that hypothesis as well. `cases` detects unreachable cases and closes them automatically.
|
||||
For example, given `n : Nat` and a goal with a hypothesis `h : P n` and target `Q n`, `cases n` produces one goal with hypothesis `h : P 0` and target `Q 0`,
|
||||
and one goal with hypothesis `h : P (Nat.succ a)` and target `Q (Nat.succ a)`. Here the name `a` is chosen automatically and are not accessible. You can use `with` to provide the variables names for each constructor.
|
||||
- `cases e`, where `e` is an expression instead of a variable, generalizes `e` in the goal, and then cases on the resulting variable.
|
||||
- Given `as : List α`, `cases as with | nil => tac₁ | cons a as' => tac₂`, uses tactic `tac₁` for the `nil` case, and `tac₂` for the `cons` case, and `a` and `as'` are used as names for the new variables introduced.
|
||||
- `cases h : e`, where `e` is a variable or an expression, performs cases on `e` as above, but also adds a hypothesis `h : e = ...` to each hypothesis, where `...` is the constructor instance for that particular case.
|
||||
-/
|
||||
syntax (name := cases) "cases " casesTarget,+ (" using " ident)? (inductionAlts)? : tactic
|
||||
|
||||
@@ -616,8 +362,7 @@ macro_rules
|
||||
| `(tactic| repeat $seq) => `(tactic| first | ($seq); repeat $seq | skip)
|
||||
|
||||
/--
|
||||
`trivial` tries different simple tactics (e.g., `rfl`, `contradiction`, ...)
|
||||
to close the current goal.
|
||||
`trivial` tries different simple tactics (e.g., `rfl`, `contradiction`, ...) to close the current goal.
|
||||
You can use the command `macro_rules` to extend the set of tactics used. Example:
|
||||
```
|
||||
macro_rules | `(tactic| trivial) => `(tactic| simp)
|
||||
@@ -631,32 +376,17 @@ For a `match` expression with `n` cases, the `split` tactic generates at most `n
|
||||
-/
|
||||
syntax (name := split) "split " (colGt term)? (location)? : tactic
|
||||
|
||||
/-- `dbg_trace "foo"` prints `foo` when elaborated.
|
||||
Useful for debugging tactic control flow:
|
||||
```
|
||||
example : False ∨ True := by
|
||||
first
|
||||
| apply Or.inl; trivial; dbg_trace "left"
|
||||
| apply Or.inr; trivial; dbg_trace "right"
|
||||
```
|
||||
-/
|
||||
syntax (name := dbgTrace) "dbg_trace " str : tactic
|
||||
|
||||
/--
|
||||
`stop` is a helper tactic for "discarding" the rest of a proof:
|
||||
it is defined as `repeat sorry`.
|
||||
It is useful when working on the middle of a complex proofs,
|
||||
and less messy than commenting the remainder of the proof.
|
||||
-/
|
||||
/-- `stop` is a helper tactic for "discarding" the rest of a proof. It is useful when working on the middle of a complex proofs,
|
||||
and less messy than commenting the remainder of the proof. -/
|
||||
macro "stop" tacticSeq : tactic => `(repeat sorry)
|
||||
|
||||
/--
|
||||
The tactic `specialize h a₁ ... aₙ` works on local hypothesis `h`.
|
||||
The premises of this hypothesis, either universal quantifications or
|
||||
non-dependent implications, are instantiated by concrete terms coming
|
||||
from arguments `a₁` ... `aₙ`.
|
||||
The tactic adds a new hypothesis with the same name `h := h a₁ ... aₙ`
|
||||
and tries to clear the previous one.
|
||||
The premises of this hypothesis, either universal quantifications or non-dependent implications,
|
||||
are instantiated by concrete terms coming either from arguments `a₁` ... `aₙ`.
|
||||
The tactic adds a new hypothesis with the same name `h := h a₁ ... aₙ` and tries to clear the previous one.
|
||||
-/
|
||||
syntax (name := specialize) "specialize " term : tactic
|
||||
|
||||
@@ -667,117 +397,26 @@ macro_rules | `(tactic| trivial) => `(tactic| decide)
|
||||
macro_rules | `(tactic| trivial) => `(tactic| apply True.intro)
|
||||
macro_rules | `(tactic| trivial) => `(tactic| apply And.intro <;> trivial)
|
||||
|
||||
/--
|
||||
`unhygienic tacs` runs `tacs` with name hygiene disabled.
|
||||
This means that tactics that would normally create inaccessible names will instead
|
||||
make regular variables. **Warning**: Tactics may change their variable naming
|
||||
strategies at any time, so code that depends on autogenerated names is brittle.
|
||||
Users should try not to use `unhygienic` if possible.
|
||||
```
|
||||
example : ∀ x : Nat, x = x := by unhygienic
|
||||
intro -- x would normally be intro'd as inaccessible
|
||||
exact Eq.refl x -- refer to x
|
||||
```
|
||||
-/
|
||||
macro "unhygienic " t:tacticSeq : tactic => `(set_option tactic.hygienic false in $t)
|
||||
macro "unhygienic " t:tacticSeq : tactic => `(set_option tactic.hygienic false in $t:tacticSeq)
|
||||
|
||||
/-- `fail msg` is a tactic that always fails, and produces an error using the given message. -/
|
||||
/-- `fail msg` is a tactic that always fail and produces an error using the given message. -/
|
||||
syntax (name := fail) "fail " (str)? : tactic
|
||||
|
||||
/--
|
||||
`checkpoint tac` acts the same as `tac`, but it caches the input and output of `tac`,
|
||||
and if the file is re-elaborated and the input matches, the tactic is not re-run and
|
||||
its effects are reapplied to the state. This is useful for improving responsiveness
|
||||
when working on a long tactic proof, by wrapping expensive tactics with `checkpoint`.
|
||||
|
||||
See the `save` tactic, which may be more convenient to use.
|
||||
|
||||
(TODO: do this automatically and transparently so that users don't have to use
|
||||
this combinator explicitly.)
|
||||
-/
|
||||
syntax (name := checkpoint) "checkpoint " tacticSeq : tactic
|
||||
|
||||
/--
|
||||
`save` is defined to be the same as `skip`, but the elaborator has
|
||||
special handling for occurrences of `save` in tactic scripts and will transform
|
||||
`by tac1; save; tac2` to `by (checkpoint tac1); tac2`, meaning that the effect of `tac1`
|
||||
will be cached and replayed. This is useful for improving responsiveness
|
||||
when working on a long tactic proof, by using `save` after expensive tactics.
|
||||
|
||||
(TODO: do this automatically and transparently so that users don't have to use
|
||||
this combinator explicitly.)
|
||||
-/
|
||||
macro (name := save) "save" : tactic => `(skip)
|
||||
|
||||
/--
|
||||
The tactic `sleep ms` sleeps for `ms` milliseconds and does nothing.
|
||||
It is used for debugging purposes only.
|
||||
-/
|
||||
/-- The tactic `sleep ms` sleeps for `ms` milliseconds and does nothing. It is used for debugging purposes only. -/
|
||||
syntax (name := sleep) "sleep" num : tactic
|
||||
|
||||
/--
|
||||
`exists e₁, e₂, ...` is shorthand for `refine ⟨e₁, e₂, ...⟩; try trivial`.
|
||||
It is useful for existential goals.
|
||||
-/
|
||||
/-- `exists e₁, e₂, ...` is shorthand for `refine ⟨e₁, e₂, ...⟩; try trivial`. It is useful for existential goals. -/
|
||||
macro "exists " es:term,+ : tactic =>
|
||||
`(tactic| (refine ⟨$es,*, ?_⟩; try trivial))
|
||||
|
||||
/--
|
||||
Apply congruence (recursively) to goals of the form `⊢ f as = f bs` and `⊢ HEq (f as) (f bs)`.
|
||||
The optional parameter is the depth of the recursive applications.
|
||||
This is useful when `congr` is too aggressive in breaking down the goal.
|
||||
For example, given `⊢ f (g (x + y)) = f (g (y + x))`,
|
||||
`congr` produces the goals `⊢ x = y` and `⊢ y = x`,
|
||||
while `congr 2` produces the intended `⊢ x + y = y + x`.
|
||||
-/
|
||||
syntax (name := congr) "congr " (num)? : tactic
|
||||
|
||||
end Tactic
|
||||
|
||||
namespace Attr
|
||||
/--
|
||||
Theorems tagged with the `simp` attribute are by the simplifier
|
||||
(i.e., the `simp` tactic, and its variants) to simplify expressions occurring in your goals.
|
||||
We call theorems tagged with the `simp` attribute "simp theorems" or "simp lemmas".
|
||||
Lean maintains a database/index containing all active simp theorems.
|
||||
Here is an example of a simp theorem.
|
||||
```lean
|
||||
@[simp] theorem ne_eq (a b : α) : (a ≠ b) = Not (a = b) := rfl
|
||||
```
|
||||
This simp theorem instructs the simplifier to replace instances of the term
|
||||
`a ≠ b` (e.g. `x + 0 ≠ y`) with `Not (a = b)` (e.g., `Not (x + 0 = y)`).
|
||||
The simplifier applies simp theorems in one direction only:
|
||||
if `A = B` is a simp theorem, then `simp` replaces `A`s with `B`s,
|
||||
but it doesn't replace `B`s with `A`s. Hence a simp theorem should have the
|
||||
property that its right-hand side is "simpler" than its left-hand side.
|
||||
In particular, `=` and `↔` should not be viewed as symmetric operators in this situation.
|
||||
The following would be a terrible simp theorem (if it were even allowed):
|
||||
```lean
|
||||
@[simp] lemma mul_right_inv_bad (a : G) : 1 = a * a⁻¹ := ...
|
||||
```
|
||||
Replacing 1 with a * a⁻¹ is not a sensible default direction to travel.
|
||||
Even worse would be a theorem that causes expressions to grow without bound,
|
||||
causing simp to loop forever.
|
||||
|
||||
By default the simplifier applies `simp` theorems to an expression `e`
|
||||
after its sub-expressions have been simplified.
|
||||
We say it performs a bottom-up simplification.
|
||||
You can instruct the simplifier to apply a theorem before its sub-expressions
|
||||
have been simplified by using the modifier `↓`. Here is an example
|
||||
```lean
|
||||
@[simp↓] theorem not_and_eq (p q : Prop) : (¬ (p ∧ q)) = (¬p ∨ ¬q) :=
|
||||
```
|
||||
|
||||
When multiple simp theorems are applicable, the simplifier uses the one with highest priority.
|
||||
If there are several with the same priority, it is uses the "most recent one". Example:
|
||||
```lean
|
||||
@[simp high] theorem cond_true (a b : α) : cond true a b = a := rfl
|
||||
@[simp low+1] theorem or_true (p : Prop) : (p ∨ True) = True :=
|
||||
propext <| Iff.intro (fun _ => trivial) (fun _ => Or.inr trivial)
|
||||
@[simp 100] theorem ite_self {d : Decidable c} (a : α) : ite c a a = a := by
|
||||
cases d <;> rfl
|
||||
```
|
||||
-/
|
||||
-- simp attribute syntax
|
||||
syntax (name := simp) "simp" (Tactic.simpPre <|> Tactic.simpPost)? (prio)? : attr
|
||||
end Attr
|
||||
|
||||
@@ -785,49 +424,25 @@ end Parser
|
||||
end Lean
|
||||
|
||||
/--
|
||||
`‹t›` resolves to an (arbitrary) hypothesis of type `t`.
|
||||
It is useful for referring to hypotheses without accessible names.
|
||||
`t` may contain holes that are solved by unification with the expected type;
|
||||
in particular, `‹_›` is a shortcut for `by assumption`.
|
||||
-/
|
||||
`‹t›` resolves to an (arbitrary) hypothesis of type `t`. It is useful for referring to hypotheses without accessible names.
|
||||
`t` may contain holes that are solved by unification with the expected type; in particular, `‹_›` is a shortcut for `by assumption`. -/
|
||||
macro "‹" type:term "›" : term => `((by assumption : $type))
|
||||
|
||||
/--
|
||||
`get_elem_tactic_trivial` is an extensible tactic automatically called
|
||||
by the notation `arr[i]` to prove any side conditions that arise when
|
||||
constructing the term (e.g. the index is in bounds of the array).
|
||||
The default behavior is to just try `trivial` (which handles the case
|
||||
where `i < arr.size` is in the context) and `simp_arith`
|
||||
(for doing linear arithmetic in the index).
|
||||
-/
|
||||
syntax "get_elem_tactic_trivial" : tactic
|
||||
syntax "get_elem_tactic_trivial" : tactic -- extensible tactic
|
||||
|
||||
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| trivial)
|
||||
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| simp (config := { arith := true }); done)
|
||||
|
||||
/--
|
||||
`get_elem_tactic` is the tactic automatically called by the notation `arr[i]`
|
||||
to prove any side conditions that arise when constructing the term
|
||||
(e.g. the index is in bounds of the array). It just delegates to
|
||||
`get_elem_tactic_trivial` and gives a diagnostic error message otherwise;
|
||||
users are encouraged to extend `get_elem_tactic_trivial` instead of this tactic.
|
||||
-/
|
||||
macro "get_elem_tactic" : tactic =>
|
||||
`(first
|
||||
| get_elem_tactic_trivial
|
||||
| fail "failed to prove index is valid, possible solutions:
|
||||
- Use `have`-expressions to prove the index is valid
|
||||
- Use `a[i]!` notation instead, runtime check is perfomed, and 'Panic' error message is produced if index is not valid
|
||||
- Use `a[i]?` notation instead, result is an `Option` type
|
||||
- Use `a[i]'h` notation instead, where `h` is a proof that index is valid"
|
||||
| fail "failed to prove index is valid, possible solutions:\n - Use `have`-expressions to prove the index is valid\n - Use `a[i]!` notation instead, runtime check is perfomed, and 'Panic' error message is produced if index is not valid\n - Use `a[i]?` notation instead, result is an `Option` type\n - Use `a[i]'h` notation instead, where `h` is a proof that index is valid"
|
||||
)
|
||||
|
||||
@[inheritDoc getElem]
|
||||
macro:max x:term noWs "[" i:term "]" : term => `(getElem $x $i (by get_elem_tactic))
|
||||
|
||||
/-- Helper declaration for the unexpander -/
|
||||
@[inline] def getElem' [GetElem cont idx elem dom] (xs : cont) (i : idx) (h : dom xs i) : elem :=
|
||||
@[inline] def getElem' [GetElem Cont Idx Elem Dom] (xs : Cont) (i : Idx) (h : Dom xs i) : Elem :=
|
||||
getElem xs i h
|
||||
|
||||
@[inheritDoc getElem]
|
||||
macro x:term noWs "[" i:term "]'" h:term:max : term => `(getElem' $x $i $h)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user