Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check module equality before issuing "Ambiguous name" error #6035

Open
anuyts opened this issue Aug 18, 2022 · 8 comments
Open

Check module equality before issuing "Ambiguous name" error #6035

anuyts opened this issue Aug 18, 2022 · 8 comments
Labels
ambiguous-names Issues to do with reporting of name ambiguity parameters Module parameters scope Issues relating to scope checking type: enhancement Issues and pull requests about possible improvements
Milestone

Comments

@anuyts
Copy link
Contributor

anuyts commented Aug 18, 2022

When I use a defined name A that corresponds to multiple definitions, Agda issues an "Ambiguous name" error. However, if the definitions are actually the same piece of code but accessed through different open or open import statements and the module of A is moreover instantiated each time with the same arguments, then there is no ambiguity and A should simply be used.

Either of the following solutions could work:

  • before reporting ambiguity, check whether all definitions are actually in the same place in the code and their parent modules are instantiated with the same arguments; if yes, there is no ambiguity;
  • before reporting ambiguity, check whether all definitions are judgementally equal; if yes, there is no ambiguity; (I guess this would allow name overloading as a way to have named instances…)

Consider the following small example:

module OverlappingOpens where

postulate _≡_ : {A : Set}  A  A  Set

module Fine where
  module There (A0 : Set) where
    A : Set
    A = A0

  module Thither (A0 : Set) (X : Set) where
    open There A0
    postulate thither : X  A

  module Thence (A0 : Set) (X : Set) where
    open There A0
    postulate thence : A  X

  module Iso (A0 : Set) (X : Set) where
    open There A0
    open Thither A0 X
    open Thence A0 X
    postulate here : (x : X)  thence (thither x) ≡ x
    postulate there : (a : A)  thither (thence a) ≡ a

module Bad where
  module There (A0 : Set) where
    A : Set
    A = A0

  module Thither (A0 : Set) (X : Set) where
    open There A0 public
    postulate thither : X  A

  module Thence (A0 : Set) (X : Set) where
    open There A0 public
    postulate thence : A  X

  module Iso (A0 : Set) (X : Set) where
    open Thither A0 X
    open Thence A0 X
    postulate here : (x : X)  thence (thither x) ≡ x
    postulate there : (a : A)  thither (thence a) ≡ a

In Fine, there is no issue, but in Bad, ambiguity is reported for A. If I replace A with an underscore, the file typechecks, showing that both As (the domain of thence and the codomain of thither) really coincide.

As a real life example, in my draft pull request about (relative) adjunctions, the modules LeftRelativeRightAdhoc and LeftAdhocRightRelative both open Adhoc. The module Birelative opens both LeftRelativeRightAdhoc and LeftAdhocRightRelative, which would lead to consistent openings of Adhoc. However, I had to make LeftRelativeRightAdhoc and LeftAdhocRightRelative open Adhoc non-publicly to avoid ambiguity errors in Birelative. This is already less than ideal.
But I inferred that under the current circumstances, open ... public and open import ... public are in general an annoying thing to do, so I had Birelative export none of the aforementioned modules. Then in NaturalBijection I had to open all four (actually their non-relative counterparts) where I would just have wanted to write open Plain F G public and have that module open the other three. This means that as a library developer, I have little control over the interface of my modules, which makes it hard to make edits with good backwards compatibility.

@andreasabel andreasabel added ambiguous-names Issues to do with reporting of name ambiguity scope Issues relating to scope checking labels Aug 18, 2022
@gallais
Copy link
Member

gallais commented Aug 18, 2022

This may be convenient in the short term but don't you run the risk of building a massive scope
with many duplicated definitions thus slowing down compilation?

@plt-amy
Copy link
Member

plt-amy commented Aug 18, 2022

Relevant: #4331

.. by which I mean that in a structured signature setup, it'd be possible to cheaply implement this, I think?

@jespercockx jespercockx added the type: enhancement Issues and pull requests about possible improvements label Aug 23, 2022
@jespercockx jespercockx added this to the icebox milestone Aug 23, 2022
@andreasabel andreasabel modified the milestones: icebox, later Oct 8, 2022
@andreasabel
Copy link
Member

Me too. Just ran into the situation of the OP. Very annoying to see an error like:

