-
Notifications
You must be signed in to change notification settings - Fork 449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Contextual suggestions #1223
Comments
@EdAyers Thanks for creating this issue. I love it! In Mathlib 3, there are tactics such as In Lean 3, we had support for interactive holes. They were suggested by @david-christiansen, and he actually implemented the Emacs support for this feature. This feature never became popular due to Lean 3 design limitations, and some of these motivations made us start the Lean 4 project. However, I still think "interactive holes" are a great idea. The idea is to click in a hole and get suggestions. Does it make sense to integrate "interactive holes" into your RFC? I would love to have a unified framework for providing suggestions to users. Thanks again for all your work. It is amazing. |
Nice proposal! It's great to see progress on suggestions. Can you please explain how the location is computed (that the client passes to
I agree, it is very distracting if every hover in the infoview has a CPU-intensive side-effect (and ten seconds later blows up the popup with fifty suggestions). Can we put this behind a "suggest" button? We can also add an extra "suggest" button for the whole tactic state. There's also the question what happens when one provider is fast and another one takes forever. Do we wait for the last one, timeouts? UX-wise, I'm a bit afraid that we'll get too many suggestions. Almost every tactic can be turned into a suggestion (and will probably be if this proposal works well). If every tactic has multiple suggestions, you quickly get dozens of suggestions. Very useful generic suggestions like "give me a conv state here" would even be a suggestion for every location. Maybe a two-step approach, where we first show a (very quickly computable) list of at most one suggestion per provider, and then pick the the concrete suggestion in the second step. E.g., first click on
Absolutely. We should just reuse whatever LSP uses for completions / code actions. And
The proposal doesn't specify how structure SuggestionQuery where
pos : Lean.Lsp.TextDocumentPositionParams
loc : Json It's probably best to have a separate
I think it would be better to call this |
Added to RFC in location details section.
I think this is probably the way to go. I was hoping it would be possible to get all the information in one shot for a better UX, but this is undermined if it takes too long. Rather than wait for all suggestion-providers to complete perhaps they could each be performed in a separate task and incrementally shown as they resolve.
I'll look in to this.
I kept the position because the RPC handler needs to recover the
Yes some other contenders: |
I think there are some shared functionality. I will have a look at how try-this and the lightbulbs work. It is a slightly different feature because the suggestion is also dependent on the particular location in the infoview that the user clicked on.
Yes @Vtec234 and I have chatted a bit about this. I am also very keen for interactive holes, particularly when turbocharged by syntax-aware document editing by the lean server. |
Thanks! So these are four cases and not an arbitrary-length list. In that case I think it would be better to make an inductive for it. Now I have a new question: how do you specify the tactic state to the
But the position is only relevant for the RPC handler, not the suggestion provider. That's why I suggested to remove it from the provider. |
Maybe it's helpful to categorize the various kinds of useful code insertion features that we want to have:
In Lean 3, "try this" provided (1), and hole commands (triggered by Some of these kinds can be simulated by others, with varying amounts of awkwardness. For example if you have (1), then you get (2) by manually inserting a placeholder tactic first (i.e., you write However you can't get (1) from (2); so this proposal is not a replacement for "try this". And neither of them give you (5). UX-wise (1),(3),(5) should also be available as code actions ("light bulbs"). For (2) the UI in this proposal looks great. For (4) I don't think you can get around writing some placeholder, but it would be great if you didn't need the But as Ed says, there's certainly a lot of code that can be shared between these code insertion features. |
For (4), could the code actions be triggered by an |
Yeah it was originally an inductive but I kept changing my mind about the cases, I'll make it an inductive. |
Yes, that's how it worked in Lean 3 and that seems like a good approach for Lean 4 as well. Theoretically (4) could also be done like autocompletion (without inserting anything). |
I think maybe I have not found the correct way of producing a tactic state from an RPC handler. |
It is possible to send the
It might be useful to distinguish tactics that are always useful and ones which are only sometimes so. |
This sounds really cool! I don't feel like I have enough knowledge of the guts of Lean to contribute too much here, but I do think there's a few areas related to the above discussion that I could draw your attention to. First off, while GHC and Idris use something like an unsolved Since Agda was built, Cyrus Omar (@cyrus-) and his collaborators have also done the work to figure out reasonable operational semantics for holes, so it becomes possible to run a program with holes in it and use the resulting value as input into the process of figuring out what belongs in them. In their work, the value of a hole is a closure that gives the values of all free variables when that specific hole was evaluated. From an NbE perspective, I'm not sure how to quote those back to syntax, but I suspect that something elegant could be done. Secondly, Joomy Korkut's M.Sc. thesis is related work for anyone wanting to add metaprogrammable code actions: http://cattheory.com/editTimeTacticsDraft.pdf (we have a TyDe paper based on it here: http://cattheory.com/extensibleTypeDirectedEditing.pdf ). Finally, Lean has a really nice system of quoted syntax with appropriate hygiene. Why would the insertion be a string, or a collection of text editing instructions, when it could be syntax? As a Lean metaprogrammer, I think in terms of syntax at all times. I can think of two challenges. First, there's no standard formatter for Lean, so the formatting of the inserted syntax might be bogus. Secondly, variable capture would have to be handled in a smart way, but I think that the hygiene info could be used to fix up binder names automatically, which would be a huge win. |
BTW, the current |
that's a good spot, I'm currently in the process of writing a library for PR: #1208 |
@david-christiansen Re: syntax vs inserting text. Yes the goal is absolutely to do it in terms of syntax, and then write a function The main thing that I've got from the conversation so far is that this RFC should be dependent on a more general RFC for how syntax-aware code-actions should work generally in Lean 4. In particular there seems to be a lot of potential to do something really cool like in the citations that David referenced. |
I mean, there's always the issue with a project like this where the discussion of the giant dream gets in the way of building useful stuff in the here and now. On the one hand, a coherent vision helps guide designs so that they work together, but on the other hand, requiring a coherent vision can lead to a painful pile of bikeshedding that blocks progress. I don't know where to put the balance here, but I trust you all to do so :) |
Agda's holes go back to Epigram's notion of the "shed", by the way - in case you want to do a full search for related work. See "The View from the Left" by McBride and McKinna (two versions here: http://strictlypositive.org/publications.html) for context on Epigram, and https://cs.ru.nl/~freek/courses/tt-2010/tvftl/epigram-notes.pdf for a better idea of what it was like. I tend to see Epigram as something like an artistic manifesto rather than a useful proof/programming tool, but it's influence has been wide. Lean has made practical many of the things that were presented in Epigram, and it would be cool to see more! |
I'm dubious about |
I was hoping that some uses of However, I found the following remark from that thread quite interesting:
The basic idea is that we just allow the user to change the signature of a method in an IDE, without invoking refactor explicitly, and then supply a code action to fix the call-sites. The same works for Java's analogue of move between files -- if you select some code, cut it in one file, and paste it into the other, the IDE prompts you with yes/now dialog for updating call-sites. While that may not cover quite the same use cases David had in mind, this one at least we could reasonably support without further protocol extensions. We'd "just" have to store and diff syntax trees in the server and produce appropriate code actions from that. |
The
The frustrations with the hole-actions in Lean 3 contributed to the decision of starting Lean 4. |
For what it's worth, |
Here are some previous discussions we've had on abusing autocompletion:
I'm decidedly not a fan of having autocomplete insert syntax snippets. This feature has annoyed me greatly since I first encountered it years (decades?) ago in IntelliJ. To offer a more constructive suggestion: we could offer a code action on I don't think it's a good idea to invent new nonfunctional syntax just to access code actions. I already know |
Yeah, we should definitely have it on |
Sorry to continue the sidetracking of the thread, @EdAyers. I don't see holes as primarily being about quickly getting completion to make it faster to do things like building match expressions - for that, things like @Kha's demo or even good old yasnippet are fine. Instead, I see them as something I can use to delimit the border between "code I believe in" and "code that I don't yet believe in". Then, the system knows about this border, and can offer help (e.g. diffing the types, suggesting transformations, providing a todo list, etc). The Holes are really a tool for programming, rather than for proving. I see the fundamental difference as being that, when proving, I care that I get an inhabitant of a type, whereas when programming, I care which inhabitant I end up with. Tactics are great for the former, but for the latter, they're pretty risky. I find holes to be very useful for programming even in GHC, where they're a bit less developed than in Idris or Agda. Circling back to the original question of this thread: Lean is pretty great today at explaining incomplete proof states, and providing more help in them is great! It would be neat if similar things worked for incomplete program states, but the two approaches seem at least somewhat orthogonal to me. |
RFC: contextual suggestions
People who are probably interested: @KaseQuark @Kha @javra @Vtec234 @leodemoura @gebner
Links
Goal
The purpose of contextual suggestions is to add some interactivity to the infoview's tactic state. The idea is that if you click on a particular subexpression in the target type of a goal, Lean should suggest rewrites or simplification tactics or conv tactics that could be applied at that subexpression.
Similarly if the user clicks on a hypothesis, and the hypothesis is an inductive datatype, Lean should suggest
cases
orinduction
on that type.Another, more ambitious example is for lean to suggest lemmas that could be
apply
ed depending on the subexpression you clicked on according to some heuristics.A potential extension to this feature to consider in the future is where Lean will also return the interactive goal state after the suggestion has been applied. Perhaps rendered by the infoview as a diff between the current goal state and the new one.
Proposal
In order to develop this, the tactic state infoview javascript code needs a way of telling the Lean server where the user clicked in the interactive tactic state.
Workflow
5
on the main goal's target.Lean.Widget.queryContextualSuggestions
RPC method with the argument being[goal.mvarid, "type", 5]
, representing the location that the user clicked.SuggestionProvider
s (registered using an attribute@[suggestion_provider]
). The proposed definition ofSuggestionProvider
is given below.Suggestion
object and then runs them on the tactic state, producing a new set ofInteractiveGoals
that can be used to make a diff for the user.Lean.Widget.queryContextualSuggestions
with the set of succeeding suggestions.diff : Syntax → Syntax → TextEdit
that determines a more precise edit (eg so you could add new rewrite lemmas to )Potential suggestion providers
Congruence tactics
If you click on a subexpression, what are the rewrite rules in mathlib that can be applied at that point? This should be fairly efficient to calculate because the rewrite lemmas can be stored in a discrimination tree, and we can restrict to rewrites that only hit the head expression.
In general any tactic that works inside
conv
will be available with this method. @KaseQuark is working on this.Other, more heavy suggestions could involve
simp
,ring
,norm_num
, e-matching etc. In this case, performance would become an issue and we will need to start thinking about concurrency and having cancellable versions of these tactic.other tactics
I think that the suggestions api has some potential in teaching new users which tactics to use. Most of these could be implemented as just a flat list of suggestions for the goal, but clicking in the target or hypothesis that is relevent for the tactic can help narrow the list of suggestions that the new user has to choose from.
For example,
if they click on the goal and it is a pi-binder, it can suggest
intros
. The number of items that are intro'd could depend on exactly where they click in the expression.cases
,rcases
clicking on an inductive datatypeapply
, clicking on a hypothesis that matches with the goal, clicking on a subexpression of the goal and doing a discrimination tree search of lemmas that match with the subexpression the user clicked on. (perhaps with some heuristics to keep the results list small)rfl
if the goal can be solved with rfl.Specialised use cases
Since suggestions are added with the
@[suggestion_provider]
attribute. Users can add their own special suggestions based on new, future tactics. Eg perhaps one for zooming in on a subexpression in an inequality proof using monotonicity lemmas.Detail on locations
To specify a position in the interactive goal, rather than having a special-purpose type I propse I having an ad-hoc approach, where a location is represented as an array of numbers and strings. This has the advantage of keeping the types passed through the RPC as simple as possible, with the disadvantage being that we lose some type safety.
In the current prototype the location has the following format:
[mvarid, "type", subexprPos]
[mvarid, "hyps", fvarids[0], "type"]
(the hypothesis bundle may have multiple fvars in it, in which case we just assume the suggestion is for the first fvar in the bundle)[mvarid, "hyps", fvarids[0], "type", subexprPos]
[mvarid, "hyps", fvarids[0], "value", subexprPos]
[mvarid, "hyps", fvarid]
These are stored as a React context (eg see here and consumed here) that is passed to various parts of the interactive goal view UI. So the computed location is decided entirely by vscode. Another approach would be to have the location passed directly from Lean server as an identifier for each
InteractiveGoal
andInteractiveHypothesisBundle
object.Tasks
insert
being a string, perhaps have it be a set of text-edit instructions?The text was updated successfully, but these errors were encountered: