Zulip Chat Archive

Stream: new members

Topic: Force explicit brackets in VS Code?


Stuart Presnell (Jan 11 2022 at 09:51):

Is there a way to get VS Code to explicitly write all the brackets that it normally suppresses, e.g to write (a + b) - c rather than a + b - c?

Yaël Dillies (Jan 11 2022 at 09:53):

There should be some argument to set_option that does this.

Stuart Presnell (Jan 11 2022 at 10:09):

I've tried set_option pp. and looked through the autocomplete suggestions but nothing seems to be the thing I want. Is there documentation for set_option somewhere? (I didn't have much luck with a Google search for "mathlib set_option")

Kyle Miller (Jan 11 2022 at 11:13):

#help options

Kevin Buzzard (Jan 11 2022 at 12:01):

Wait! I think that what Yael means is "someone should write such code", not "such code already exists".

Stuart Presnell (Jan 11 2022 at 13:25):

Ah! That would explain why I couldn't find it! :)

Kyle Miller (Jan 11 2022 at 16:22):

It seems possible to implement without much difficulty. In lean/src/frontends/lean/pp.cpp there's a function add_paren_if_needed that could decide that they're always needed. There are other functions, too, that seem to decide whether to add parens, like pp_num, pp_notation_child, and maybe some things about pretty printing universe levels.

Patrick Johnson (Jan 11 2022 at 19:10):

I suggest that we do not include parentheses in function application. For example, f x y z should not be (((f x) y) z), but (f x y z) is fine. Otherwise, the output of pp.all will be even less readable than it is now.

Patrick Massot (Jan 12 2022 at 15:55):

I'm also interested in an option to force some more parentheses, for teaching purposes. I guess this doesn't require modifying cpp files. I think we can add it in tactic.interactive_expr. This has the advantage that anyone can use it in a project, it doesn't have to be in mathlib. This is what I do to use #5440 for teaching. I'd be very grateful if someone understanding the widget framework could show how to tun every a+b into (a+b) by modifying that file.

Alex J. Best (Jan 12 2022 at 16:20):

Ok its not what you really want or pretty but I just tried a quick experiment with a much more low-level feature, custom info view css:

  • Open Vscode settings, go to "Lean: Info view style"
  • Paste in
.expr-boundary:before {content: '(';} .expr-boundary:after { content: ')'; }

Kyle Miller (Jan 12 2022 at 16:42):

I have a reason to dig into the pretty printer for something else anyway -- seeing as there's more interest in a parens option, I'll create a lean PR for it. (Maybe I'll figure out how to do #5440, too, @Patrick Massot)

Patrick Massot (Jan 12 2022 at 16:55):

Thanks! What do you mean by "do #5440"? This PR already works perfectly. Do you mean adding a config option?

Patrick Massot (Jan 12 2022 at 16:55):

In the mean time I managed to do what I suggested except that I'm stuck at adding a config option.

Patrick Massot (Jan 12 2022 at 16:56):

I tried to hack tactic.is_trace_enabled_for but I can't get it to work

Patrick Massot (Jan 12 2022 at 16:56):

It's really a pain that adding options is not easy

Kyle Miller (Jan 12 2022 at 17:01):

Patrick Massot said:

Thanks! What do you mean by "do #5440"? This PR already works perfectly. Do you mean adding a config option?

I mean get it implemented directly in the pretty printer along with a configurable option

Patrick Massot (Jan 12 2022 at 17:10):

Nice

Patrick Massot (Jan 12 2022 at 17:14):

I would still be really interested in getting help with my option issue. Maybe @Rob Lewis could have a look? I put my file at https://gist.github.com/PatrickMassot/5700bded8c1536f4bd470df3852de6f2

Patrick Massot (Jan 12 2022 at 17:14):

The line that does nothing is 148

Patrick Massot (Jan 12 2022 at 17:15):

At the very bottom there an example that fails

Patrick Massot (Jan 12 2022 at 17:15):

Replacing line 148 by match tt with ... makes the example work

Patrick Massot (Jan 12 2022 at 17:18):

I'm not insisting on using a trace option, any way of having some sort of custom option would be good.

Rob Lewis (Jan 12 2022 at 17:38):

I have no idea about widgets. It seems like maybe it's reading options from a different environment or something

Patrick Massot (Jan 12 2022 at 17:43):

Thanks for trying. Maybe @Gabriel Ebner could help then?

Patrick Massot (Jan 12 2022 at 17:48):

I have another meta question for Rob. In the following code, is it possible to rewrite foo so that it looks like foo' (which doesn't work at all)?

import tactic.basic

namespace tactic.interactive
setup_tactic_parser
open tactic

meta def foo : tactic unit :=
do
   goal  get_goal >>= infer_type,
   match goal with
   | (expr.pi n _ t b) := do trace "a", skip
   | _ := do trace "b"
   end

