Scope of imported names

Karl-Filip Faxen kff@it.kth.se
Mon, 22 Oct 2001 11:34:00 +0200


Hi all!

I have been thinking about the scoping issues for imported names. Of
course, this musing is ispired by the formal static semantics I have
been working on, but it is also prompted by the revised Haskell report.

Section 5.5.2 relates to name clashes and has an interesting example
towards the end:

  module F where

    sin :: Float -> Float
    sin x = (x::Float)

    f x = Prelude.sin (F.sin x)

where the type signature refers to the local "sin" rather than the imported
although none of them is visible unqualified. These rules are quite tricky
to understand, I think. They are also different in spirit from the rules
for instance declarations in section 4.3.2 where the binding occurrences
for the names of the methods must be qualified if the unqualified method 
name is not in scope. In the "sin" example it is allowed to resolve the 
name clash using the "extra" knowledge that it is illegal to provide type
signatures for imported names, wheras in the case for instance declarations
we may not use the corresponding "extra" knowledge that only methods in 
the instance'd class may be bound by the bindings.

What I'm driving at is this: I propose that top level bindings shadow
imported names and that qualified names can not be used to refer to
declarations in the same module. 

So. Now I've said it. It feels much better now.

The first part is, as far as I can see, a compatible change. All programs 
will mean the same things as before but some additional programs will be 
legal. Of course, using qualified names to refer to top level declarations 
now becomes a redundant feature, and I would very much like it removed from
the language, but this second change breaks programs. Maybe that is not
acceptable for this revision and clarification. Or maybe it is? There is
actually a mechanical translation from the old rules to the new:

If the module name is M, then some occurences of qualified names of the
form M.name, where "name" may be a name of a type, constructor, class or
variable (including method or field name) refer to top level declarations 
in M itself. Call these "local qualified names". Now do the following:

1. For each occurrence of a local qualified name M.f such that "f"
   is also visible and refers to a different binding (which must be a
   nested binding), rename that binding (ie alpha convert it).
2. Replace any local qualified name M.name with name.

So as far as incompatible changes go, this one is rather benign, I think.

I think that accidental name clashes with imported names are annoying,
but there are more formal reasons for that change. Consider the definition
below, occurring in module M:

   f = ... M.f ...

Is it recursive? That depends on whether it is top level or nested. It might
also be illegal if there is an import declaration like

import Foo as M (f)

in the module, in which case there is a name clash between the imported M.f
and the local one. This means that nested declarations have different
semantics than top level declarations, which is unfortunate, or that the
semantics of bindings such as these can only be given after renaming the
module.

Cheers,

    /kff

who feels very relieved at having come out publicly in favour of shadowing
imported names ;-)