Zulip Chat Archive

Stream: new members

Topic: construct a name for a new def


Mark Aagaard (Feb 26 2025 at 00:38):

I'm teaching a course on logic to first year computer engineering students. One of the topics is formalizing English sentences in logic. To do this, we construct some uninterpreted types and functions, then write additional formulas in terms of these uninterpreted types and functions.

Sometimes we need to define an ordering relation (e.g., ≤) on the uninterpreted type. I would like the students to be able to write:

axiom Rank : Type
Overload () Rank

and have Overload (≤) Rank expand to:

axiom LE_Rank : Rank  Rank  Prop
instance : LE Rank where
  le := LE_Rank

The macro I have written is:

macro "Overload" "(≤)" ty:term : command =>
  `(axiom LE_foo : $ty  $ty  Prop
    instance : LE $ty where
      le := LE_foo
  )

which works, except that the name of the definition LE_foo is hard coded. I would like to construct a name based on $ty.

-mark

Eric Wieser (Feb 26 2025 at 01:11):

How's this?

import Lean

open Lean

elab cmd:"Overload" "(≤)" ty:term : command => do
  let name  Elab.Command.liftTermElabM do
    let e  Lean.Elab.Term.elabTerm ty none
    let .const name _  Meta.whnf e | throwErrorAt ty "Could not guess name"
    return name

  let ident  mkIdentFromRef (name ++ `LE)

  Elab.Command.elabCommand <|  `(
    axiom $ident : $ty  $ty  Prop
    instance : LE $ty where
      le := $ident)

  Elab.addConstInfo cmd (name ++ `LE)


axiom Rank : Type
Overload () Rank

Eric Wieser (Feb 26 2025 at 01:13):

Though axiom is not a great pattern here

Eric Wieser (Feb 26 2025 at 01:14):

If you do really want axiom, you could also skip the Overload syntax with @[instance] axiom Rank.instLE : LE Rank

Mark Aagaard (Feb 26 2025 at 01:55):

@Eric Wieser - thanks.

One question, you wrote "axiom is not a great pattern here". What would be a better alternative? I chose axiomsimply because it was the first idea I had that worked.

-mark

Aaron Liu (Feb 26 2025 at 02:14):

You can use variable

Mark Aagaard (Feb 26 2025 at 15:59):

I originally tried variable, but ran into problems:

variable (Ty:Type)
variable (p : Ty  Prop)
def q (x:Ty) := p x
def r (x:Ty) := q x

gives an error on q x, because Lean expects the first argument of q to be an instantiation of p.

If either Ty or x is a variable, I get an error with the q x in the definition of r. For Ty, I could use either inductive Ty or axiom Ty : Type. For p, the only solution I've found is axiom:

inductive Ty
axiom p : Ty  Prop
def q (x:Ty) := p x
def r (x:Ty) := q x

-mark

Eric Wieser (Feb 26 2025 at 16:10):

Another option is to use

class MyContext (Ty : Type*) where
  p : Ty  Prop

export MyContext (p)

variable [MyContext Ty]

def q (x : Ty) := p x

This has the big advantage over axiom that you can go back and apply these results to some concrete Ty

Robin Arnez (Feb 26 2025 at 16:20):

If you don't care about which relation you get, you can use opaque instead of axiom (the rhs is just to show that LE $id is inhabited):

import Lean.Elab.Command

open Lean Elab Command in

elab cmd:"Overload" "(≤)" id:ident : command => do
  let name  liftCoreM (realizeGlobalConstNoOverload id)
  let ident  mkIdentFromRef (name ++ `LE)
  elabCommand <|  `(@[instance] opaque $ident : LE $id := fun _ _ => True)
  addConstInfo cmd (name ++ `LE)

axiom MyType : Type
Overload () MyType

Although I'm not quite sure why you'd want an LE relation without any knowledge about its properties.

Mark Aagaard (Feb 26 2025 at 16:31):

Aaron, Eric, Robin -
Thanks for the insightful answers. It's very helpful to see a variety of alternatives.

@Robin Arnez : I'm teaching students to formalize English sentences into logic. The first step is to define an environment of uninterpreted types, functions, relations, etc. Hence, I want < as uninterpreted.

-mark

Matt Diamond (Feb 26 2025 at 23:48):

@Robin Arnez I would do it in exactly the opposite way: define opaque MyType : Type and then set up LE MyType as an axiom

Matt Diamond (Feb 26 2025 at 23:50):

but maybe it's just a question of personal style

Matt Diamond (Feb 26 2025 at 23:52):

opaque is good because it prevents the introduction of a proof of false (or at least that's my understanding)

Robin Arnez (Feb 28 2025 at 15:53):

Or both opaque! And yes, axioms by nature can introduce proofs of False if you are not careful enough. That, and it's slightly weird to see a ton of obscure axioms when you look at #print axioms (which is probably not relevant here but I just wanted to note).

Mark Aagaard (Feb 28 2025 at 17:31):

I like the idea of making both the type and LE function opaque.

@Robin Arnez - I'm having difficulty understanding your elaboration function. Superficially, it looks like it is the equivalent of:

opaque Ty1 : Type
@[instance] opaque LE_Ty1 : LE Ty1 := (fun _ _ => True)

But, the above doesn't typecheck because fun _ _ => True doesn't have the type LE Ty1. What am I missing?

Aaron Liu (Feb 28 2025 at 17:52):

Not (fun _ _ => True), but ⟨fun _ _ => True⟩.

Aaron Liu (Feb 28 2025 at 17:52):

(note the brackets)

Mark Aagaard (Feb 28 2025 at 18:12):

Ah, ok. Thanks. I've been bitten by that before when transcribing code. I should have thought of the brackets.


Last updated: May 02 2025 at 03:31 UTC