
With the prelude changes that people have been discussing recently I've been wondering is there any reason why importing an identifier explicitly and unqualified doesn't automatically hide any implicit imports of the same identifier? Specifically I'm wondering about cases where you've imported an identifier explicitly from only one module, like this: module Foo (x, ...) where { ... } module Bar (x, ...) where { ... } import Bar import Foo (x) Even if you needed a pragma to enable it I can't think of any sensible reason why that shouldn't be equivalent to: import Bar hiding (x) import Foo (x) I don't know much of GHC's internals, but it seems like a pretty minimal change. Typing rules remain the same; explicit imports just shadow implicits. So importing multiple identifiers both implicitly or both explicitly would remain ambiguous.

I think this is a great idea. I also think it should apply to the name
shadowing warning—identifiers imported implicitly should never trigger that.
On Thu, Oct 16, 2014 at 6:19 PM, Malcolm Gooding
With the prelude changes that people have been discussing recently I've been wondering is there any reason why importing an identifier explicitly and unqualified doesn't automatically hide any implicit imports of the same identifier? Specifically I'm wondering about cases where you've imported an identifier explicitly from only one module, like this:
module Foo (x, ...) where { ... } module Bar (x, ...) where { ... }
import Bar import Foo (x)
Even if you needed a pragma to enable it I can't think of any sensible reason why that shouldn't be equivalent to:
import Bar hiding (x) import Foo (x)
I don't know much of GHC's internals, but it seems like a pretty minimal change. Typing rules remain the same; explicit imports just shadow implicits. So importing multiple identifiers both implicitly or both explicitly would remain ambiguous. _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

My first thought is: Wouldn't this make it impossible to reorder or
sort imports lexicographically (or otherwise), without fully parsing,
renaming and typechecking the code?
For example, I often use ghc-mod plus stylish-haskell to order and
format my imports. If there is no syntactic indication that one import
should be hidden (and another preferred) as there is now, then
reordering the imports of a working program willy-nilly could result
in a program that no longer typechecks (or worse, one that does, but
is now wrong).
Maybe there are some cases today where something like this could
happen, but this seems awfully, awfully implicit and hard-to-follow as
a language feature.
In general I think a program that has imports like this that may clash
can be automated to make it easier to manage - but ultimately such
imports tend to represent a complex relationship between a module and
its dependencies - I'd prefer it if these were as clear as possible.
On Thu, Oct 16, 2014 at 5:19 PM, Malcolm Gooding
With the prelude changes that people have been discussing recently I've been wondering is there any reason why importing an identifier explicitly and unqualified doesn't automatically hide any implicit imports of the same identifier? Specifically I'm wondering about cases where you've imported an identifier explicitly from only one module, like this:
module Foo (x, ...) where { ... } module Bar (x, ...) where { ... }
import Bar import Foo (x)
Even if you needed a pragma to enable it I can't think of any sensible reason why that shouldn't be equivalent to:
import Bar hiding (x) import Foo (x)
I don't know much of GHC's internals, but it seems like a pretty minimal change. Typing rules remain the same; explicit imports just shadow implicits. So importing multiple identifiers both implicitly or both explicitly would remain ambiguous. _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Regards, Austin Seipp, Haskell Consultant Well-Typed LLP, http://www.well-typed.com/

Well I suppose tooling might need to be aware of the feature depending on what it does, but I don't see why the code actually typechecking would need to be dependent on ordering. When I say shadowing I don't mean explicitly having any explicit import create a new scope, since in that case it would be sensitive to re-ordering, which I agree would be bad. My thought would be first you would need to parse all the imports to see which identifiers they import, then do another pass to change the imports to hide any identifiers that should be shadowed. So in the example I gave you would need to be aware that Foo exports x, because otherwise there would be no way to know that x needs to be hidden from Bar. I assume GHC already would have access to that information though. -- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57581... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

