Zulip Chat Archive

Stream: mathlib4

Topic: Cannot Find `Real.add`


Kaiyu Yang (Nov 14 2023 at 18:16):

The code below gives me the error unknown constant 'Real.add', but Real.add is defined here. Did I miss something? Thanks!

import Mathlib.Data.Real.Basic

#check Real.add

Kyle Miller (Nov 14 2023 at 18:17):

It's marked private, which means you're not allowed to reference it outside that module.

Kaiyu Yang (Nov 14 2023 at 18:18):

But the code below works. I thought marking something with private just means we need to use its fully qualified name for accessing it.

namespace Foo

private def bar := 1

end Foo

#eval Foo.bar

Kyle Miller (Nov 14 2023 at 18:19):

protected is for fully qualified names.

I guess I don't know the exact semantics of private, but definitely outside the module you can't refer to it anymore. In your example you're still in the same module. (A module isn't a namespace.)

Kyle Miller (Nov 14 2023 at 18:21):

If you want to circumvent the fact that the designers of Real made Real.add private, you can indirectly refer to it using Real.instAddReal.1, since it's the field inside the Add Real instance. But you're supposed to use + instead of Real.add.

Kyle Miller (Nov 14 2023 at 18:21):

#xy-ing, why do you need to refer to Real.add?

Kaiyu Yang (Nov 14 2023 at 18:22):

Ah, I see. I'll try the workaround. Thanks

why do you need to refer to Real.add?

I'm writing some metaprogramming code to translate Lean expressions of real numbers to SMT queries.

Eric Wieser (Nov 14 2023 at 18:23):

Why not match against HAdd.hAdd? (aka +)

Kaiyu Yang (Nov 14 2023 at 18:24):

I guess maybe in my use case I just need to use `Real.add instead of ``Real.add.

Kaiyu Yang (Nov 14 2023 at 18:26):

Why not match against HAdd.hAdd? (aka +)

I'll also try that. Thanks!

Kyle Miller (Nov 14 2023 at 18:27):

Kaiyu Yang said:

I guess maybe in my use case I just need to use `Real.add instead of ``Real.add.

I don't think that would work -- I believe the way private works is that it gets a name that's not a normal identifier, so you can't refer to it.

Eric Wieser (Nov 14 2023 at 18:30):

Kaiyu Yang said:

Why not match against HAdd.hAdd? (aka +)

I'll also try that. Thanks!

Better yet would be to use Qq, as let ~q($a + $b) := e

Kyle Miller (Nov 14 2023 at 18:30):

In particular, x + y is @HAdd.hAdd ℝ ℝ ℝ (@instHAdd ℝ Real.instAddReal) x y, so you can look for @HAdd.hAdd _ _ _ (@instHAdd _ Real.instAddReal) x y (each _ is implied by Real.instAddReal)

Kyle Miller (Nov 14 2023 at 18:31):

