Zulip Chat Archive
Stream: mathlib4
Topic: Bringing back ⇑
Eric Wieser (Nov 09 2023 at 13:28):
In Lean4, core only supports ↑
. We added a custom elaborator in mathlib that lets us write ⇑
and ↥
(though it's buggy until #8297). Kyle Miller added support for delaboration of ↥
for pretty much all the cases we care about.
Do we want ⇑
to appear in the goal view instead of ↑
for functions? Should this be a mathlib-only feature or in Std?
Eric Wieser (Nov 09 2023 at 13:29):
/poll What do you prefer to see in your goal view
- ↑f (x + y) = ↑f x + ↑f y (the status quo)
- ⇑f (x + y) = ⇑f x + ⇑f y
Eric Wieser (Nov 09 2023 at 13:30):
Arguments in favor of ⇑
:
- It should round-trip better
- It allows use of the heuristic "
⇑
s are always boring,↑
s could be hiding something unexpected"
Eric Wieser (Nov 09 2023 at 13:31):
Arguments against:
- It's one more magic arrow to teach new users
Alex J. Best (Nov 09 2023 at 13:52):
Do we have actual examples of situations where round tripping is better? Those would be helpful to know when deciding
Eric Wieser (Nov 09 2023 at 13:59):
Sure, here's an example:
import Mathlib.Algebra.Group.Hom.Basic
import Mathlib.Tactic.Coe
example {M N : Type*} [Monoid M] [Monoid N] (f : M →* N) (x y : M) : f x = f y := by
show (⇑f) x = (⇑f) y -- works
show ↑f x = ↑f y -- fails
sorry
Eric Wieser (Nov 09 2023 at 13:59):
(the extra parens will go away after #8297)
Patrick Massot (Nov 09 2023 at 14:15):
In Lean 3, you needed to remove all ⇑
as a very first step when trying to get round-tripping.
Patrick Massot (Nov 09 2023 at 14:16):
And #8297 doesn't build.
Eric Wieser (Nov 09 2023 at 14:17):
Yes, and the status quo in mathlib 4 is that you have to do the same thing with removing all the ↑
, which is even worse because you can't tell which ones you need to remove!
Floris van Doorn (Nov 09 2023 at 16:11):
I would like to not see ⇑f x
in my goal view at all. f x
is unambiguous and (almost?) always parses correctly anyway. So it's just noise. (Ideally clicking on f
in the goal view would show that there is a coercion.)
If ⇑f
is not applied to an argument (e.g. ⇑f = ⇑g
or F (⇑f)
), then the ⇑
would be nice to see.
Floris van Doorn (Nov 09 2023 at 16:12):
The same applies to sorts, but they are a lot less frequent (in the formalizations I do), so I care less about that.
Eric Wieser (Nov 09 2023 at 16:13):
As an example of where you do need ⇑
, consider the goal state ⇑f + ⇑g = ⇑(f + g)
Eric Wieser (Nov 09 2023 at 16:16):
It's not immediately clear to me what the heuristic here is; the problem in general is when you have some polymorphic function h
(here HAdd.hAdd
) such that both h ⇑f ⇑g
and ⇑(h f g)
are well-typed.
Floris van Doorn (Nov 09 2023 at 16:17):
I covered that case in my message, and I do want to see the ⇑
(or ↑
) there, indeed.
Eric Wieser (Nov 09 2023 at 16:17):
I'm not sure that helps with (⇑f + ⇑g) x = ⇑(f + g) x
Eric Wieser (Nov 09 2023 at 16:18):
Ah yes, that sounds sufficient, you'd see (⇑f + ⇑g) x = (f + g) x
Floris van Doorn (Nov 09 2023 at 16:18):
The heuristic I was thinking of: is the ⇑f
applied to another argument. There might be a few cases where the ⇑
is still superfluous, like Function.curry ⇑f
, but I'm happy to accept those.
Eric Wieser (Nov 09 2023 at 16:40):
std4#354 implements that heuristic
Floris van Doorn (Nov 09 2023 at 16:57):
Eric Wieser said:
std4#354 implements that heuristic
Did you forget to push new commits?
/-- info: ⇑f 1 : Nat-/
Eric Wieser (Nov 09 2023 at 16:59):
$ git commt -m "implement Floris' heuristic"
git: 'commt' is not a git command. See 'git --help'.
Whoops, yes
Eric Wieser (Nov 09 2023 at 17:00):
I guess we need to settle the precedence discussion before that it safe to merge; I don't really care much about the outcome of #8297, and was just following Mario's advice
Floris van Doorn (Nov 09 2023 at 17:06):
I understand. I also don't care too much in #8297, and would definitely not block it, but when looking at the changes I was wondering whether it was actually a regression.
It's counterintuitive that ↑
and ⇑
have different precedences, and maybe that weighs stronger than a few extra parentheses.
Eric Wieser (Nov 09 2023 at 17:08):
Hot take: ↑
has the wrong precedence, and should be just shy of =
so that (↑a + b : X)
means "coerce a + b
to X
", since that's nicer than the (↑(a + b) : X)
we currently have to write
Floris van Doorn (Nov 09 2023 at 17:11):
Thinking seriously about your message makes me agree with Mario's suggestion. :sweat_smile:
Yes, low-precedence ↑
would probably lead to fewer parentheses, but a prefix operation with such a low precedence is just evil.
Mario Carneiro (Nov 09 2023 at 17:28):
Unfortunately, we have very many low precedence prefix operators because most macros are like that. Another one with an evil precedence is !
:
example (f : Bool → Bool → Bool) (x : Bool) : (f x !x = x) = (f x !(x = x)) := rfl
Eric Wieser (Nov 09 2023 at 17:34):
That's surely a bug, right?
Jireh Loreaux (Nov 09 2023 at 17:47):
eww, gross.
Martin Dvořák (Nov 09 2023 at 18:25):
Eric Wieser said:
Hot take:
↑
has the wrong precedence, and should be just shy of=
so that(↑a + b : X)
means "coercea + b
toX
", since that's nicer than the(↑(a + b) : X)
we currently have to write
Your suggestion would essentially guarantee that my brain would experience a parse error every time ↑a + b
appears in front of me. I understand your desire to do coërcion as the last step, but the syntax would have to be something like (a + b ↑: X)
to preserve the last drops of my sanity. A prefix operator, especially one that is typically written without a space, must bind tightly.
Martin Dvořák (Nov 09 2023 at 18:27):
Mario Carneiro said:
Unfortunately, we have very many low precedence prefix operators because most macros are like that. Another one with an evil precedence is
!
:example (f : Bool → Bool → Bool) (x : Bool) : (f x !x = x) = (f x !(x = x)) := rfl
My disgust is immeasurable, and my day is ruined. Please somebody PR a fix asap.
Eric Wieser (Nov 13 2023 at 15:25):
Eric Wieser said:
std4#354 implements that heuristic
Just to be explicit, the behavior in that PR is now:
variable (f : WrappedFun Nat) (g : Nat → WrappedFun Nat) (h : WrappedFun (WrappedFun Nat))
/-- info: ⇑f : Nat → Nat -/
#guard_msgs in #check f.fn
/-- info: ⇑f : Nat → Nat -/
#guard_msgs in #check ⇑f
-- applied functions do not need the `⇑`
/-- info: f 1 : Nat -/
#guard_msgs in #check ⇑f 1
/-- info: ⇑(g 1) : Nat → Nat -/
#guard_msgs in #check ⇑(g 1)
/-- info: (g 1) 2 : Nat -/
#guard_msgs in #check g 1 2
/-- info: ⇑h : Nat → WrappedFun Nat -/
#guard_msgs in #check ⇑h
/-- info: h 1 : WrappedFun Nat -/
#guard_msgs in #check h 1
/-- info: ⇑(h 1) : Nat → Nat -/
#guard_msgs in #check ⇑(h 1)
/-- info: (h 1) 2 : Nat -/
#guard_msgs in #check h 1 2
Eric Wieser (Nov 21 2023 at 15:36):
This is now live, see docs#FunLike.coe_injective for a place where the new arrow appears in place of the old one, and docs#FunLike.ext_iff for a place where it now is eliminated.
Eric Wieser (Nov 21 2023 at 15:36):
(hmm, this doesn't seem to have affected the docs)
Last updated: Dec 20 2023 at 11:08 UTC