Zulip Chat Archive

Stream: lean4

Topic: Function dot notation error


Kyle Miller (Sep 01 2022 at 23:53):

Is dot notation on functions supposed to respect which arguments are explicit vs implicit?

In this example, it appears dot notation is inserting the function as the implicit argument:

def Function.foo {f : α  β} (g : β  γ) := g  f
variable (f : α  β) (g : β  γ)
#reduce f.foo
-- fun g x => g (f x)

This is what I was experimenting with until I realized it might be that dot notation is trying to fill in γ in Function.swap:

def Function.swap {α β} {γ : α  β  Sort _} (f : (a : α)  (b : β)  γ a b)
  (b : β) (a : α) : γ a b := f a b

def mul : Nat  Nat  Nat := (· * ·)
#check Function.swap mul -- works
#check mul.swap
/-
:6:7 application type mismatch
  @Function.swap ?m.151 ?m.152 mul
argument
  mul
has type
  Nat → Nat → Nat : Type
but is expected to have type
  ?m.151 → ?m.152 → Sort ?u.142 : Sort (max (max (?u.142 + 1) ?u.143) ?u.144)
-/
#check (mul.swap 1 2 : Nat)
/-
:17:17 invalid universe level, ?u.264 is not greater than 0

:17:8 application type mismatch
  @Function.swap ?m.239 ?m.240 mul
argument
  mul
has type
  Nat → Nat → Nat : Type
but is expected to have type
  ?m.239 → Nat → Sort ?u.230 : Sort (max (?u.230 + 1) ?u.232)
-/
#check (Function.swap mul : Nat  Nat  Nat) 1 2 -- works
#check (mul.swap : Nat  Nat  Nat) 1 2
/-
:31:8 application type mismatch
  @Function.swap ?m.1489 ?m.1490 mul
argument
  mul
has type
  Nat → Nat → Nat : Type
but is expected to have type
  ?m.1489 → ?m.1490 → Sort ?u.1480 : Sort (max (max (?u.1480 + 1) ?u.1481) ?u.1482)

:31:7 type mismatch
  Function.swap
has type
  ((a : ?m.1489) → (b : ?m.1490) → ?m.1511 a b) →
    (b : ?m.1490) → (a : ?m.1489) → ?m.1511 a b : Sort (imax (imax ?u.1482 ?u.1481 ?u.1480) ?u.1481 ?u.1482 ?u.1480)
but is expected to have type
  Nat → Nat → Nat : Type
-/

Kyle Miller (Sep 02 2022 at 01:45):

Oh, I didn't realize that Lean 4 has changed how dot notation works from Lean 3 like this. In Lean 3 dot notation looks for the first explicit argument with the right type, but now it allows implicit arguments too. (It looks like Lean 4 started the Lean 3 way but changed in this commit from 2020).

A short example illustrating the difference:

structure S where
  n : Nat

def S.foo {s : S} : Nat := s.n * 2

def s : S := 37

#eval s.foo
/- works, but in Lean 3 would give
invalid field notation, function 'S.foo' does not have explicit argument with type (S ...)
-/

Kyle Miller (Sep 02 2022 at 02:28):

Here's an example (in addition to Function.swap) that I might have expected to work with dot notation, but the function arguments conflict with the type family arguments:

def Function.pi_comp {β γ : α  Sort _}
  (g : (x : α)  β x  γ x) (f : (x : α)  β x) (x : α) : γ x :=
g x (f x)

theorem forall_imp {p q : α  Prop}
  (h :  a, p a  q a) (h' :  a, p a) :  a, q a :=
h.pi_comp h'
/-
type mismatch
  Function.pi_comp ?m.171
has type
  ((x : ?m.124) → ?m.144 x) → (x : ?m.124) → ?m.145 x : Sort (imax (imax ?u.117 ?u.116) ?u.117 ?u.115)
but is expected to have type
  ∀ (a : α), q a : Prop
-/

Kevin Buzzard (Sep 02 2022 at 05:53):

I guess I certainly remember thinking more than once "grr, I wish lean 3 would let dot notation work here even though the argument I want to insert is implicit"

Kyle Miller (Sep 02 2022 at 17:06):

Very brief RFC:

As a first step when inserting arguments for an LVal in addLValArg, the elaborator should look for an argument named self, check that it is the only argument with that name (otherwise raise an error), and then use that argument for the expression for the field notation no matter the baseName.

This (1) allows one to use field notation to work for Function even if there are type family arguments like in the above examples and (2) allows one to use field notation for extension methods that make use of typeclass instances (or coercions) associated to a type.

Sebastian Ullrich (Sep 02 2022 at 17:17):

Do you have an example for your second use case? The function must still be in the type's namespace for the elaborator to find it.

Kyle Miller (Sep 02 2022 at 17:32):

@Sebastian Ullrich Yes, I have a number of example applications (this goes back to some posts I had about graphs and getting G.adj u v to work for G having a type that implement a HasAdj class).

Here's an example from a recent mathlib discussion that would be useful, since many different types re-implement a .nonempty predicate in the same way, and we like dot notation in mathlib for nonempty:

class HasMem (α : outParam $ Type _) (β : Type _) where
  mem : α  β  Prop

infix:50 " ∈ " => HasMem.mem

def HasMem.nonempty [HasMem α β] (self : β) : Prop :=
 x, x  self

def Set (α : Type _) := α  Prop

instance : HasMem α (Set α) where
  mem x s := s x

namespace Set
export HasMem (nonempty)
end Set

variable (s : Set α)
#check s.nonempty -- currently an error

Sebastian Ullrich (Sep 02 2022 at 18:13):

I see, it makes sense in combination with export

Victor Maia (Sep 02 2022 at 19:00):

how do I import a file in the same dir? I'm trying import Base, but it says nat_exp.lean:1:0: error: unknown package 'Base', even though there is a file named Base.lean on the same directory, which type-checks just fine

Kevin Buzzard (Sep 02 2022 at 19:35):

@Victor Maia if this question is unrelated to the above discussion can you ask it in a new thread?

Victor Maia (Sep 02 2022 at 19:36):

oops, sorry - I'm new to Zulip

Kevin Buzzard (Sep 02 2022 at 19:39):

No problem!

Kyle Miller (Sep 07 2022 at 22:05):

Would it be worth fleshing out the mini-RFC at https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/Function.20dot.20notation.20error/near/296860982 further? Or is it clear what the problem is and what sorts of applications would be enabled by letting a user specify the argument that field notation applies to?

There are a couple alternative designs to the simplistic one of using an argument named self:

  • Use a type annotation for the argument that should receive the object of the field notation.
  • Use an attribute to specify the name of the argument that should receive the object of the field notation. (This also makes the feature more opt-in.)

Leonardo de Moura (Sep 07 2022 at 22:24):

@Kyle Miller Yes, a GitHub issue would be perfect.


Last updated: Dec 20 2023 at 11:08 UTC