Zulip Chat Archive

Stream: new members

Topic: Scope of notation inside a namespace


Luiz Kazuo Takei (Jan 14 2026 at 21:22):

Correct me if I am wrong but my understanding is that definitions inside a namespace have priority over definitions outside that namespace. So, for instance, the code below would work fine:

namespace TestSpace

-- yes, this definition is wrong but just for the sake of the example
def Even (n : ) :=  (k : ), n = 2*k + 1

example : Even 5 := by
  use 2

end TestSpace

Now I tried to do something similar for an infix notation (that clashes with a notation defined elsewhere in mathlib) and Lean gives me an error message saying that it is an ambiguous term. Here's the code:

namespace TestSpace

def Divides (a b : ) :=  (k : ), b = k*a

infix:99 "∣" => Divides

example : 5  20 := by sorry

end Testspace

Why doesn't it use the notation defined in the namespace by default? Also, can I force it to use the definition inside the namespace?

Mirek Olšák (Jan 14 2026 at 22:36):

Notation and a name is quite a different thing. Notation is even global by default (still applicable after leaving the namespace but that can be changed with scoped notation). I am not sure if you can delete a default notation (some smarter people here could know).

Hovewer, a standard way of defining your own division is through implementing a custom Dvd typeclass (which can even handle ambiguous typeclass resolutions).

Eric Paul (Jan 15 2026 at 02:26):

Here are two options which are probably not the best but may be interesting. The first is changing priority and the second is removing the default notation as Mirek Olšák mentioned.

When two notations work equally well, Lean tries choosing the one with higher priority. The default priority is 1000, so by giving your notation any priority higher than that, it becomes the one Lean chooses to use:

import Mathlib

namespace TestSpace

def Divides (a b : ) :=  (k : ), b = k*a

infix:99 (priority := 1001) "∣" => Divides

example : 5  20 := by sorry

end TestSpace

Now here is a way to remove previously existing notation and replace it with your own. I think this is probably unreasonable but is fun and emphasizes the fact that Lean's parser is entirely customizable.

import Mathlib
import Lean
open Lean Parser

-- For each first token of `p`, set list of relevant parsers to just be `p`
def setTrailingParser (tables : PrattParsingTables) (p : TrailingParser) (prio : Nat) :
    PrattParsingTables :=
  let addTokens (tks : List Token) : PrattParsingTables :=
    let tks := tks.map fun tk => Name.mkSimple tk
    tks.eraseDups.foldl (init := tables) fun tables tk =>
      { tables with trailingTable := Std.TreeMap.insert tables.trailingTable tk [(p, prio)] }
  match p.info.firstTokens with
  | FirstTokens.tokens tks    => addTokens tks
  | FirstTokens.optTokens tks => addTokens tks
  | _ => tables

elab "makeOnlyParser " p:ident : command => do
  let cats := parserExtension.getState (getEnv) |>.categories
  let parserName  resolveGlobalConstNoOverload p
  let (_, parser)  Elab.Command.liftTermElabM <| mkParserOfConstant cats parserName
  modifyEnv fun env =>
    parserExtension.modifyState env fun state =>
        let termCat := state.categories.find! `term
        let newTables := setTrailingParser termCat.tables parser 1000
        let newCats := state.categories.insert `term {termCat with tables := newTables}
        {state with categories := newCats}

namespace TestSpace

def Divides (a b : ) :=  (k : ), b = k*a

infix:99 (name := myDiv) "∣" => Divides
makeOnlyParser myDiv

example : 5  20 := by sorry
-- The default notation is gone in this namespace
example [Field α] (x y : α) : x  y := by sorry

end TestSpace
-- Still works outside of this namespace
example [Field α] (x y : α) : x  y := by sorry

Luiz Kazuo Takei (Jan 15 2026 at 10:41):

Thank you very much for the answers @Mirek Olšák and @Eric Paul !

@Eric Paul, the 1st solution works very well. The 2nd solution is interesting but way above my level... :joy:


Last updated: Feb 28 2026 at 14:05 UTC