meta def foo' : tactic unit :=
do
   { `( %%n : %%t, %%b)  get_goal >>= infer_type,
     trace "a", skip } <|> do { trace "b", skip}
   end

end tactic.interactive

Patrick Massot (Jan 12 2022 at 17:49):

The motivation is first readability but also I'd like to have a more complicated pattern in my real use case.

Gabriel Ebner (Jan 12 2022 at 17:59):

Here you go: https://gist.github.com/gebner/87defe68efaac3de248d1f0639fcff2e

Patrick Massot (Jan 12 2022 at 18:04):

Thank you so much Gabriel!

Patrick Massot (Jan 12 2022 at 18:04):

Is tactic.get_bool_option the key thing I was missing?

Julian Berman (Jan 12 2022 at 18:05):

(Line 543 should say "1 goal" instead of "1 but")

Gabriel Ebner (Jan 12 2022 at 18:05):

I think that's French.

Julian Berman (Jan 12 2022 at 18:05):

Aha! TIL.

Patrick Massot (Jan 12 2022 at 18:06):

Oh yes, this is all for French speaking teaching

Patrick Massot (Jan 12 2022 at 18:07):

Having a tactic that switches this on is perfectly fine , but I'm still curious to know whether it could be done with a set_option

Gabriel Ebner (Jan 12 2022 at 18:07):

Patrick Massot said:

Is tactic.get_bool_option the key thing I was missing?

Trace options are kind of special, is_trace_enabled_for doesn't look at the options. I think get_bool_option `trace.parens ff would work as well (if you wanted to abuse the trace option).

Gabriel Ebner (Jan 12 2022 at 18:07):

set_option is hard because afair there is no way to register options from the Lean side.

Patrick Massot (Jan 12 2022 at 18:08):

But there is still declare_trace that sounded promising

Gabriel Ebner (Jan 12 2022 at 18:09):

I mean sure if you want set_option trace.parens true then that should work (declare_trace + get_bool_option).

Patrick Massot (Jan 12 2022 at 18:10):

Ok, I'll try for learning purposes but I'll probably keep the tactic anyway.

Patrick Massot (Jan 12 2022 at 18:11):

Julian, there are other teaching tweaks in this file. For instance the "goals accomplished message" is not only translated, it's also written bigger, because math is hard.

Patrick Massot (Jan 12 2022 at 18:15):

For the record: using the trace option does indeed work.

Patrick Massot (Jan 12 2022 at 18:17):

and we can have both: the trace option and the switching tactic.

Patrick Massot (Jan 12 2022 at 18:18):

Last question for Gabriel : is the if-then-else bool.tt ← tactic.get_bool_option `trace.parens ff | pure s, a normal trick? I was searching around stuff like when and gave up because I had other issues.

Gabriel Ebner (Jan 12 2022 at 18:19):

For the record, another option would be to add a checkbox in the goal view. This is not that complicated, see here for an example with a counter: https://github.com/leanprover-community/lean/blob/a8cf8a0c9ea19a633baeb3aa7e8d706b86c2c0f9/tests/lean/widget/widget2.lean

Patrick Massot (Jan 12 2022 at 18:20):

That would be really nice! Would you know how to make that persistent?

Gabriel Ebner (Jan 12 2022 at 18:20):

is the if-then-else bool.tt ← tactic.get_bool_option `trace.parens ff | pure s, a normal trick?

I think I used it pretty often in Lean 3, although not everyone liked it. In Lean 4 you can also use the slightly more readable if !(← getBoolOption `trace.parens) then return s.

Patrick Massot (Jan 12 2022 at 18:20):

I mean more persistent than the tactic state filtering.

Kevin Buzzard (Jan 12 2022 at 18:21):

...which we can't make persistent, right?

Kevin Buzzard (Jan 12 2022 at 18:21):

although around here you get the impression that anything can happen nowadays

Gabriel Ebner (Jan 12 2022 at 18:21):

Patrick Massot said:

That would be really nice! Would you know how to make that persistent?

Unfortunately that's just as impossible as the tactic state filtering.

Patrick Massot (Jan 12 2022 at 18:21):

Yes Kevin, that's why I was asking.

Kevin Buzzard (Jan 12 2022 at 18:21):

Lean 4?

Patrick Massot (Jan 12 2022 at 18:22):

Ritual question: Lean 4?

Gabriel Ebner (Jan 12 2022 at 18:23):

In Lean 4 all of this happens on the vscode/editor side. So: persistent tactic filters → super easy, I hope that Chris will do this soon; French localization → impossible from the Lean side, you'll need to PR it to the vscode extension.

Gabriel Ebner (Jan 12 2022 at 18:24):

(More parentheses → probably doable by adding a custom delaborator)

Patrick Massot (Jan 12 2022 at 18:25):

The impossible localization is bad news :sad:

Patrick Massot (Jan 12 2022 at 18:27):

I didn't know Chris is working on this. Which Chris do you mean btw?

Gabriel Ebner (Jan 12 2022 at 18:27):

Impossible to do from the Lean side, you can still customize the extension though (but I realize that this is organizationally much harder).

Gabriel Ebner (Jan 12 2022 at 18:27):

Chris Lovett.

Patrick Massot (Jan 12 2022 at 18:27):

It's dinner time. I need to go. Thanks again for all your help!

Kyle Miller (Jan 15 2022 at 03:55):

What do we think about this?

set_option pp.parens true

#check @nat.add_assoc
/- nat.add_assoc : ∀ (n m k : ℕ), (((n + m) + k) = (n + (m + k))) -/

example (x y z : ) : x + y - z = x + y * z - y :=
begin
  /- ⊢ (((x + y) - z) = ((x + (y * z)) - y)) -/
end

example (f :     ) : f 2 (f 3 4 + 1) + 2 = f 1 2 :=
begin
  /- ⊢ ((f 2 (f 3 4 + 1) + 2) = f 1 2) -/
end

example (x y z : ) : x - -3 = -2 + x :=
begin
  /- ⊢ ((x - (-3)) = ((-2) + x)) -/
end

(Ping: @Stuart Presnell)

Kyle Miller (Jan 15 2022 at 03:57):

The implementation is just to wrap anything that is notation in parentheses, which seemed to be the smallest modification I could do that would get this feature to work, without adding way too many parentheses.

Kyle Miller (Jan 15 2022 at 03:58):

To create a PR, should I push directly to the lean repository? Or can I do it from a fork? (This is my first lean PR, and I don't have write access.)

Kyle Miller (Jan 15 2022 at 03:58):

Diff: https://github.com/leanprover-community/lean/compare/master...kmill:kmill_pp_parens?expand=1

Reid Barton (Jan 15 2022 at 04:34):

Outermost parentheses seem superfluous, but otherwise it looks good!

Stuart Presnell (Jan 15 2022 at 06:49):

I agree that it would be nice to get rid of the outermost brackets, but otherwise this looks like just what I was hoping for!

Patrick Massot (Jan 15 2022 at 15:14):

Getting rid of outermost parentheses looks really hard, it requires a more global analysis. This output is also what you get with my implementation based on widgets.

Kyle Miller (Jan 15 2022 at 17:15):

I had also considered an approach where pp.parens overrides the precedences of notation to convince lean to add more parentheses, but I couldn't convince myself that it would always produce correct output.

Kyle Miller (Jan 15 2022 at 17:15):

Even if the outermost parentheses seem superfluous, at least there's this invariant that every notation has a corresponding set of parentheses wrapping it. All the examples above are equalities, so we see outermost ones.

In contrast, in this example there are no outermost parentheses so we know the top-level expression is not notation:

example (p :     Prop) : p (1 + 2) 3 :=
begin
  /- ⊢ p (1 + 2) 3 -/
end

Kyle Miller (Jan 15 2022 at 19:02):

@Patrick Johnson Since you mentioned pp.all, what I learned looking through the source code is that, contrary to its name, it just overrides a handful of pretty printer options. Here's a complete list, for the curious:

  • pp.implicit true
  • pp.proofs true
  • pp.coercions true
  • pp.notation false
  • pp.universes true
  • pp.full_names true
  • pp.beta false
  • pp.numerals false
  • pp.strings false
  • pp.binder_types true
  • pp.generalized_field_notation false

Kevin Buzzard (Jan 16 2022 at 08:20):

Oh wow, I had assumed that there was nothing between eg pp.proofs true and pp.all. I had no idea I had such fine control over output. This is surely helpful for debugging

Mario Carneiro (Jan 16 2022 at 08:34):

I will often use pp.implicit true and/or pp.notation false as a minimal set of options to see what I want (usually implicit typeclass arguments) without as much stuff as pp.all

Kevin Buzzard (Jan 16 2022 at 08:44):

Yes you've told me that before, but in situations where you need more I'd not realised that there are still many options available. I guess I'm yet to find out if this is useful in practice

Alex J. Best (Jul 11 2023 at 09:10):

@Kyle Miller, how feasible do you think a version of this option in lean 4?

Kayla Thomas (Nov 14 2023 at 04:09):

Did this become an option in Lean 4?

Kyle Miller (Nov 14 2023 at 04:11):

Not yet, but I was just looking into it the other day.

Kayla Thomas (Nov 14 2023 at 04:13):

Cool. Thank you.


Last updated: Dec 20 2023 at 11:08 UTC