It should be good enough (for what you're talking about) to hide them all.
Turn
import A (foo)
import B (bar)
import C hiding (baz)
import D
into
import A (foo)
import B (bar)
import C hiding (foo,bar,baz)
import D hiding (foo,bar)
There's no reason to worry about hiding nonexistent identifiers, I don't
think.
On Thu, Oct 16, 2014 at 7:10 PM, htebalaka
Well I suppose tooling might need to be aware of the feature depending on what it does, but I don't see why the code actually typechecking would need to be dependent on ordering. When I say shadowing I don't mean explicitly having any explicit import create a new scope, since in that case it would be sensitive to re-ordering, which I agree would be bad. My thought would be first you would need to parse all the imports to see which identifiers they import, then do another pass to change the imports to hide any identifiers that should be shadowed.
So in the example I gave you would need to be aware that Foo exports x, because otherwise there would be no way to know that x needs to be hidden from Bar. I assume GHC already would have access to that information though.
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57581... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com. _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

Yeah, I just realized that would work too. You would still need to do two passes over the imports, so foo and bar are hidden from anything imported above A. Though while we're reasoning syntactically, you would also need to hide them from the Prelude if it was being implicitly imported. -- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57581... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

Maybe there are some cases today where something like this could happen, but this seems awfully, awfully implicit and hard-to-follow as a language feature.
In general I think a program that has imports like this that may clash can be automated to make it easier to manage - but ultimately such imports tend to represent a complex relationship between a module and its dependencies - I'd prefer it if these were as clear as possible. Very strong +1 from me. It seems awfully implicit and obscure for very
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 On 17/10/14 00:40, Austin Seipp wrote: little benefit, and it may mean quite a bit of work for tool developers. - -- Alexander alexander@plaimi.net https://secure.plaimi.net/~alexander -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/ iF4EAREIAAYFAlRAxf8ACgkQRtClrXBQc7WGewD/Vt/7OdyPgzORiE4uHtU/p22a TLGGnQuceSlrMJiWFhMA/0GuZ1leom0ILvrqW/oJYugnwGgX1atqmneJoZ72qNEM =+oUV -----END PGP SIGNATURE-----

On 10/17/14 12:32, Alexander Berntsen wrote:
Maybe there are some cases today where something like this could happen, but this seems awfully, awfully implicit and hard-to-follow as a language feature.
In general I think a program that has imports like this that may clash can be automated to make it easier to manage - but ultimately such imports tend to represent a complex relationship between a module and its dependencies - I'd prefer it if these were as clear as possible. Very strong +1 from me. It seems awfully implicit and obscure for very
On 17/10/14 00:40, Austin Seipp wrote: little benefit, and it may mean quite a bit of work for tool developers.
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example: import Prelude import Data.Text.Lazy.IO (putStrLn) Regardless of the ordering of the imports, is there any way for me to use putStrLn in any context without hiding it from the Prelude (and any other modules that I might be unintentionally importing it from)? Any unqualified use will be ambiguous, unless you hide it from every other module that might export a function with the same name. I would think the fact that it shouldn't be implicitly imported from other modules would directly follow from the fact you imported it explicitly (otherwise, why did you import it?). I'm having trouble coming up with a single example where the current behaviour is useful. I can't speak to tooling, though I suppose if this doesn't get implemented I'll write my own. Just to be very clear, supposing you have some Import datatype which stores a list of any identifiers that are being explicitly imported unqualified (or conversely, a list of any identifiers that are being hidden), then the behaviour I'm suggesting is a pragma to enable something like this: hide :: [Import] -> [Import] hide = flip (fmap fmap appendHiddenImports) <*> collectOnly where collectOnly :: [Import] -> [Identifier] collectOnly = concat . mapMaybe getExplicitImports appendHiddenImports :: [Identifier] -> Import -> Import getExplicitImports :: Import -> Maybe [Identifier] where appendHiddenImports would only change import statements that import an unspecified number of unqualified identifiers, like "import X hiding (x, y)" or "import Y". -- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

You mention only unqualified imports, but if we do this, it should also
apply to qualified ones:
import qualified Data.List as L
import qualified MyModule as L (isInfixOf)
On Oct 18, 2014 2:02 PM, "htebalaka"
On 10/17/14 12:32, Alexander Berntsen wrote:
Maybe there are some cases today where something like this could happen, but this seems awfully, awfully implicit and hard-to-follow as a language feature.
In general I think a program that has imports like this that may clash can be automated to make it easier to manage - but ultimately such imports tend to represent a complex relationship between a module and its dependencies - I'd prefer it if these were as clear as possible. Very strong +1 from me. It seems awfully implicit and obscure for very
On 17/10/14 00:40, Austin Seipp wrote: little benefit, and it may mean quite a bit of work for tool developers.
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
Regardless of the ordering of the imports, is there any way for me to use putStrLn in any context without hiding it from the Prelude (and any other modules that I might be unintentionally importing it from)? Any unqualified use will be ambiguous, unless you hide it from every other module that might export a function with the same name. I would think the fact that it shouldn't be implicitly imported from other modules would directly follow from the fact you imported it explicitly (otherwise, why did you import it?). I'm having trouble coming up with a single example where the current behaviour is useful.
I can't speak to tooling, though I suppose if this doesn't get implemented I'll write my own. Just to be very clear, supposing you have some Import datatype which stores a list of any identifiers that are being explicitly imported unqualified (or conversely, a list of any identifiers that are being hidden), then the behaviour I'm suggesting is a pragma to enable something like this:
hide :: [Import] -> [Import] hide = flip (fmap fmap appendHiddenImports) <*> collectOnly where collectOnly :: [Import] -> [Identifier] collectOnly = concat . mapMaybe getExplicitImports appendHiddenImports :: [Identifier] -> Import -> Import getExplicitImports :: Import -> Maybe [Identifier]
where appendHiddenImports would only change import statements that import an unspecified number of unqualified identifiers, like "import X hiding (x, y)" or "import Y".
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com. _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

I don't normally import things qualified like that, so it hadn't occurred to me, but that makes sense. I do want to be clear that this shouldn't be sensitive to ordering, and it can occur before type checking. You just need to know what modules are importing which identifiers explicitly, and what modules are importing an unspecified set of identifiers. It should be possible to do while parsing even, as soon as you've reached a point where you know every import statement has been reached. The behaviour would thus remain the same if someone was to explicitly import the same identifier from two or more modules, since hiding the implicit imports wouldn't remove the ambiguity in that case. I'm pretty sure the code I provided is sufficient; you would just need a separate pass for qualified and unqualified imports. David Feuer wrote
You mention only unqualified imports, but if we do this, it should also apply to qualified ones:
import qualified Data.List as L import qualified MyModule as L (isInfixOf) On Oct 18, 2014 2:02 PM, "htebalaka" <
goodingm@
> wrote:
On 10/17/14 12:32, Alexander Berntsen wrote:
Maybe there are some cases today where something like this could happen, but this seems awfully, awfully implicit and hard-to-follow as a language feature.
In general I think a program that has imports like this that may clash can be automated to make it easier to manage - but ultimately such imports tend to represent a complex relationship between a module and its dependencies - I'd prefer it if these were as clear as possible. Very strong +1 from me. It seems awfully implicit and obscure for very
On 17/10/14 00:40, Austin Seipp wrote: little benefit, and it may mean quite a bit of work for tool developers.
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
Regardless of the ordering of the imports, is there any way for me to use putStrLn in any context without hiding it from the Prelude (and any other modules that I might be unintentionally importing it from)? Any unqualified use will be ambiguous, unless you hide it from every other module that might export a function with the same name. I would think the fact that it shouldn't be implicitly imported from other modules would directly follow from the fact you imported it explicitly (otherwise, why did you import it?). I'm having trouble coming up with a single example where the current behaviour is useful.
I can't speak to tooling, though I suppose if this doesn't get implemented I'll write my own. Just to be very clear, supposing you have some Import datatype which stores a list of any identifiers that are being explicitly imported unqualified (or conversely, a list of any identifiers that are being hidden), then the behaviour I'm suggesting is a pragma to enable something like this:
hide :: [Import] -> [Import] hide = flip (fmap fmap appendHiddenImports) <*> collectOnly where collectOnly :: [Import] -> [Identifier] collectOnly = concat . mapMaybe getExplicitImports appendHiddenImports :: [Identifier] -> Import -> Import getExplicitImports :: Import -> Maybe [Identifier]
where appendHiddenImports would only change import statements that import an unspecified number of unqualified identifiers, like "import X hiding (x, y)" or "import Y".
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com. _______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
_______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

On Sat, Oct 18, 2014 at 1:02 PM, htebalaka
On 10/17/14 12:32, Alexander Berntsen wrote:
Maybe there are some cases today where something like this could happen, but this seems awfully, awfully implicit and hard-to-follow as a language feature.
In general I think a program that has imports like this that may clash can be automated to make it easier to manage - but ultimately such imports tend to represent a complex relationship between a module and its dependencies - I'd prefer it if these were as clear as possible. Very strong +1 from me. It seems awfully implicit and obscure for very
On 17/10/14 00:40, Austin Seipp wrote: little benefit, and it may mean quite a bit of work for tool developers.
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
Regardless of the ordering of the imports, is there any way for me to use putStrLn in any context without hiding it from the Prelude (and any other modules that I might be unintentionally importing it from)?
I suppose my point isn't that the current behavior is more useful, but the *proposed behavior seems more confusing for humans*. I would rather have GHC inform me of an ambiguous import as opposed to silently accepting or rejecting my program based on the import list, and whether it shadows something prior to it. I don't even always know what identifiers may get imported in the first place, due to transitive module reexports. It just seems like pretty confusing behavior - shadowing of identifiers is rarely a 'feature' for humans, IMO. In the example you have, what happens if I change the import list of Data.Text by removing it, for example, or what happens if I *remove* the Prelude import, and stick it after the Text import? Rather than getting an out of scope identifier error, or something ambiguous, I'd get a confusing type error based on Prelude's use of putStrLn in the context of needing Texts', because the shadowing would fail to apply since it didn't occur before the Text import. Shadowing of previously imported identifiers only works one-way, so to speak, where with 'hiding', order no longer matters in the import list. Of course you might say, "Well, of course Prelude exports putStrLn, so you wouldn't move the import, and it wouldn't be a problem". The problem is I don't know what exports an arbitrary module has; it doesn't seem to scale mentally for humans at all. In this case, I know Prelude exports that, but in the general case of: import Frob import Knob (xyz) Today, this means I only import 'xyz' from Knob, and there are no other ambiguous names. But under your proposal, I have zero clue if 'xyz' is actually shadowing a prior import. So unless I check *all* the transitive exports of 'Frob', I have no clue if it's actually safe to move the import of 'Knob' higher up - an identifier may not be shadowed if I do that. OTOH, I know *for a fact* when I see this: import Frob hiding (xyz) import Knob (xyz) which 'xyz' I'm referring to later, without ambiguity. Also, what happens if I do this: import Knob (xyz) import Frob legitimately, without shadowing, and 'Frob' later ends up exporting its own 'xyz'? Do I just get an ambiguous identifier error, like I would today? Again, shadowing in this sense only works 'one-way': top to bottom, and it fails any other case they might be rearranged. This all just seems like a relatively large amount of hoops to jump through, just to avoid writing 'hiding' in a on a few things, so to me, the cure looks worse than the disease. But I may just be missing something completely.
Any unqualified use will be ambiguous, unless you hide it from every other module that might export a function with the same name. I would think the fact that it shouldn't be implicitly imported from other modules would directly follow from the fact you imported it explicitly (otherwise, why did you import it?). I'm having trouble coming up with a single example where the current behaviour is useful.
I can't speak to tooling, though I suppose if this doesn't get implemented I'll write my own. Just to be very clear, supposing you have some Import datatype which stores a list of any identifiers that are being explicitly imported unqualified (or conversely, a list of any identifiers that are being hidden), then the behaviour I'm suggesting is a pragma to enable something like this:
hide :: [Import] -> [Import] hide = flip (fmap fmap appendHiddenImports) <*> collectOnly where collectOnly :: [Import] -> [Identifier] collectOnly = concat . mapMaybe getExplicitImports appendHiddenImports :: [Identifier] -> Import -> Import getExplicitImports :: Import -> Maybe [Identifier]
where appendHiddenImports would only change import statements that import an unspecified number of unqualified identifiers, like "import X hiding (x, y)" or "import Y".
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com. _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Regards, Austin Seipp, Haskell Consultant Well-Typed LLP, http://www.well-typed.com/

And also, the ultimate confusing case: if two modules exported
identifiers with the same name *and* type. At least under the current
scheme, you'd be required to clearly disambiguate them in all cases.
Under the proposed scheme, there's no telling what behavior your
program might have, based solely on the shadowing rules/ordering of
the imports of your module and nothing else.
I don't think this would be a common occurrence. But it seems deeply
upsetting that in such a case, rather than the compiler complaining
loudly about ambiguity in a very obvious case (a compiler which
catches many *other* very obvious static code errors), it would
instead silently accept accept your program under a very implicit
"DWIM-ish" import rule.
On Sat, Oct 18, 2014 at 1:33 PM, Austin Seipp
On Sat, Oct 18, 2014 at 1:02 PM, htebalaka
wrote: On 10/17/14 12:32, Alexander Berntsen wrote:
Maybe there are some cases today where something like this could happen, but this seems awfully, awfully implicit and hard-to-follow as a language feature.
In general I think a program that has imports like this that may clash can be automated to make it easier to manage - but ultimately such imports tend to represent a complex relationship between a module and its dependencies - I'd prefer it if these were as clear as possible. Very strong +1 from me. It seems awfully implicit and obscure for very
On 17/10/14 00:40, Austin Seipp wrote: little benefit, and it may mean quite a bit of work for tool developers.
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
Regardless of the ordering of the imports, is there any way for me to use putStrLn in any context without hiding it from the Prelude (and any other modules that I might be unintentionally importing it from)?
I suppose my point isn't that the current behavior is more useful, but the *proposed behavior seems more confusing for humans*. I would rather have GHC inform me of an ambiguous import as opposed to silently accepting or rejecting my program based on the import list, and whether it shadows something prior to it. I don't even always know what identifiers may get imported in the first place, due to transitive module reexports. It just seems like pretty confusing behavior - shadowing of identifiers is rarely a 'feature' for humans, IMO.
In the example you have, what happens if I change the import list of Data.Text by removing it, for example, or what happens if I *remove* the Prelude import, and stick it after the Text import? Rather than getting an out of scope identifier error, or something ambiguous, I'd get a confusing type error based on Prelude's use of putStrLn in the context of needing Texts', because the shadowing would fail to apply since it didn't occur before the Text import. Shadowing of previously imported identifiers only works one-way, so to speak, where with 'hiding', order no longer matters in the import list.
Of course you might say, "Well, of course Prelude exports putStrLn, so you wouldn't move the import, and it wouldn't be a problem". The problem is I don't know what exports an arbitrary module has; it doesn't seem to scale mentally for humans at all. In this case, I know Prelude exports that, but in the general case of:
import Frob import Knob (xyz)
Today, this means I only import 'xyz' from Knob, and there are no other ambiguous names. But under your proposal, I have zero clue if 'xyz' is actually shadowing a prior import. So unless I check *all* the transitive exports of 'Frob', I have no clue if it's actually safe to move the import of 'Knob' higher up - an identifier may not be shadowed if I do that. OTOH, I know *for a fact* when I see this:
import Frob hiding (xyz) import Knob (xyz)
which 'xyz' I'm referring to later, without ambiguity. Also, what happens if I do this:
import Knob (xyz) import Frob
legitimately, without shadowing, and 'Frob' later ends up exporting its own 'xyz'? Do I just get an ambiguous identifier error, like I would today? Again, shadowing in this sense only works 'one-way': top to bottom, and it fails any other case they might be rearranged.
This all just seems like a relatively large amount of hoops to jump through, just to avoid writing 'hiding' in a on a few things, so to me, the cure looks worse than the disease. But I may just be missing something completely.
Any unqualified use will be ambiguous, unless you hide it from every other module that might export a function with the same name. I would think the fact that it shouldn't be implicitly imported from other modules would directly follow from the fact you imported it explicitly (otherwise, why did you import it?). I'm having trouble coming up with a single example where the current behaviour is useful.
I can't speak to tooling, though I suppose if this doesn't get implemented I'll write my own. Just to be very clear, supposing you have some Import datatype which stores a list of any identifiers that are being explicitly imported unqualified (or conversely, a list of any identifiers that are being hidden), then the behaviour I'm suggesting is a pragma to enable something like this:
hide :: [Import] -> [Import] hide = flip (fmap fmap appendHiddenImports) <*> collectOnly where collectOnly :: [Import] -> [Identifier] collectOnly = concat . mapMaybe getExplicitImports appendHiddenImports :: [Identifier] -> Import -> Import getExplicitImports :: Import -> Maybe [Identifier]
where appendHiddenImports would only change import statements that import an unspecified number of unqualified identifiers, like "import X hiding (x, y)" or "import Y".
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com. _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Regards,
Austin Seipp, Haskell Consultant Well-Typed LLP, http://www.well-typed.com/
-- Regards, Austin Seipp, Haskell Consultant Well-Typed LLP, http://www.well-typed.com/

Shadowing may have been a poor name on my part, since that does imply a scope that is sensitive to order. It's more like import precedence. The explicit import takes precedence over ones that are being implicitly imported. So in the Frob/Knob examples the behaviour would be identical in all cases, regardless of transitive imports. An explicit import of xyz from Knob hides xyz from the implicit imports regardless of where they appear in the import list. I suppose in the case where the types of the implicitly imported thing match that of the explicitly imported one, AND you later remove the explicit import, that would be potentially dangerous, since you might not realize that the lower precedence function is now being used, and wouldn't be able to use the compiler to track down those uses without first manually hiding the function from the module thats being implicitly imported (which the current behaviour would have required you to do already). I still think a pragma would be useful, though clearly it wouldn't be acceptable as the default; you don't really need to be careful when adding imports, but you do need to be more careful when removing. Austin Seipp-5 wrote
And also, the ultimate confusing case: if two modules exported identifiers with the same name *and* type. At least under the current scheme, you'd be required to clearly disambiguate them in all cases. Under the proposed scheme, there's no telling what behavior your program might have, based solely on the shadowing rules/ordering of the imports of your module and nothing else.
I don't think this would be a common occurrence. But it seems deeply upsetting that in such a case, rather than the compiler complaining loudly about ambiguity in a very obvious case (a compiler which catches many *other* very obvious static code errors), it would instead silently accept accept your program under a very implicit "DWIM-ish" import rule.
On Sat, Oct 18, 2014 at 1:33 PM, Austin Seipp <
austin@
> wrote:
On Sat, Oct 18, 2014 at 1:02 PM, htebalaka <
goodingm@
> wrote:
On 10/17/14 12:32, Alexander Berntsen wrote:
Maybe there are some cases today where something like this could happen, but this seems awfully, awfully implicit and hard-to-follow as a language feature.
In general I think a program that has imports like this that may clash can be automated to make it easier to manage - but ultimately such imports tend to represent a complex relationship between a module and its dependencies - I'd prefer it if these were as clear as possible. Very strong +1 from me. It seems awfully implicit and obscure for very
On 17/10/14 00:40, Austin Seipp wrote: little benefit, and it may mean quite a bit of work for tool developers.
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
Regardless of the ordering of the imports, is there any way for me to use putStrLn in any context without hiding it from the Prelude (and any other modules that I might be unintentionally importing it from)?
I suppose my point isn't that the current behavior is more useful, but the *proposed behavior seems more confusing for humans*. I would rather have GHC inform me of an ambiguous import as opposed to silently accepting or rejecting my program based on the import list, and whether it shadows something prior to it. I don't even always know what identifiers may get imported in the first place, due to transitive module reexports. It just seems like pretty confusing behavior - shadowing of identifiers is rarely a 'feature' for humans, IMO.
In the example you have, what happens if I change the import list of Data.Text by removing it, for example, or what happens if I *remove* the Prelude import, and stick it after the Text import? Rather than getting an out of scope identifier error, or something ambiguous, I'd get a confusing type error based on Prelude's use of putStrLn in the context of needing Texts', because the shadowing would fail to apply since it didn't occur before the Text import. Shadowing of previously imported identifiers only works one-way, so to speak, where with 'hiding', order no longer matters in the import list.
Of course you might say, "Well, of course Prelude exports putStrLn, so you wouldn't move the import, and it wouldn't be a problem". The problem is I don't know what exports an arbitrary module has; it doesn't seem to scale mentally for humans at all. In this case, I know Prelude exports that, but in the general case of:
import Frob import Knob (xyz)
Today, this means I only import 'xyz' from Knob, and there are no other ambiguous names. But under your proposal, I have zero clue if 'xyz' is actually shadowing a prior import. So unless I check *all* the transitive exports of 'Frob', I have no clue if it's actually safe to move the import of 'Knob' higher up - an identifier may not be shadowed if I do that. OTOH, I know *for a fact* when I see this:
import Frob hiding (xyz) import Knob (xyz)
which 'xyz' I'm referring to later, without ambiguity. Also, what happens if I do this:
import Knob (xyz) import Frob
legitimately, without shadowing, and 'Frob' later ends up exporting its own 'xyz'? Do I just get an ambiguous identifier error, like I would today? Again, shadowing in this sense only works 'one-way': top to bottom, and it fails any other case they might be rearranged.
This all just seems like a relatively large amount of hoops to jump through, just to avoid writing 'hiding' in a on a few things, so to me, the cure looks worse than the disease. But I may just be missing something completely.
Any unqualified use will be ambiguous, unless you hide it from every other module that might export a function with the same name. I would think the fact that it shouldn't be implicitly imported from other modules would directly follow from the fact you imported it explicitly (otherwise, why did you import it?). I'm having trouble coming up with a single example where the current behaviour is useful.
I can't speak to tooling, though I suppose if this doesn't get implemented I'll write my own. Just to be very clear, supposing you have some Import datatype which stores a list of any identifiers that are being explicitly imported unqualified (or conversely, a list of any identifiers that are being hidden), then the behaviour I'm suggesting is a pragma to enable something like this:
hide :: [Import] -> [Import] hide = flip (fmap fmap appendHiddenImports) <*> collectOnly where collectOnly :: [Import] -> [Identifier] collectOnly = concat . mapMaybe getExplicitImports appendHiddenImports :: [Identifier] -> Import -> Import getExplicitImports :: Import -> Maybe [Identifier]
where appendHiddenImports would only change import statements that import an unspecified number of unqualified identifiers, like "import X hiding (x, y)" or "import Y".
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com. _______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Regards,
Austin Seipp, Haskell Consultant Well-Typed LLP, http://www.well-typed.com/
-- Regards,
Austin Seipp, Haskell Consultant Well-Typed LLP, http://www.well-typed.com/ _______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

I hate to reply three times in a row, but I guess even when adding in that case it's a little iffy. Currently you could add an explicit import, use the compiler to track down which cases are ambiguous, and then once you're confident they're taken care of hide the identifier from the module that was implicitly importing it. With the pragma on I still think it would be unambiguous that you want to prefer the one you've imported explicitly, but you couldn't use the compiler to check out each ambiguous occurrence before you hide it from the Prelude (or whatever module was importing it); they would instead get immediately changed to the explicit identifier. I hadn't considered those, but I still think it would be kind of useful as a pragma. I get kind of annoyed when modules essentially steal identifiers that you never use; the hiding clauses become very mechanical when you want to use most of what a module exports, but not everything. I've described it as best I can at any rate, so if there's still not enough interest I'll try to make something of my own to just parse a source file and auto-add the hiding clauses. htebalaka wrote
Shadowing may have been a poor name on my part, since that does imply a scope that is sensitive to order. It's more like import precedence. The explicit import takes precedence over ones that are being implicitly imported. So in the Frob/Knob examples the behaviour would be identical in all cases, regardless of transitive imports. An explicit import of xyz from Knob hides xyz from the implicit imports regardless of where they appear in the import list.
I suppose in the case where the types of the implicitly imported thing match that of the explicitly imported one, AND you later remove the explicit import, that would be potentially dangerous, since you might not realize that the lower precedence function is now being used, and wouldn't be able to use the compiler to track down those uses without first manually hiding the function from the module thats being implicitly imported (which the current behaviour would have required you to do already). I still think a pragma would be useful, though clearly it wouldn't be acceptable as the default; you don't really need to be careful when adding imports, but you do need to be more careful when removing. Austin Seipp-5 wrote
And also, the ultimate confusing case: if two modules exported identifiers with the same name *and* type. At least under the current scheme, you'd be required to clearly disambiguate them in all cases. Under the proposed scheme, there's no telling what behavior your program might have, based solely on the shadowing rules/ordering of the imports of your module and nothing else.
I don't think this would be a common occurrence. But it seems deeply upsetting that in such a case, rather than the compiler complaining loudly about ambiguity in a very obvious case (a compiler which catches many *other* very obvious static code errors), it would instead silently accept accept your program under a very implicit "DWIM-ish" import rule.
On Sat, Oct 18, 2014 at 1:33 PM, Austin Seipp <
austin@
> wrote:
On Sat, Oct 18, 2014 at 1:02 PM, htebalaka <
goodingm@
> wrote:
On 10/17/14 12:32, Alexander Berntsen wrote:
Maybe there are some cases today where something like this could happen, but this seems awfully, awfully implicit and hard-to-follow as a language feature.
In general I think a program that has imports like this that may clash can be automated to make it easier to manage - but ultimately such imports tend to represent a complex relationship between a module and its dependencies - I'd prefer it if these were as clear as possible. Very strong +1 from me. It seems awfully implicit and obscure for very
On 17/10/14 00:40, Austin Seipp wrote: little benefit, and it may mean quite a bit of work for tool developers.
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
Regardless of the ordering of the imports, is there any way for me to use putStrLn in any context without hiding it from the Prelude (and any other modules that I might be unintentionally importing it from)?
I suppose my point isn't that the current behavior is more useful, but the *proposed behavior seems more confusing for humans*. I would rather have GHC inform me of an ambiguous import as opposed to silently accepting or rejecting my program based on the import list, and whether it shadows something prior to it. I don't even always know what identifiers may get imported in the first place, due to transitive module reexports. It just seems like pretty confusing behavior - shadowing of identifiers is rarely a 'feature' for humans, IMO.
In the example you have, what happens if I change the import list of Data.Text by removing it, for example, or what happens if I *remove* the Prelude import, and stick it after the Text import? Rather than getting an out of scope identifier error, or something ambiguous, I'd get a confusing type error based on Prelude's use of putStrLn in the context of needing Texts', because the shadowing would fail to apply since it didn't occur before the Text import. Shadowing of previously imported identifiers only works one-way, so to speak, where with 'hiding', order no longer matters in the import list.
Of course you might say, "Well, of course Prelude exports putStrLn, so you wouldn't move the import, and it wouldn't be a problem". The problem is I don't know what exports an arbitrary module has; it doesn't seem to scale mentally for humans at all. In this case, I know Prelude exports that, but in the general case of:
import Frob import Knob (xyz)
Today, this means I only import 'xyz' from Knob, and there are no other ambiguous names. But under your proposal, I have zero clue if 'xyz' is actually shadowing a prior import. So unless I check *all* the transitive exports of 'Frob', I have no clue if it's actually safe to move the import of 'Knob' higher up - an identifier may not be shadowed if I do that. OTOH, I know *for a fact* when I see this:
import Frob hiding (xyz) import Knob (xyz)
which 'xyz' I'm referring to later, without ambiguity. Also, what happens if I do this:
import Knob (xyz) import Frob
legitimately, without shadowing, and 'Frob' later ends up exporting its own 'xyz'? Do I just get an ambiguous identifier error, like I would today? Again, shadowing in this sense only works 'one-way': top to bottom, and it fails any other case they might be rearranged.
This all just seems like a relatively large amount of hoops to jump through, just to avoid writing 'hiding' in a on a few things, so to me, the cure looks worse than the disease. But I may just be missing something completely.
Any unqualified use will be ambiguous, unless you hide it from every other module that might export a function with the same name. I would think the fact that it shouldn't be implicitly imported from other modules would directly follow from the fact you imported it explicitly (otherwise, why did you import it?). I'm having trouble coming up with a single example where the current behaviour is useful.
I can't speak to tooling, though I suppose if this doesn't get implemented I'll write my own. Just to be very clear, supposing you have some Import datatype which stores a list of any identifiers that are being explicitly imported unqualified (or conversely, a list of any identifiers that are being hidden), then the behaviour I'm suggesting is a pragma to enable something like this:
hide :: [Import] -> [Import] hide = flip (fmap fmap appendHiddenImports) <*> collectOnly where collectOnly :: [Import] -> [Identifier] collectOnly = concat . mapMaybe getExplicitImports appendHiddenImports :: [Identifier] -> Import -> Import getExplicitImports :: Import -> Maybe [Identifier]
where appendHiddenImports would only change import statements that import an unspecified number of unqualified identifiers, like "import X hiding (x, y)" or "import Y".
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com. _______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- Regards,
Austin Seipp, Haskell Consultant Well-Typed LLP, http://www.well-typed.com/
-- Regards,
Austin Seipp, Haskell Consultant Well-Typed LLP, http://www.well-typed.com/ _______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57582... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

Hi, Am Samstag, den 18.10.2014, 11:02 -0700 schrieb htebalaka:
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
I find this quite convincing. If I bother to explicitly write out „take putStrLn from Data.Text.Lazy.IO“, why should the compiler assume that I might have meant some putStrLn from somewhere else. Of course, order should not matter (I don’t think anyone suggested it should, I think Austin simply mis-read that). Greetings, Joachim -- Joachim “nomeata” Breitner mail@joachim-breitner.de • http://www.joachim-breitner.de/ Jabber: nomeata@joachim-breitner.de • GPG-Key: 0xF0FBF51F Debian Developer: nomeata@debian.org

I'm generally in favor of the proposal, but I figured I should mention one
situation when I personally might find this confusing. If the module import
list is very long, and includes an unrestricted import of a well-known
module, it might be easy to assume a certain well-known function comes from
there, when in fact it comes from some other module on the other end of the
import list.
On Oct 18, 2014 6:39 PM, "Joachim Breitner"
Hi,
Am Samstag, den 18.10.2014, 11:02 -0700 schrieb htebalaka:
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
I find this quite convincing. If I bother to explicitly write out „take putStrLn from Data.Text.Lazy.IO“, why should the compiler assume that I might have meant some putStrLn from somewhere else.
Of course, order should not matter (I don’t think anyone suggested it should, I think Austin simply mis-read that).
Greetings, Joachim
-- Joachim “nomeata” Breitner mail@joachim-breitner.de • http://www.joachim-breitner.de/ Jabber: nomeata@joachim-breitner.de • GPG-Key: 0xF0FBF51F Debian Developer: nomeata@debian.org
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

I feel that this extension, while looking tempting for writing code
from scratch, might hurt maintainability of code. Adding an explicit
import can suddenly cause type errors in completely unrelated places
(when it hides an implicit import and the new function is type
incorrect), or worse, can cause semantic change (when it hides an
implicit import and the new function is type correct, but has
different behavior). Remember that not all code is written by the same
person, or in a short time frame, so not everybody might fully
understand every module and every import of the code they're editing.
Erik
On Sun, Oct 19, 2014 at 1:32 AM, David Feuer
I'm generally in favor of the proposal, but I figured I should mention one situation when I personally might find this confusing. If the module import list is very long, and includes an unrestricted import of a well-known module, it might be easy to assume a certain well-known function comes from there, when in fact it comes from some other module on the other end of the import list.
On Oct 18, 2014 6:39 PM, "Joachim Breitner"
wrote: Hi,
Am Samstag, den 18.10.2014, 11:02 -0700 schrieb htebalaka:
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
I find this quite convincing. If I bother to explicitly write out „take putStrLn from Data.Text.Lazy.IO“, why should the compiler assume that I might have meant some putStrLn from somewhere else.
Of course, order should not matter (I don’t think anyone suggested it should, I think Austin simply mis-read that).
Greetings, Joachim
-- Joachim “nomeata” Breitner mail@joachim-breitner.de • http://www.joachim-breitner.de/ Jabber: nomeata@joachim-breitner.de • GPG-Key: 0xF0FBF51F Debian Developer: nomeata@debian.org
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
_______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

At least in the case where something being hidden causes an unintended type error, compiler errors could /potentially/ be aware of the extension, though defining unintended seems non-trivial. You don't want actual type errors being replaced with "maybe you didn't realize this other function is being auto-hidden", and you definitely don't want to have to type check for different combination of hidden functions. I don't think it's really worth extending the scope, unless there's some trivial solution I'm missing. What I want to be able to do is use a not-too-large set of more general (IO -> MonadIO) or differently monomorphic (String -> Text) functions than what I'm implicitly importing. In either case I think the change is semantically safe: any type errors you encounter are actual type errors. This should make that kind of use a little more lightweight than it is currently. I'll definitely concede it could be unsuitable behaviour by default for large projects. My coding is mostly personal-use stuff, so that's where my experience is. Is there a good example proposal somewhere for what kind of format I should follow. I'm somewhat busy at the moment, but I'll write up a more detailed proposal on the wiki by the end of the week unless someone feels compelled to beat me to it. Erik Hesselink wrote
I feel that this extension, while looking tempting for writing code from scratch, might hurt maintainability of code. Adding an explicit import can suddenly cause type errors in completely unrelated places (when it hides an implicit import and the new function is type incorrect), or worse, can cause semantic change (when it hides an implicit import and the new function is type correct, but has different behavior). Remember that not all code is written by the same person, or in a short time frame, so not everybody might fully understand every module and every import of the code they're editing.
Erik
On Sun, Oct 19, 2014 at 1:32 AM, David Feuer <
david.feuer@
> wrote:
I'm generally in favor of the proposal, but I figured I should mention one situation when I personally might find this confusing. If the module import list is very long, and includes an unrestricted import of a well-known module, it might be easy to assume a certain well-known function comes from there, when in fact it comes from some other module on the other end of the import list.
On Oct 18, 2014 6:39 PM, "Joachim Breitner" <
mail@
>
wrote:
Hi,
I guess my central point is I don't see how anyone can benefit from
Am Samstag, den 18.10.2014, 11:02 -0700 schrieb htebalaka: the
current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
I find this quite convincing. If I bother to explicitly write out „take putStrLn from Data.Text.Lazy.IO“, why should the compiler assume that I might have meant some putStrLn from somewhere else.
Of course, order should not matter (I don’t think anyone suggested it should, I think Austin simply mis-read that).
Greetings, Joachim
-- Joachim “nomeata” Breitner
mail@
• http://www.joachim-breitner.de/
Jabber:
nomeata@
• GPG-Key: 0xF0FBF51F
Debian Developer:
nomeata@
_______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
_______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
_______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57583... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

There is one more case that I hadn't considered that would be kind of ambiguous. If you import something like "import Control.Comonad (Comonad(..))" it's unclear if the compiler should automatically treat it as if you added "hiding (Comonad(..))" to implicit imports, or added "hiding (Comonad, extend, extract, duplicate)". The former could hide identifiers from modules that define Comonad differently, and might still cause using Comonads methods to be ambiguous. The latter resolves the ambiguity, but could change what is automatically hidden if the Comonad typeclass is ever changed. In some sense the methods being imported from Comonad could still be considered implicit, since they aren't being imported individually by name. Unless people feel differently, I'd propose neither solution, since users would be confused if they assumed the wrong way the compiler did, and silent changes to what is being imported would be bad things. Instead they would still get an ambiguous occurrence as they do now. It also makes its behaviour more uniform: only identifiers imported *by name* are auto-hidden. For a pragma, AutoHidingImports? htebalaka wrote
At least in the case where something being hidden causes an unintended type error, compiler errors could / potentially / be aware of the extension, though defining unintended seems non-trivial. You don't want actual type errors being replaced with "maybe you didn't realize this other function is being auto-hidden", and you definitely don't want to have to type check for different combination of hidden functions. I don't think it's really worth extending the scope, unless there's some trivial solution I'm missing.
What I want to be able to do is use a not-too-large set of more general (IO -> MonadIO) or differently monomorphic (String -> Text) functions than what I'm implicitly importing. In either case I think the change is semantically safe: any type errors you encounter are actual type errors. This should make that kind of use a little more lightweight than it is currently. I'll definitely concede it could be unsuitable behaviour by default for large projects. My coding is mostly personal-use stuff, so that's where my experience is.
Is there a good example proposal somewhere for what kind of format I should follow. I'm somewhat busy at the moment, but I'll write up a more detailed proposal on the wiki by the end of the week unless someone feels compelled to beat me to it. Erik Hesselink wrote
I feel that this extension, while looking tempting for writing code from scratch, might hurt maintainability of code. Adding an explicit import can suddenly cause type errors in completely unrelated places (when it hides an implicit import and the new function is type incorrect), or worse, can cause semantic change (when it hides an implicit import and the new function is type correct, but has different behavior). Remember that not all code is written by the same person, or in a short time frame, so not everybody might fully understand every module and every import of the code they're editing.
Erik
On Sun, Oct 19, 2014 at 1:32 AM, David Feuer <
david.feuer@
> wrote:
I'm generally in favor of the proposal, but I figured I should mention one situation when I personally might find this confusing. If the module import list is very long, and includes an unrestricted import of a well-known module, it might be easy to assume a certain well-known function comes from there, when in fact it comes from some other module on the other end of the import list.
On Oct 18, 2014 6:39 PM, "Joachim Breitner" <
mail@
>
wrote:
Hi,
I guess my central point is I don't see how anyone can benefit from
Am Samstag, den 18.10.2014, 11:02 -0700 schrieb htebalaka: the
current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
I find this quite convincing. If I bother to explicitly write out „take putStrLn from Data.Text.Lazy.IO“, why should the compiler assume that I might have meant some putStrLn from somewhere else.
Of course, order should not matter (I don’t think anyone suggested it should, I think Austin simply mis-read that).
Greetings, Joachim
-- Joachim “nomeata” Breitner
mail@
• http://www.joachim-breitner.de/
Jabber:
nomeata@
• GPG-Key: 0xF0FBF51F
Debian Developer:
nomeata@
_______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
_______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
_______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57583... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

On 14-10-19 08:10 AM, Erik Hesselink wrote:
I feel that this extension, while looking tempting for writing code from scratch, might hurt maintainability of code.
That depends on how you define maintainability.
Adding an explicit import can suddenly cause type errors in completely unrelated places (when it hides an implicit import and the new function is type incorrect), or worse, can cause semantic change (when it hides an implicit import and the new function is type correct, but has different behavior). Remember that not all code is written by the same person, or in a short time frame, so not everybody might fully understand every module and every import of the code they're editing.
Your example requires somebody actively editing the import list. A code change causes a compile error or worse? That is not all that surprising. No, what I find much worse is a cabal update causing an error in a module that was correct before the update. Consider
module Main where
import Foo (foo) import Bar
main = foo
Now suppose Bar came from the package bar-1.0, and the new version bar-1.0.1 decides to export foo. With the current behaviour, this change would break my module. With Malcolm's proposal the old code would continue to work. Anyway, count me as +1 on the proposal. It would improve the long-term stability of Haskell code.

On Mon, Oct 20, 2014 at 11:57 PM, Mario Blažević
On 14-10-19 08:10 AM, Erik Hesselink wrote:
Adding an explicit import can suddenly cause type errors in completely unrelated places (when it hides an implicit import and the new function is type incorrect), or worse, can cause semantic change (when it hides an implicit import and the new function is type correct, but has different behavior). Remember that not all code is written by the same person, or in a short time frame, so not everybody might fully understand every module and every import of the code they're editing.
Your example requires somebody actively editing the import list. A code change causes a compile error or worse? That is not all that surprising.
But right now, we have a useful property that adding imports and code to a module does not break or change other code in that module. With this extension, that changes. I find this kind of local reasoning very useful, IMHO it's one of the most useful things about Haskell in general.
No, what I find much worse is a cabal update causing an error in a module that was correct before the update. Consider
module Main where
import Foo (foo) import Bar
main = foo
Now suppose Bar came from the package bar-1.0, and the new version bar-1.0.1 decides to export foo. With the current behaviour, this change would break my module. With Malcolm's proposal the old code would continue to work.
That's a very good point. Given that and the above, I don't understand your conclusion:
Anyway, count me as +1 on the proposal. It would improve the long-term stability of Haskell code.
How is adding more ways to break something "improving the long-term stability"? Regards, Erik

I'm having a hard time keeping track of what's going on in this discussion. But, I'm generally in favor of making *some* change along the lines discussed here, and also in #9702 (https://ghc.haskell.org/trac/ghc/ticket/9702). Could the proposers of various features perhaps create a wiki page, naming the individual pieces or proposals, and then we can have something closer to a vote among a stable list of candidates?
Thanks!
Richard
On Oct 21, 2014, at 7:14 AM, Erik Hesselink
On Mon, Oct 20, 2014 at 11:57 PM, Mario Blažević
wrote: On 14-10-19 08:10 AM, Erik Hesselink wrote:
Adding an explicit import can suddenly cause type errors in completely unrelated places (when it hides an implicit import and the new function is type incorrect), or worse, can cause semantic change (when it hides an implicit import and the new function is type correct, but has different behavior). Remember that not all code is written by the same person, or in a short time frame, so not everybody might fully understand every module and every import of the code they're editing.
Your example requires somebody actively editing the import list. A code change causes a compile error or worse? That is not all that surprising.
But right now, we have a useful property that adding imports and code to a module does not break or change other code in that module. With this extension, that changes. I find this kind of local reasoning very useful, IMHO it's one of the most useful things about Haskell in general.
No, what I find much worse is a cabal update causing an error in a module that was correct before the update. Consider
module Main where
import Foo (foo) import Bar
main = foo
Now suppose Bar came from the package bar-1.0, and the new version bar-1.0.1 decides to export foo. With the current behaviour, this change would break my module. With Malcolm's proposal the old code would continue to work.
That's a very good point. Given that and the above, I don't understand your conclusion:
Anyway, count me as +1 on the proposal. It would improve the long-term stability of Haskell code.
How is adding more ways to break something "improving the long-term stability"?
Regards,
Erik _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

Hi Erik,
But right now, we have a useful property that adding imports and code to a module does not break or change other code in that module. With this extension, that changes.
So your biggest concern is about silent breakage of code, that suddenly a different function is used which has the same name and type, but different semantics. In all other cases the compiler would throw an error, because the types wouldn't match. I have the feeling, that the proposal shouldn't be about implicit/explicit imports, but about prefering the function with the fitting type. If there're two functions with the same name and type in scope, then the compiler most likely should always throw an error. The only exception might be, if there's a function locally defined in the module, then this one might be prefered over every imported one with the same name and type. But perhaps even in this case it might be a good idea to throw an error. Greetings, Daniel

On 14-10-21 07:14 AM, Erik Hesselink wrote:
On Mon, Oct 20, 2014 at 11:57 PM, Mario Blažević
wrote: On 14-10-19 08:10 AM, Erik Hesselink wrote:
Adding an explicit import can suddenly cause type errors in completely unrelated places (when it hides an implicit import and the new function is type incorrect), or worse, can cause semantic change (when it hides an implicit import and the new function is type correct, but has different behavior). Remember that not all code is written by the same person, or in a short time frame, so not everybody might fully understand every module and every import of the code they're editing.
Your example requires somebody actively editing the import list. A code change causes a compile error or worse? That is not all that surprising.
But right now, we have a useful property that adding imports and code to a module does not break or change other code in that module. With this extension, that changes. I find this kind of local reasoning very useful, IMHO it's one of the most useful things about Haskell in general.
Right now, adding an import can certainly cause a compiler error. It can't change the run-time behaviour of a correct module, that is true. With the proposal, there would be still be a somewhat weaker guarantee: adding an *implicit* import would never change the module's run-time behaviour.
No, what I find much worse is a cabal update causing an error in a module that was correct before the update. Consider
module Main where
import Foo (foo) import Bar
main = foo
Now suppose Bar came from the package bar-1.0, and the new version bar-1.0.1 decides to export foo. With the current behaviour, this change would break my module. With Malcolm's proposal the old code would continue to work.
That's a very good point. Given that and the above, I don't understand your conclusion:
Anyway, count me as +1 on the proposal. It would improve the long-term stability of Haskell code.
How is adding more ways to break something "improving the long-term stability"?
You seem determined to misunderstand every message in this thread. What I said was that the change from bar-1.0 to bar-1.0.1 breaks the example code *with the current behaviour*. The proposal removes this breakage. Anything that keeps the old code compiling and working with new libraries improves its long-term stability in my book. Note that the bar-1.0.1 upgrade wasn't even a major version change. This is correct according to the PVP:
Otherwise, if only new bindings, types, classes, non-orphan instances or modules (but see below) were added to the interface, then A.B may remain the same but the new C must be greater than the old C.
but it still breaks the example module. Mind you, while the current proposal only increases the chances that old Haskell code continues to work with new libraries. If my module had defined a function foo itself instead of importing it, the upgrade would still break it. An extended version of the proposal might state that implicitly imported members get shadowed by local definitions as well, but this might be harder to implement. Besides, even this proposal wouldn't protect the importer from new instance imports from clashing with the local ones. There's no shadowing solution in this case. To summarize, the proposal is not providing any hard guarantees, and it's weakening an existing guarantee (see above). I'm still in favour, because it increases the stability of the legacy Haskell codebase.

On Tue, Oct 21, 2014 at 4:55 PM, Mario Blažević
On 14-10-21 07:14 AM, Erik Hesselink wrote:
On Mon, Oct 20, 2014 at 11:57 PM, Mario Blažević
wrote: No, what I find much worse is a cabal update causing an error in a module that was correct before the update. Consider
module Main where
import Foo (foo) import Bar
main = foo
Now suppose Bar came from the package bar-1.0, and the new version bar-1.0.1 decides to export foo. With the current behaviour, this change would break my module. With Malcolm's proposal the old code would continue to work.
That's a very good point. Given that and the above, I don't understand your conclusion:
Anyway, count me as +1 on the proposal. It would improve the long-term stability of Haskell code.
How is adding more ways to break something "improving the long-term stability"?
You seem determined to misunderstand every message in this thread. What I said was that the change from bar-1.0 to bar-1.0.1 breaks the example code *with the current behaviour*. The proposal removes this breakage. Anything that keeps the old code compiling and working with new libraries improves its long-term stability in my book.
Note that the bar-1.0.1 upgrade wasn't even a major version change. This is correct according to the PVP:
You're right, I misread your message. I understand now, and you're right that this is currently something that can break your package, although it happens so rarely that people still depend on major version ranges, even though they sometimes don't use explicit or qualified imports. Regards, Erik

The current situation could be summed up as requiring some code duplication when explicitly importing (due to the possibility of one or more redundant hides), while encouraging a sort of measure-twice-cut-once mentality. Not suggesting changing the default; I'll have a proposal detailing the exact semantics and possible consequences of using it on the wiki on Thursday, so that more people can weigh in. Erik Hesselink wrote
On Tue, Oct 21, 2014 at 4:55 PM, Mario Blažević <
mblazevic@
> wrote:
On 14-10-21 07:14 AM, Erik Hesselink wrote:
On Mon, Oct 20, 2014 at 11:57 PM, Mario Blažević <
mblazevic@
>
wrote:
No, what I find much worse is a cabal update causing an error in a module that was correct before the update. Consider
module Main where
import Foo (foo) import Bar
main = foo
Now suppose Bar came from the package bar-1.0, and the new version bar-1.0.1 decides to export foo. With the current behaviour, this change would break my module. With Malcolm's proposal the old code would continue to work.
That's a very good point. Given that and the above, I don't understand your conclusion:
Anyway, count me as +1 on the proposal. It would improve the long-term stability of Haskell code.
How is adding more ways to break something "improving the long-term stability"?
You seem determined to misunderstand every message in this thread. What I said was that the change from bar-1.0 to bar-1.0.1 breaks the example code *with the current behaviour*. The proposal removes this breakage. Anything that keeps the old code compiling and working with new libraries improves its long-term stability in my book.
Note that the bar-1.0.1 upgrade wasn't even a major version change. This is correct according to the PVP:
You're right, I misread your message. I understand now, and you're right that this is currently something that can break your package, although it happens so rarely that people still depend on major version ranges, even though they sometimes don't use explicit or qualified imports.
Regards,
Erik _______________________________________________ Glasgow-haskell-users mailing list
Glasgow-haskell-users@
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57584... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

It's occurred to me that a much simpler description of the proposed change would be automatically lifting *uses* of the explicitly imported variable to a qualified use, /if/ there was only one explicit import of the identifier. For instance, with the following import: import Data.Text.Lazy.IO (putStrLn) import Prelude import qualified Data.List as L import qualified MyModule as L (isInfixOf) automatically change any unqualified (and un-shadowed) use of putStrLn to Data.Text.Lazy.IO.putStrLn, and any use of L.infixOf to MyModule.isInfixOf. The latter should be easier to implement covering a number of corner cases: * you can still refer to Prelude.putStrLn or Data.List.isInfixOf by prepending the path, which the naive auto-hiding transformation would prevent. * possibly easier to have the pragma available to use in GHCi, since you don't need to retroactively hide identifiers from previously imported modules when a new explicit import is made. * works better with imports like "import Data.Either (Either(..)); import MyModule (Left)", which would otherwise require translating the Data.Either import to something like "import Data.Either (Either(..)) hiding (Left)" which isn't currently possible or "import Data.Either (Either(Right))" which can't be done without knowing which modules export which identifiers. The only remaining corner case is if you shadow the name of a module like: import Data.Text.Lazy.IO (putStrLn) import Prelude as Data.Text.Lazy.IO An unqualified user of putStrLn /should/ refer to the text version, but if anything translating it to Data.Text.Lazy.IO.putStrLn makes it more ambiguous. I assume GHC can disambiguate even if the user can't, or if you didn't want to cover that case the existing ambiguous identifier occurrence error could still fire. Really just depends where in the pipeline the change is implemented. I suppose I'll describe both possible implementations. Functionally they're the same, but maybe this one is simpler. -- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57584... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

Proposal is up for your viewing pleasure: http://www.haskell.org/haskellwiki/PermissiveImportsProposal -- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57585... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.

Thanks for writing this up so clearly.
I'm +1 on the proposal as written. I think the (smallish) drawbacks are mitigated by the fact that the whole feature is opt-in.
Implementation should be relatively straightforward.
Thanks,
Richard
On Oct 23, 2014, at 2:22 PM, htebalaka
Proposal is up for your viewing pleasure: http://www.haskell.org/haskellwiki/PermissiveImportsProposal
-- View this message in context: http://haskell.1045720.n5.nabble.com/Hiding-import-behaviour-tp5758155p57585... Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com. _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

On 2014-10-19 at 00:39:42 +0200, Joachim Breitner wrote:
I guess my central point is I don't see how anyone can benefit from the current behaviour. For instance, a simple real world example:
import Prelude import Data.Text.Lazy.IO (putStrLn)
I find this quite convincing. If I bother to explicitly write out „take putStrLn from Data.Text.Lazy.IO“, why should the compiler assume that I might have meant some putStrLn from somewhere else.
I think we've reached the point where this proposal should be turned into a Wiki page detailing the exact semantics[1], and giving this extension a catchy name... If it's trivial enough (in the sense there is little design-space variation to get it wrong), having it in 7.10 rather than 7.12 may be still possible (and IMHO desirable to be able to start using it earlier rather than later on real-world code). [1]: Cases to consider include all combinations of variations of import-statements; as well as turning most unqualified imports from which entities are to be hidden into a pair of qualified and unqualified import: import Doo (doo) import Bar -- provide 'doo' as well which I'd think should be transformed into import Doo (doo) import qualified Bar import Bar hiding (doo) as to allow to still have 'Bar.doo' in scope (unless some other import explicitly mentions `... as Bar (doo)`, in which case that needs to be hidden from the qualified `Bar` import as well) Cheers, hvr
participants (12)
-
Alexander Berntsen
-
Austin Seipp
-
Daniel Trstenjak
-
David Feuer
-
Erik Hesselink
-
Herbert Valerio Riedel
-
htebalaka
-
Joachim Breitner
-
Malcolm Gooding
-
Mario Blažević
-
Merijn Verstraaten
-
Richard Eisenberg