doc: explain variable conditions in type classes
This commit is contained in:
parent
14f8ff1642
commit
586079462c
1 changed files with 46 additions and 23 deletions
|
|
@ -34,6 +34,7 @@ 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:
|
||||
```
|
||||
|
|
@ -47,10 +48,44 @@ between these possibilities, but generally Lean will elaborate working from the
|
|||
and assign the `+` to be the one for `Int`, and then need to insert coercions
|
||||
for the subterms `↑x : Int` and `↑y : Int`, resulting in the `↑x + ↑y` version.
|
||||
|
||||
Note that unlike most operators like `+`, `↑` is always eagerly unfolded at
|
||||
parse time into its definition. So if we look at the definition of `f` from
|
||||
before, we see no trace of the `CoeT.coe` function:
|
||||
```
|
||||
def f (x : Nat) : Int := x
|
||||
#print f
|
||||
-- def f : Nat → Int :=
|
||||
-- fun (x : Nat) => Int.ofNat x
|
||||
```
|
||||
|
||||
## Important typeclasses
|
||||
|
||||
Lean resolves a coercion by either inserting a `CoeDep` instance
|
||||
or chaining `CoeHead? CoeOut* Coe* CoeTail?` instances.
|
||||
(That is, zero or one `CoeHead` instances, an arbitrary number of `CoeOut`
|
||||
instances, etc.)
|
||||
|
||||
The `CoeHead? CoeOut*` instances are chained from the "left" side.
|
||||
So if Lean looks for a coercion from `Nat` to `Int`, it starts by trying coerce
|
||||
`Nat` using `CoeHead` by looking for a `CoeHead Nat ?α` instance, and then
|
||||
continuing with `CoeOut`. Similarly `Coe* CoeTail?` are chained from the "right".
|
||||
|
||||
These classes should be implemented for coercions:
|
||||
|
||||
* `Coe α β` is the most basic class, and the usual one you will want to use
|
||||
when implementing a coercion for your own types.
|
||||
The variables in the type `α` must be a subset of the variables in `β`
|
||||
(or out-params of type class parameters),
|
||||
because `Coe` is chained right-to-left.
|
||||
|
||||
* `CoeOut α β` is like `Coe α β` but chained left-to-right.
|
||||
Use this if the variables in the type `α` are a superset of the variables in `β`.
|
||||
|
||||
* `CoeTail α β` is like `Coe α β`, but only applied once.
|
||||
Use this for coercions that would cause loops, like `[Ring R] → CoeTail Nat R`.
|
||||
|
||||
* `CoeHead α β` is similar to `CoeOut α β`, but only applied once.
|
||||
Use this for coercions that would cause loops, like `[SetLike S α] → CoeHead S (Set α)`.
|
||||
|
||||
* `CoeDep α (x : α) β` allows `β` to depend not only on `α` but on the value
|
||||
`x : α` itself. This is useful when the coercion function is dependent.
|
||||
|
|
@ -63,32 +98,20 @@ for the subterms `↑x : Int` and `↑y : Int`, resulting in the `↑x + ↑y` v
|
|||
* `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`.
|
||||
`f` does not have a function type.
|
||||
`CoeFun` instances apply to `CoeOut` as well.
|
||||
|
||||
* `CoeSort α β` is a coercion to a sort. `β` must be a universe, and if
|
||||
`a : α` appears in a place where a type is expected, like `(x : a)` or `a → a`,
|
||||
then it will be turned into `(x : CoeSort.coe a)`.
|
||||
`a : α` appears in a place where a type is expected, like `(x : a)` or `a → a`.
|
||||
`CoeSort` instances apply to `CoeOut` as well.
|
||||
|
||||
* `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.
|
||||
On top of these instances this file defines several auxiliary type classes:
|
||||
* `CoeTC := Coe*`
|
||||
* `CoeOTC := CoeOut* Coe*`
|
||||
* `CoeHTC := CoeHead? CoeOut* Coe*`
|
||||
* `CoeHTCT := CoeHead? CoeOut* Coe* CoeTail?`
|
||||
* `CoeDep := CoeHead? CoeOut* Coe* CoeTail? | CoeDep`
|
||||
|
||||
* `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'
|
||||
|
|
@ -208,7 +231,7 @@ attribute [coe_decl] CoeDep.coe
|
|||
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`.
|
||||
A `CoeT` chain has the grammar `CoeHead? CoeOut* Coe* CoeTail? | CoeDep`.
|
||||
-/
|
||||
class CoeT (α : Sort u) (_ : α) (β : Sort v) where
|
||||
/-- The resulting value of type `β`. The input `x : α` is a parameter to
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue