Zulip Chat Archive

Stream: lean4

Topic: conflicting namespaces in mutual declaration


Andrés Goens (Jul 07 2022 at 19:20):

Is there some fundamental reason why we can't mix namespaces in mutually-recursive functions?

As an example (MWE), if I have mutually recursive types:

mutual
inductive Foo
 | somefoo : Foo
 | bar : Bar  Foo

inductive Bar
 | somebar : Bar
 | foobar : Foo  Bar  Bar
end

and want to define some functions on them, say

mutual
def Foo.ToString : Foo  String
| Foo.somefoo => "foo"
| Foo.bar b => Bar.toString b

def Bar.ToString : Bar  String
| Bar.somebar => "bar"
| Bar.foobar f b => (Foo.toString f) ++ (Bar.toString b)
end

Then Lean complains: conflicting namespaces in mutual declaration, using namespace 'Bar', but used 'Foo' in previous declaration

I know that I could solve that easily for this example (and my real-use case), by splitting it up into two "phases":

mutual
def fooToString : Foo  String
  | Foo.somefoo => "foo"
  | Foo.bar b => barToString b

def barToString : Bar  String
  | Bar.somebar => "bar"
  | Bar.foobar f b => (fooToString f) ++ (barToString b)
end

def Foo.toString : Foo  String := fooToString
def Bar.toString : Bar  String := barToString

My question is wether there's any reason (e.g. type-theoretical) why this is difficult for the general case, i.e. why Lean won't support it, or is it "just" busywork to define e.g. a hygenic expansion that does something like this?

Leonardo de Moura (Jul 07 2022 at 23:02):

In Lean, def Foo.bla ... is expanded to namespace Foo def bla ... end Foo. When we defined mutual commands, we wanted to keep the same semantics. This is where the restriction came from. mutual is an atomic command in Lean. To correctly support your example, we would have to simulate the namespace command in the mutual. This is doable, and it will happen in the future.
In the meantime, one can use the new _root_ modifier as a workaround. It is not perfect since the namespace Bar will not be open at Bar.Foo.

mutual
inductive Foo
 | somefoo : Foo
 | bar : Bar  Foo

inductive Bar
 | somebar : Bar
 | foobar : Foo  Bar  Bar
end

mutual
  def Foo.toString : Foo  String
    | Foo.somefoo => "foo"
    | Foo.bar b => b.toString

  def _root_.Bar.toString : Bar  String
    | Bar.somebar => "bar"
    | Bar.foobar f b => f.toString ++ b.toString
end

Leonardo de Moura (Jul 07 2022 at 23:05):

BTW, the example above requires the following fix pushed today https://github.com/leanprover/lean4/commit/fce7697151475aef7d46ebba052952e2148e77a4


Last updated: Dec 20 2023 at 11:08 UTC