## Stream: new members

### Topic: Custom notation inside structures

#### Kuba (May 18 2023 at 23:37):

Hi,
I have a structure whose constructor takes, among other things, a quaternary congruence relation and proofs of some of its properties as arguments. I would like to define a custom notation, so that I can write a b ≅ c d instead of congruence a b c d. However, if I use the notation command and then try to use the newly defined notation, I get an error.
My code:

structure space where
(point_t : Type)
(congruence : point_t → point_t → point_t → point_t → Prop)
notation a b " ≅ " c d => space.congruence a b c d
(symmetry_of_congruence : ∀ a b : point_t, a b ≅ b a)


Error message (relating to the congruence symbol on the last line):

expected token


#### Chris Bailey (May 19 2023 at 04:19):

The parser won't let you declare notation inside a structure declaration. You would want to declare the notation beforehand somewhere in the syntax/macro/notation hierarchy. One way of doing it (someone can probably give you a more clean implementation):

set_option hygiene false in
macro a:term b:term " ≅ " c:term:max d:term : term => (congruence $a$b $c$d)

structure space where
(point_t : Type)
(congruence : point_t → point_t → point_t → point_t → Prop)
(symmetry_of_congruence : ∀ a b : point_t, a b ≅ b a)

#print space


#### Eric Wieser (May 19 2023 at 05:58):

Does putting the notation command in () help?

#### Eric Wieser (May 19 2023 at 05:59):

Oh nevermind, this is lean4

#### Kuba (May 22 2023 at 23:24):

Hmm, thanks for the answer, but I feel a bit queasy about disabling an option that has "hygiene" in its name. Does anyone know whether there is a cleaner way of doing this? I would have thought that my use case is a pretty common one. Or maybe the problem is with me using a lean structure here? What I am trying to achieve, is to describe a space made up of points and equipped with two relations that satisfy certain axioms. Both the points and the relations are primitive notions.

#### Reid Barton (May 23 2023 at 11:42):

Of course you can always manually expand the notation while defining the structure, and the worst that can happen is it may be hard to read/write.

#### Reid Barton (May 23 2023 at 11:42):

Sometimes it could also make sense to split the structure into two parts, and define the notation in between.

#### Chris Bailey (May 23 2023 at 13:55):

Kuba said:

Hmm, thanks for the answer, but I feel a bit queasy about disabling an option that has "hygiene" in its name. Does anyone know whether there is a cleaner way of doing this?

macro a:term b:term " ≅ " c:term:max d:term : term =>
($(mkIdent congruence)$a $b$c \$d)


(This produces pretty much identical code, but if it makes you feel better to not write the word hygiene, go for it)

#### Kyle Miller (May 24 2023 at 10:01):

"Hygienic macros" refers to making sure that identifiers in a macro expansion do not accidentally refer to local variables -- here, you do want congruence to refer to the local congruence. Think of it not that your macro is dirty, but that the hygiene system doesn't understand what you're trying to do.

Since congruence is the only literal identifier in your macro expansion, set_option hygiene false will only has affect on the handling of that one identifier.

#### Kuba (May 24 2023 at 21:21):

I see, that makes sense. What I ended up doing was separating out the two relations as separate type classes and then having the axioms as a structure. This is what I ended up with (the code has grown a bit since I started):

set_option autoImplicit false

class Congruence (point_t : Type) where
(congruence : point_t → point_t → point_t → point_t → Prop)
notation:37 a:38 b:38 " ≅ " c:max d:38 => Congruence.congruence a b c d

class Betweennes (point_t : Type) where
(betweennes : point_t → point_t → point_t → Prop)
notation:37 a:38 " B " b:max c:38 => Betweennes.betweennes a b c

-- Five Segment Configuration
def FSC {point_t : Type} [Congruence point_t] [Betweennes point_t]
(a b c d : point_t) (a' b' c' d' : point_t) : Prop :=
b B a c ∧
b' B a' c' ∧
a b ≅ a' b' ∧
b c ≅ b' c' ∧
a d ≅ a' d' ∧
b d ≅ b' d'

structure TarskisAxioms (point_t) [Congruence point_t] [Betweennes point_t] where
(congruence_of_reversed : ∀ a b : point_t, a b ≅ b a)
(transitivity_of_congruence :
∀ a b p q r s : point_t, a b ≅ p q ∧ a b ≅ r s → p q ≅ r s)
(identity_axiom_for_congruence : ∀ a b c : point_t, a b ≅ c c → a = b)
(segment_extension_axiom :
∀ o p a b : point_t, ∃ q : point_t, p B o q ∧ p q ≅ a b)
(five_segment_axiom :
∀ a b c d a' b' c' d' : point_t,
FSC a b c d a' b' c' d' ∧ a ≠ b → c d ≅ c' d')
(identity_axiom_for_betweennes : ∀ a b : point_t, a B b b → a = b)
`

Does this approach make sense? I think this may be similar to how mathlib does it, but I only took a brief look. Also, how do you choose the precedence values? I think I managed to tune them by trial and error, but I am wondering if there is a recipe to follow.

Last updated: Dec 20 2023 at 11:08 UTC