Zulip Chat Archive

Stream: lean4

Topic: notation over field


Tomas Skrivan (Feb 01 2024 at 15:43):

In calculus and linear algebra you have to specify the field over which you are working over and over. I find this annoying. Especially when doing physics we almost exclusively work over real numbers. Specifying it everywhere just adds noise and is tedious. Here is my solution to that:

Init file

initialize currentFieldName : IO.Ref Name  IO.mkRef default

elab "open_notation_over_field" K:ident : command => do
  currentFieldName.set K.getId
  Lean.Elab.Command.elabCommand <| 
   `(open NotationOverField)

Example use on continuous linear map

namespace NotationOverField
open Lean Elab Term
scoped elab X:term " →L " Y:term : term => do
  let K  NotationOverField.currentFieldName.get
  let K := mkIdent K
  elabTerm ( `(term| $X L[$K] $Y)) none
end NotationOverField

open_notation_over_field Real

#check  L 

There are two downsides

  1. then notation ℝ →L ℝ is not a macro anymore
  2. the global reference currentFieldName can have a some random value from imported files which might work or not

Is there a way to set the value of currentFieldName just for the current file? Reset it once you are done elaborating the file where you call open_notation_over_field K.

Maybe using IO.Ref is a bad approach, can this be done in a different way?

Kyle Miller (Feb 01 2024 at 16:41):

How about this?

import Mathlib

syntax "currentBaseRing%" : term

macro_rules | `(currentBaseRing%) => Lean.Macro.throwError "\
  There is no current base ring for this notation. \
  To set one, add the following local macro rules \
  and adjust '𝕜' to be your desired base ring.\
  \n\n  local macro_rules | `(currentBaseRing%) => `(𝕜)"

notation:25 M " →L " M₂ => ContinuousLinearMap (RingHom.id currentBaseRing%) M M₂

#check  L 
/-
There is no current base ring for this notation. To set one, add the following local macro rules
and adjust '𝕜' to be your desired base ring.

  local macro_rules | `(currentBaseRing%) => `(𝕜)
-/

local macro_rules | `(currentBaseRing%) => `()

#check  L 
-- ℝ →L[ℝ] ℝ : Type

Kyle Miller (Feb 01 2024 at 16:42):

Since it's a local macro rule, it's also scoped to the current section/namespace.

Tomas Skrivan (Feb 01 2024 at 16:43):

Ahh this is a much neater solution! And gives a nice error.

Adam Topaz (Feb 01 2024 at 16:45):

What about this:

import Mathlib

syntax "currentBaseRing%" : term

macro_rules | `(currentBaseRing%) => Lean.Macro.throwError "
  There is no current base ring for this notation.
  To set one, add the following local macro rules
  and adjust '𝕜' to be your desired base ring.
  \n\n  local macro_rules | `(currentBaseRing%) => `(𝕜)"

notation:25 M " →L " M₂ => ContinuousLinearMap (RingHom.id currentBaseRing%) M M₂

#check  L 
/-
There is no current base ring for this notation. To set one, add the following local macro rules
and adjust '𝕜' to be your desired base ring.

  local macro_rules | `(currentBaseRing%) => `(𝕜)
-/

macro "set_base_ring" r:term : command =>
  `(local macro_rules | `(currentBaseRing%) => `($r))

set_base_ring 

#check  L 

Adam Topaz (Feb 01 2024 at 16:46):

modulo modifying Kyle's error message...

Kyle Miller (Feb 01 2024 at 16:47):

(Did you remove the trailing \s because you're you using an old Mathlib? Those eliminate the whitespace at the beginning of the following line.)

Adam Topaz (Feb 01 2024 at 16:47):

Yeah I'm probably using an older mathlib

Adam Topaz (Feb 01 2024 at 16:47):

it's in some scrap project I have on my machine

Kyle Miller (Feb 01 2024 at 16:50):

In case you need this on an older Lean, this is what you could write instead of using \:

macro_rules | `(currentBaseRing%) => Lean.Macro.throwError s!"{
  ""}There is no current base ring for this notation. {
  ""}To set one, add the following local macro rules {
  ""}and adjust '𝕜' to your desired base ring.{
  ""}\n\n  local macro_rules | `(currentBaseRing%) => `(𝕜)"

Kyle Miller (Feb 01 2024 at 16:51):

(I'm glad that's not the style anymore!)

Kim Morrison (Feb 01 2024 at 21:49):

This is appealing. (Works fine with variables too.)

Kim Morrison (Feb 01 2024 at 21:50):

I'm a little concerned that adding local macros in the files in this manner does make maintenance harder: you can't move lemmas around as easily.

Kim Morrison (Feb 01 2024 at 21:50):

(The problem is only as bad as the same problem caused by variable.)

Adam Topaz (Feb 01 2024 at 21:51):

the behavior seems similar to parameter in lean3

