doc: explain why elaborator fails and propose alternative elaboration strategies
cc @dselsam @kha @rwbarton
This commit is contained in:
parent
6cb4442349
commit
1933d70aae
1 changed files with 57 additions and 0 deletions
|
|
@ -13,6 +13,9 @@ def Bar.chalk : B → Unit := λ _ => ()
|
|||
open Foo
|
||||
open Bar
|
||||
|
||||
instance listCoe {α β} [HasCoe α β] : HasCoe (List α) (List β) :=
|
||||
⟨fun as => as.map coe⟩
|
||||
|
||||
/- The following succeeds: -/
|
||||
#check Foo.chalk a ["foo"] -- succeeds
|
||||
|
||||
|
|
@ -31,3 +34,57 @@ and that I can not employ an otherwise desirable use of overloading because of i
|
|||
|
||||
Note: it works if `Foo.chalk` takes `A` and `Var` and we pass `a` and `"foo"`.
|
||||
-/
|
||||
|
||||
/-
|
||||
|
||||
Here is the analysis of why it doesn't work.
|
||||
Given `chalk a ["foo"]` where `chalk` is overloaded,
|
||||
the current elaborator performs the following steps:
|
||||
|
||||
1- Elaborate the arguments `a` and `["foo"]` without an expected
|
||||
type. Thus, `["foo"]` is elaborated as a list of strings.
|
||||
|
||||
2- For each possible interpretation of `chalk`, we try to match the
|
||||
arguments with the expected types. `Bar.chalk` fails because there is
|
||||
no coercion from `A` to `B`. `Foo.chalk` fails because there is no
|
||||
coercion from `List String` to `List Var`. Note that the example would
|
||||
work if we had the coercion
|
||||
|
||||
```
|
||||
instance listCoe {α β} [HasCoe α β] : HasCoe (List α) (List β) :=
|
||||
⟨fun as => as.map coe⟩
|
||||
```
|
||||
However, users could still be surprised by the fact that the `chalk a ["foo"]` is
|
||||
elaborated as `Foo.chalk a (coe ["foo"])` instead of `Foo.chalk a [coe "foo"]`.
|
||||
|
||||
Here are some alternative elaboration strategies I have considered.
|
||||
We should discuss them in the next Dev meeting.
|
||||
|
||||
1- Instead of elaborating all arguments without an expected type, we
|
||||
elaborate only the shortest argument prefix that is sufficient for
|
||||
selecting the right overload candidate. Daniel's comments above
|
||||
suggest this is the strategy he expected. It would fix this particular
|
||||
instance, but it would still confuse users. For example, we can create
|
||||
the alternative problem `chalk ["foo"] a`. Users could say "the second
|
||||
argument clearly distinguishes the two applications."
|
||||
|
||||
2- Elaborate `chalk a ["foo"]` for each possible overload. This is a
|
||||
robust solution but may produce an exponential blowup. For example,
|
||||
suppose we have `f_1 (f_2 (f_3 (f_4 ... (f_n a) ... )))` where all
|
||||
`f_i` are overloaded. Moreover, every overload has the same result
|
||||
type. Thus, we cannot prune the search space using the expected
|
||||
type. This situation does not seem to occur in our code base. It did
|
||||
happen in Lean2, when we used to overload symbols such as `+` and `*`.
|
||||
We should ask Reid how often overloads are used in mathlib, and
|
||||
whether the exponential blowup is a real problem or not for them.
|
||||
|
||||
3- Use Lean3 approach, but when we get a typing error after we
|
||||
selected the right overload candidate, we re-elaborate it using the
|
||||
expected type instead of failing. This approach looks too haskish, and
|
||||
it may still produce an exponential blowup.
|
||||
|
||||
I am inclined to (try to) use solution 2 in Lean4. We can have a
|
||||
threshold on the amount of backtracking, and when it exceeds we
|
||||
produce an error message stating all overloads that are generating
|
||||
the huge search space.
|
||||
-/
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue