Proposal: Scoping rule change

It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules. The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers. Why? Consider the following snippet module M where import I foo = True Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M. Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary. Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type. Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice. -- Lennart

sounds good. will there be a shadowing warning?
On Mon, Jul 23, 2012 at 5:28 PM, Lennart Augustsson
It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M.
Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary.
Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type.
Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice.
-- Lennart
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

An optional shadowing warning (for the paranoid) seems reasonable.
On Tue, Jul 24, 2012 at 1:45 AM, Greg Weber
sounds good. will there be a shadowing warning?
On Mon, Jul 23, 2012 at 5:28 PM, Lennart Augustsson
wrote: It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M.
Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary.
Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type.
Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice.
-- Lennart
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

If you're using unqualified and unrestricted imports, there's still the risk that another module will export something you care about, e.g. module M where import I -- currently exports foo import J -- might be changed in future to export foo ... foo ... So I think you need to use import lists or qualified anyway to avoid any risk of future name clashes - given that, does this change buy much? From: haskell-prime-bounces@haskell.org [mailto:haskell-prime-bounces@haskell.org] On Behalf Of Lennart Augustsson Sent: 24 July 2012 02:29 To: Haskell Prime Subject: Proposal: Scoping rule change It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules. The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers. Why? Consider the following snippet module M where import I foo = True Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M. Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary. Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type. Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice. -- Lennart =============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ===============================================================================

If Lennart's suggestion is combined with GHC's lazy checking for name clashes (i.e., only check if you ever look a name up in a particular scope), it would also work in your example.
Manuel
"Sittampalam, Ganesh"
If you’re using unqualified and unrestricted imports, there’s still the risk that another module will export something you care about, e.g.
module M where import I -- currently exports foo import J -- might be changed in future to export foo
… foo …
So I think you need to use import lists or qualified anyway to avoid any risk of future name clashes – given that, does this change buy much?
From: haskell-prime-bounces@haskell.org [mailto:haskell-prime-bounces@haskell.org] On Behalf Of Lennart Augustsson Sent: 24 July 2012 02:29 To: Haskell Prime Subject: Proposal: Scoping rule change
It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M.
Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary.
Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type.
Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice.
-- Lennart
============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ==============================================================================
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

The "... foo ..." in my example was intended to show that module M does
look up 'foo'.
From: Manuel M T Chakravarty [mailto:chak@cse.unsw.edu.au]
Sent: 25 July 2012 08:26
To: Sittampalam, Ganesh
Cc: Lennart Augustsson; Haskell Prime
Subject: Re: Proposal: Scoping rule change
If Lennart's suggestion is combined with GHC's lazy checking for name
clashes (i.e., only check if you ever look a name up in a particular
scope), it would also work in your example.
Manuel
"Sittampalam, Ganesh"

"Sittampalam, Ganesh"
The “… foo …” in my example was intended to show that module M does look up ‘foo’.
I did read that as foo is both defined and used in the body. In that case, everything should work just fine. If you use, but do not define foo, then you definitely want to get an error if J exports foo in the future. So, I think, that is fine. Manuel
From: Manuel M T Chakravarty [mailto:chak@cse.unsw.edu.au] Sent: 25 July 2012 08:26 To: Sittampalam, Ganesh Cc: Lennart Augustsson; Haskell Prime Subject: Re: Proposal: Scoping rule change
If Lennart's suggestion is combined with GHC's lazy checking for name clashes (i.e., only check if you ever look a name up in a particular scope), it would also work in your example.
Manuel
"Sittampalam, Ganesh"
: If you’re using unqualified and unrestricted imports, there’s still the risk that another module will export something you care about, e.g. module M where import I -- currently exports foo import J -- might be changed in future to export foo
… foo …
So I think you need to use import lists or qualified anyway to avoid any risk of future name clashes – given that, does this change buy much?
From: haskell-prime-bounces@haskell.org [mailto:haskell-prime-bounces@haskell.org] On Behalf Of Lennart Augustsson Sent: 24 July 2012 02:29 To: Haskell Prime Subject: Proposal: Scoping rule change
It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M.
Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary.
Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type.
Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice.
-- Lennart
============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ==============================================================================
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ==============================================================================

My point is that if you would rather not get that error when J changes,
you need to use explicit import lists:
Module M
import I (foo)
import J ()
definitioninModuleM = foo
Lennart's proposed change makes explicit import lists unnecessary for
the case where foo is defined inside M rather than being imported from I
- but as it doesn't avoid the need for them in general I'm not sure that
it is worth it.
Ganesh
From: Manuel M T Chakravarty [mailto:chak@cse.unsw.edu.au]
Sent: 25 July 2012 10:25
To: Sittampalam, Ganesh
Cc: Lennart Augustsson; Haskell Prime
Subject: Re: Proposal: Scoping rule change
"Sittampalam, Ganesh"

+1 from me
I can't count the number of times I've had this bite me when writing ByteString-like APIs that pun names from the Prelude.
On Jul 23, 2012, at 8:28 PM, Lennart Augustsson
It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M.
Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary.
Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type.
Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice.
-- Lennart
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

Nitpick: Your example actually does not lead to an error with GHC, as you define, but do not use 'foo' in M. Names (like classes) only clash when you look them up.
Manuel
Lennart Augustsson
It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M.
Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary.
Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type.
Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice.
-- Lennart
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

Lennart Augustsson wrote:
It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
I like it. That said, how does the the fact that the scope is nested affect the export list? If the module scope is inside the scope of the imports, then this means the name I.foo should appear in the export list, not foo , because the latter is in the outermost scope. I think the solution to these problems is to rearrange the import declarations so that the syntax mirrors the scoping rules. In other words, I boldly propose to move the import declaration *before* the module declaration, i.e. import I module M where foo = True or even import I where module M where foo = True This way, it is clear that the module M opens an inner scope and that the export list of M uses the names from the inner scope. Best regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com

Heinrich Apfelmus wrote:
Lennart Augustsson wrote:
It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
I like it.
That said, how does the the fact that the scope is nested affect the export list? If the module scope is inside the scope of the imports, then this means the name I.foo should appear in the export list, not foo , because the latter is in the outermost scope.
I think the solution to these problems is to rearrange the import declarations so that the syntax mirrors the scoping rules. In other words, I boldly propose to move the import declaration *before* the module declaration, i.e.
import I module M where foo = True
or even
import I where module M where foo = True
This way, it is clear that the module M opens an inner scope and that the export list of M uses the names from the inner scope.
Actually, the latter syntax should be import I in ... let import I in ... The idea is that this mirrors a let expression. (The "where" keyword would be misleading.) Best regards, Heinrich Apfelmus -- http://apfelmus.nfshost.com

Hello,
I also think that this is a good idea.
To address Manuel's nitpick, here is an example that would break if
`I` starts exporting `foo`.
module M(foo) where
import I
foo = True
To Ganesh's point: I think that this change would be useful, even if
one is willing to list all names in all imports explicitly, because it
makes it possible to leave off qualifiers on names defined in the
current module, which reduces clutter. Here is an example that I run
into quite often:
module Data.MyList (empty) where
import Text.PrettyPrint as P (empty)
empty = []
singleton x = x : Data.MyList.empty
-- vs.
singleton x = x : empty -- using new scoping rule
ppList = ... P.empty ... -- We only need to qualify imported things
Note that with the new scoping rule we wouldn't need to ever use the
current module as a qualifier, which is kind of nice, especially since
we have no way to give it a shorter name (like we can for imports).
-Iavor
On Mon, Jul 23, 2012 at 5:28 PM, Lennart Augustsson
It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M.
Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary.
Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type.
Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice.
-- Lennart
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

Hi Iavor,
In your example using "import qualified" would also resolve the clash,
although if you were importing two symbols from Text.PrettyPrint you
might like to be able to use the other one unqualified.
In my experience using the current module name for qualification is
pretty rare already, but perhaps it's just a question of different
styles.
Cheers,
Ganesh
-----Original Message-----
From: haskell-prime-bounces@haskell.org
[mailto:haskell-prime-bounces@haskell.org] On Behalf Of Iavor Diatchki
Sent: 25 July 2012 18:46
To: Lennart Augustsson
Cc: Haskell Prime
Subject: Re: Proposal: Scoping rule change
Hello,
I also think that this is a good idea.
To address Manuel's nitpick, here is an example that would break if
`I` starts exporting `foo`.
module M(foo) where
import I
foo = True
To Ganesh's point: I think that this change would be useful, even if
one is willing to list all names in all imports explicitly, because it
makes it possible to leave off qualifiers on names defined in the
current module, which reduces clutter. Here is an example that I run
into quite often:
module Data.MyList (empty) where
import Text.PrettyPrint as P (empty)
empty = []
singleton x = x : Data.MyList.empty
-- vs.
singleton x = x : empty -- using new scoping rule
ppList = ... P.empty ... -- We only need to qualify imported things
Note that with the new scoping rule we wouldn't need to ever use the
current module as a qualifier, which is kind of nice, especially since
we have no way to give it a shorter name (like we can for imports).
-Iavor
On Mon, Jul 23, 2012 at 5:28 PM, Lennart Augustsson
It's not often that one gets the chance to change something as fundamental as the scoping rules of a language. Nevertheless, I would like to propose a change to Haskell's scoping rules.
The change is quite simple. As it is, top level entities in a module are in the same scope as all imported entities. I suggest that this is changed to that the entities from the module are in an inner scope and do not clash with imported identifiers.
Why? Consider the following snippet
module M where import I foo = True
Assume this compiles. Now change the module I so it exports something called foo. After this change the module M no longer compiles since (under the current scoping rules) the imported foo clashes with the foo in M.
Pros: Module compilation becomes more robust under library changes. Fewer imports with hiding are necessary.
Cons: There's the chance that you happen to define a module identifier with the same name as something imported. This will typically lead to a type error, but there is a remote chance it could have the same type.
Implementation status: The Mu compiler has used the scoping rule for several years now and it works very well in practice.
-- Lennart
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
_______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime =============================================================================== Please access the attached hyperlink for an important electronic communications disclaimer: http://www.credit-suisse.com/legal/en/disclaimer_email_ib.html ===============================================================================
participants (7)
-
Edward Kmett
-
Greg Weber
-
Heinrich Apfelmus
-
Iavor Diatchki
-
Lennart Augustsson
-
Manuel M T Chakravarty
-
Sittampalam, Ganesh