Issue6035.agda:41,28-29
Ambiguous name A. It could refer to any one of
  Issue6035.Bad.Iso.A bound at
    Issue6035.agda:26,5-6
  Issue6035.Bad.Iso.A bound at
    Issue6035.agda:26,5-6
(hint: Use C-c C-w (in Emacs) if you want to know why)
when scope checking A

(This is the error triggered by the OP.)
Agda's report is non-sensical, claiming that the name is ambiguous, and then proving a breath later that its denotation (the abstract name behind it) is non-ambiguous.
The detailed explanation is:

A is in scope as
  * a defined name Issue6035.Bad.Iso._.A brought into scope by
    - the application of Thither at Issue6035.agda:38,10-17
    - the application of There at Issue6035.agda:30,10-15
    - its definition at Issue6035.agda:26,5-6
  * a defined name Issue6035.Bad.Iso._.A brought into scope by
    - the application of Thence at Issue6035.agda:39,10-16
    - the application of There at Issue6035.agda:34,10-15
    - its definition at Issue6035.agda:26,5-6

Ok, there are different paths ending in the same target, but why should this be an error? @gallais argues that it might be a useful information that the module setup has redundancies, but it would suffice to print a warning instead.
In my case it was even just a variable that is "ambiguous". I could not care less.

@plt-amy
Copy link
Member

plt-amy commented Oct 8, 2022

Do you think we could improve this situation slightly by adding the C-c C-w information to the error report when the names look the same?

@andreasabel
Copy link
Member

Ah, yes, I just also thought we could always directly expand the "hint" given at the end of the error. This information is available where we raise the error, yet we throw it away instead of passing it to the error message.

@andreasabel andreasabel self-assigned this Oct 9, 2022
@andreasabel
Copy link
Member

Investigating the problem, I found that the two names rendered in the error message have different NameIds, so, for the scope checker they are really different. This becomes apparent if we name all the intermediate modules created by open M args statements. Here it is, with a further shrinking of the OP:

module Issue6035 where

module M0 (A0 : Set) where
  A : Set
  A = A0

module M1 (A1 : Set) where
  open module M01 = M0 A1 public

module M2 (A2 : Set) where
  open module M02 = M0 A2 public

module M3 (A3 : Set) where
  open module M1' = M1 A3
  open module M2' = M2 A3
  B = A

With the intermediate names, the error also becomes more plausible:

Ambiguous name A. It could refer to any one of
  M1'.A 
  M2'.A

Since Agda's treatment of module is nominal rather than structural, there isn't much to do here except for making the error a bit clearer --- unless we redesign and reimplement the handling of module applications.

andreasabel added a commit that referenced this issue Oct 9, 2022
Also move explainWhyInScope to TypeChecking.Errors
andreasabel added a commit that referenced this issue Oct 10, 2022
Also move explainWhyInScope to TypeChecking.Errors
@andreasabel andreasabel removed their assignment Oct 10, 2022
@andreasabel
Copy link
Member

Do you think we could improve this situation slightly by adding the C-c C-w information to the error report when the names look the same?

I implemented this in:

@andreasabel andreasabel modified the milestones: later, icebox Oct 10, 2022
@andreasabel andreasabel added the parameters Module parameters label Oct 10, 2022
@nad
Copy link
Contributor

nad commented Oct 10, 2022

Should this be done also for AmbiguousModule?

AmbiguousName x reason -> vcat
[ fsep $ pwords "Ambiguous name" ++ [pretty x <> "."] ++
pwords "It could refer to any one of"
, nest 2 $ vcat $ fmap nameWithBinding $ ambiguousNamesInReason reason
, explainWhyInScope $ whyInScopeDataFromAmbiguousNameReason x reason
]
AmbiguousModule x ys -> vcat
[ fsep $ pwords "Ambiguous module name" ++ [pretty x <> "."] ++
pwords "It could refer to any one of"
, nest 2 $ vcat $ fmap help ys
, fwords "(hint: Use C-c C-w (in Emacs) if you want to know why)"
]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ambiguous-names Issues to do with reporting of name ambiguity parameters Module parameters scope Issues relating to scope checking type: enhancement Issues and pull requests about possible improvements
Projects
None yet
Development

No branches or pull requests

6 participants