(Eric's Qq suggestion could work too. We sent our messages at the exact same time.)

Kaiyu Yang (Nov 14 2023 at 18:32):

I'll try them. Thank you both!

Kyle Miller (Nov 14 2023 at 18:41):

Here's a matcher for real addition. It's not as nice looking as a Qq implementation, and it's less resistant to any changes to the library, but here it is in case it's helpful.

import Mathlib.Data.Real.Basic

open Lean

/-- Given `x + y` of Reals, returns `(x, y)`. Otherwise returns `none`. -/
def Lean.Expr.realAdd? (e : Expr) : Option (Expr × Expr) := do
  guard <| e.isAppOfArity ``HAdd.hAdd 6
  let inst := e.appFn!.appFn!.appArg!
  guard <| inst.isAppOfArity ``instHAdd 2
  guard <| inst.appArg!.isConstOf ``Real.instAddReal
  return (e.appFn!.appArg!, e.appArg!)

open Elab Term in
elab "test" t:term : command => Elab.Command.runTermElabM fun _ => do
  let e  withSynthesize <| Elab.Term.elabTerm t none
  let e  instantiateMVars e
  logInfo m!"e = {e}"
  let r := e.realAdd?
  logInfo m!"Got {r}"

test (1 + 2 : ) -- Got none
test (1 + 2 : ) -- Got some ((1, 2))

Scott Morrison (Nov 14 2023 at 23:28):

I would recommend avoiding using Qq for pattern matching. That part of the Qq implementation is pretty spicy (exciting macros, unclear performance), and we're still thinking about its future.

Scott Morrison (Nov 14 2023 at 23:29):

Kyle's solution is great.

Eric Wieser (Nov 14 2023 at 23:29):

Kyle's solution has a pretty major flaw; it matches instances syntactically rather than up to defeq

Eric Wieser (Nov 14 2023 at 23:30):

So if you have @AddMonoid.toAdd Real.instAddMonoid instead of Real.instAddReal then it doesn't work

Eric Wieser (Nov 14 2023 at 23:30):

(No offence meant Kyle; your code is fine, but it demonstrates just how easy it is to fall into that instance trap when not using Qq if even a metaproramming expert makes that mistake)

Kyle Miller (Nov 14 2023 at 23:33):

I'm offended ;-)

Eric Wieser (Nov 14 2023 at 23:39):

Though I will admit that Qq is not playing ball at all here

Kyle Miller (Nov 14 2023 at 23:45):

This seems to work, but I can't be sure it's correct:

import Mathlib.Data.Real.Basic

open Lean Meta Qq

/-- Given `x + y` of Reals, returns `(x, y)`. Otherwise returns `none`. -/
def Lean.Expr.realAdd? (e : Expr) : MetaM (Option (Q() × Q())) := do
  unless  isDefEq ( inferType e) (.const ``Real []) do
    return none
  have e : Q() := e
  let ~q(($x + $y : )) := e | return none
  return some (x, y)

open Elab Term in
elab "test" t:term : command => Elab.Command.runTermElabM fun _ => do
  let e  withSynthesize <| Elab.Term.elabTerm t none
  let e  instantiateMVars e
  logInfo m!"e = {e}"
  let r  e.realAdd?
  logInfo m!"Got {r}"

test (1 + 2 : ) -- Got none
test (1 + 2 : ) -- Got some ((1, 2))

Kyle Miller (Nov 14 2023 at 23:46):

I looked at the generated code and it seems like it's doing the right thing.

Eric Wieser (Nov 14 2023 at 23:48):

Here's my version:

/-- Given `x + y` of Reals, returns `(x, y)`. Otherwise fail. -/
def Lean.Expr.realAdd? (e : Expr) : MetaM (Expr × Expr) := do
  let Level.succ Level.zero, (t : Q(Type 0)), (e : Q($t))⟩  inferTypeQ e | failure
  let ~q() := t | failure
  let ~q($a + $b) := e | failure
  pure (a, b)

Eric Wieser (Nov 14 2023 at 23:52):

It's a shame Qq doesn't let that be combined into a single pattern match

Eric Wieser (Nov 15 2023 at 00:35):

This looks like a #mwe of the Qq bug:

def foo (e : Nat × Q(Nat)) : MetaM Unit := do
  let .mk u (~q(1)) := e | failure
  return

Eric Wieser (Nov 15 2023 at 01:16):

It was a one-character fix: quote4#26

Eric Wieser (Nov 15 2023 at 01:17):

With that change, you can write

/-- Given `x + y` of Reals, returns `(x, y)`. Otherwise returns `none`. -/
def Lean.Expr.realAdd? (e : Expr) : MetaM (Option (Q() × Q())) := do
  let Level.succ Level.zero, ~q(), ~q($a + $b)⟩  inferTypeQ e | return none
  return some (a, b)

Scott Morrison (Nov 15 2023 at 02:00):

#8415 is the Mathlib bump for this.

Eric Wieser (Nov 15 2023 at 02:19):

quote4#21 is a bit too spicy for me

Eric Wieser (Nov 15 2023 at 13:08):

lean4#2880 makes it so you can write 1 instead of Level.succ Level.zero there


Last updated: Dec 20 2023 at 11:08 UTC