Tomas Skrivan (Feb 01 2024 at 21:52):

Scott Morrison said:

This is appealing. (Works fine with variables too.)

Yes exactly, the point is that you do

variable (K : Type) [Ring K]
set_base_ring K

And then you forget about it

Kim Morrison (Feb 01 2024 at 21:53):

We just need autoImplicit for typeclasses, and can just write set_base_ring K! (Mostly joking.)

Kyle Miller (Feb 01 2024 at 22:00):

Here's a hack, not sure how well it works in practice:

import Mathlib

class CurrentBaseRing (R : outParam <| Type*)

syntax "currentBaseRing%" : term

open Lean Meta in
elab_rules : term | `(currentBaseRing%) => do
  let R  mkFreshTypeMVar
  let res?  synthInstance? <| Expr.app ( mkConstWithFreshMVarLevels ``CurrentBaseRing) R
  if res?.isSome then
    return R
  else
    throwError "\
      There is no current base ring for this notation. \
      To set one, add\
      \n\n  variable [CurrentBaseRing 𝕜]\
      \n\nwith an appropriate `𝕜`."

notation:25 M " →L " M₂ => ContinuousLinearMap (RingHom.id currentBaseRing%) M M₂

#check  L 
/-
There is no current base ring for this notation. To set one, add

  variable [CurrentBaseRing 𝕜]

with an appropriate `𝕜`.
-/

variable [CurrentBaseRing ]
#check  L 
-- ℝ →L[ℝ] ℝ : Type

Adam Topaz (Feb 01 2024 at 22:41):

Lean 4 is fun :)

Adam Topaz (Feb 01 2024 at 22:56):

We can take this further:

import Mathlib

class BaseTopologicalRing (R : outParam <| Type*) extends
  CommRing R, TopologicalSpace R, TopologicalRing R

syntax "currentBaseRing%" : term

open Lean Meta in
elab_rules : term | `(currentBaseRing%) => do
  let R  mkFreshTypeMVar
  let res?  synthInstance? <| Expr.app ( mkConstWithFreshMVarLevels ``BaseTopologicalRing) R
  if res?.isSome then
    return R
  else
    throwError "foobar"

notation:25 M " →L " M₂ => ContinuousLinearMap (RingHom.id currentBaseRing%) M M₂

variable [BaseTopologicalRing R]
#check R L R

Adam Topaz (Feb 01 2024 at 22:56):

Alternatively we could synthesize the additional required instances in the elaborator instead of the class.

Eric Wieser (Feb 01 2024 at 22:59):

That looks like a bad idea to me, since the resulting lemmas then actually mention BaseTopologicalRing unlike Kyle's solution where the typeclass is an invisible implementation detail to downstream users

Adam Topaz (Feb 01 2024 at 22:59):

ok, so let's do it in the elaborator

Eric Wieser (Feb 01 2024 at 23:02):

I think

class BaseTopologicalRing (R : outParam <| Type*) [CommRing R] [TopologicalSpace R] [TopologicalRing R]

would be fine

Adam Topaz (Feb 01 2024 at 23:03):

well, what I'm trying to get to work is that writing variable [BaseRing R] would either synthesize a preexisting instance and set the base ring, or if it can't find such an instance, at the variable to the local context.

Adam Topaz (Feb 01 2024 at 23:04):

I guess what I wrote above would not do that, so it was wrong for various reasons.

Kyle Miller (Feb 02 2024 at 00:16):

You could use variable? for this:

import Mathlib.Tactic

import Mathlib

class BaseTopologicalRing (R : outParam <| Type*)
  [CommRing R] [TopologicalSpace R] [TopologicalRing R]

syntax "currentBaseRing%" : term

open Lean Meta in
elab_rules : term | `(currentBaseRing%) => do
  let fn  mkConstWithFreshMVarLevels ``BaseTopologicalRing
  let (args, _, _)  forallMetaTelescope ( inferType fn)
  let R := args[0]!
  let res?  synthInstance? <| mkAppN fn args
  if res?.isSome then
    return R
  else
    throwError "\
      There is no current base ring for this notation. \
      To set one, add\
      \n\n  variable [BaseTopologicalRing 𝕜]`\
      \n\nwith an appropriate `𝕜`."

notation:25 M " →L " M₂ => ContinuousLinearMap (RingHom.id currentBaseRing%) M M₂

#check  L 
/-
There is no current base ring for this notation. To set one, add

  variable [CurrentBaseRing 𝕜]`

with an appropriate `𝕜`.
-/

section
variable [BaseTopologicalRing ]
#check  L 
-- ℝ →L[ℝ] ℝ : Type
end

section
variable (𝕜 : Type*)
variable? [BaseTopologicalRing 𝕜]
#check 𝕜 L 𝕜
-- 𝕜 →L[𝕜] 𝕜 : Type u_1
end

Last updated: May 02 2025 at 03:31 UTC