Hiding import behaviour

htebalaka goodingm at gmail.com
Sat Oct 18 19:22:49 UTC 2014


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:
>>>>> On 17/10/14 00:40, Austin Seipp 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
>>>>> 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-tp5758155p5758246.html
>>>> 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-tp5758155p5758257.html
Sent from the Haskell - Glasgow-haskell-users mailing list archive at Nabble.com.


More information about the Glasgow-haskell-users mailing list