Allow top-level shadowing for imported names?
Michael Sloan
mgsloan at gmail.com
Thu Oct 6 02:02:09 UTC 2016
It is really good to think in terms of a cleverness budget. Every
additional feature is not only implementation work, but also
maintenance, extra cognitive overhead when adding new features, an
extra thing for tooling outside of GHC to support. Personally, I'm on
the wall on this one. Here are the things I see in favor of this
proposal:
1) It is common practice to use -Wall. Infact, I think it should be a
default, and should include more warnings than -Wall (-fwarn-tabs
-fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates
-fwarn-identities)
2) It lets us do things that are otherwise quite inconvenient. We can
now easily shadow a bunch of identifiers without explicitly hiding
them.
Things I see against it:
1) It feels like a complicated way to avoid needing a "hiding ()"
clause on your imports.
2) There is no good way to use this feature without creating a
warning. I would like to be explicit in my name shadowing I'm
thinking a pragma like {-# NO_WARN myFunction #-}, or, better yet, the
more specific {-# SHADOWING myFunction #-} orso.
What if instead we re-framed this as a "top-level where clause", like this:
main :: IO ()
main = putStrLn ("Hi" <> "There")
other-function :: IO ()
other-function = putStrLn ("I can " <> "also use it")
-- NOTE: 0 indent!
where
(<>) :: String -> String -> String
(<>) = (++)
I would also like to see this extension enabling other top level
declarations in where clauses, except not typeclasses. I have
discussed this before, IIRC, with Richard, and there was some
complexity getting local data types playing well with scoped type
variables. It seems like a very worthwhile extension in its own
right.
I like this top-level where clause, because it riffs on existing
syntax. Also, there isn't very much danger in someone accidentally
dedenting their where clause to 0 level and not realizing it. The
only danger there is if they were also simultaneously relying on scope
shadowing (and somehow the types still work out).
-Michael
On Wed, Oct 5, 2016 at 9:35 AM, Christopher Allen <cma at bitemyapp.com> wrote:
> I agree with Tom on this. This isn't a good way to spend the cleverness budget.
>
> On Wed, Oct 5, 2016 at 11:34 AM, <amindfv at gmail.com> wrote:
>> I'm weakly against this proposal. I may compile with -Wall, but I read code by many people who don't. When I'm browsing a file and see e.g.
>>
>> import Network.Socket
>>
>> and then later in the file, I see a reference to "recvFrom", I currently know exactly what function is being called. I don't want to have to search around every time to make sure a function wasn't redefined in some dark corner of the module.
>>
>> This allows too much "sneakiness" for my taste.
>>
>> Tom
>>
>>
>>> On Oct 3, 2016, at 04:29, Herbert Valerio Riedel <hvriedel at gmail.com> wrote:
>>>
>>> Hi *,
>>>
>>> I seem to recall this was already suggested in the past, but I can't
>>> seem to find it in the archives. For simplicity I'll restate the idea:
>>>
>>>
>>> foo :: Int -> Int -> (Int,Int)
>>> foo x y = (bar x, bar y)
>>> where
>>> bar x = x+x
>>>
>>> results merely in a name-shadowing warning (for -Wall):
>>>
>>> foo.hs:4:9: warning: [-Wname-shadowing]
>>> This binding for ‘x’ shadows the existing binding
>>> bound at foo.hs:2:5
>>>
>>>
>>> However,
>>>
>>> import Data.Monoid
>>>
>>> (<>) :: String -> String -> String
>>> (<>) = (++)
>>>
>>> main :: IO ()
>>> main = putStrLn ("Hi" <> "There")
>>>
>>> doesn't allow to shadow (<>), but rather complains about ambiguity:
>>>
>>> bar.hs:7:23: error:
>>> Ambiguous occurrence ‘<>’
>>> It could refer to either ‘Data.Monoid.<>’,
>>> imported from ‘Data.Monoid’ at bar.hs:1:1-18
>>> or ‘Main.<>’, defined at bar.hs:4:1
>>>
>>>
>>> This is of course in line with the Haskell Report, which says in
>>> https://www.haskell.org/onlinereport/haskell2010/haskellch5.html#x11-1010005.3
>>>
>>> | The entities exported by a module may be brought into scope in another
>>> | module with an import declaration at the beginning of the module. The
>>> | import declaration names the module to be imported and optionally
>>> | specifies the entities to be imported. A single module may be imported
>>> | by more than one import declaration. Imported names serve as top level
>>> | declarations: they scope over the entire body of the module but may be
>>> | shadowed by *local non-top-level bindings.*
>>>
>>>
>>> However, why don't we allow this to be relaxed via a new language
>>> extensions, to allow top-level bindings to shadow imported names (and
>>> of course emit a warning)?
>>>
>>> Unless I'm missing something, this would help to keep existing and
>>> working code compiling if new versions of libraries start exporting new
>>> symbols (which happen to clash with local top-level defs), rather than
>>> resulting in a fatal name-clash; and have no major downsides.
>>>
>>> If this sounds like a good idea, I'll happily promote this into a proper
>>> proposal over at https://github.com/ghc-proposals/ghc-proposals; I
>>> mostly wanted to get early feedback here (and possibly find out if and
>>> where this was proposed before), before investing more time turning
>>> this into a fully fledged GHC proposal.
>>>
>>> Cheers,
>>> HVR
>>> _______________________________________________
>>> ghc-devs mailing list
>>> ghc-devs at haskell.org
>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
>> _______________________________________________
>> ghc-devs mailing list
>> ghc-devs at haskell.org
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
>
>
>
> --
> Chris Allen
> Currently working on http://haskellbook.com
> _______________________________________________
> ghc-devs mailing list
> ghc-devs at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
More information about the ghc-devs
mailing list