Name light shadowing proposal idea

GHC has a name-shadowing option which allow to generate a warning or an error

whenever an inner-scope value has the same name as an outer-scope value,

This is really helpful so I turn it on. However there is lots of “false negative” where the shadowing is in fact armless.

For example, I often deal with products (as thing to buy), so I have a Product data type but can’t have variable name product :: Product because this shadows product :: [a] -> a from the prelude.

To avoid that, I have a few options. The easier one is to use a different name for local binding like product_ or hide product from the Prelude. The former is a bit ugly and the later is not practical. To hide something from the prelude you need import it (and so stop it from being imported implicitely) which is a bit faffy.

According to GHC doc, this is to prevent

inadvertent capture of what would be a recursive call in f = ... let f = id in ... f ... .

Well, this doesn’t happen when shadowing something from another module.

What I propose is either
1- a way to ignore shadowing of all imported names
2 - a way to ignore shadowing from implicit Prelude.
3- a way to ignore shadowing of non explicitely imported names
4- a way to ignore shadowing of names outside the current binding (its top level name + local bindings)
5 - (my favorite, but more likely the hardest to do) ignore shadowing if the code compile and the shadowing and the shadowed name don’t share the same type signature.

Does anyone think it is worth making a proposal ?
I would be prepare to try implementing (if I manage to compile GHC).

I think the problem with 5 is that shadowing can still be a problem even if the type signatures are different, due to polymorphism, e.g.:

-- cosmologist's pi
pi :: Int
pi = 1
f x y = if pi > 2 then x else y

This code will still compile if you remove the custom definition of pi, but it will get a different meaning.

I feel like the simplest way to solve this is just to use a custom Prelude which doesn’t export the conflicting names. That is a bit more work than using a different warning flag, but it is not that much work.

1 Like

That’s a good point. Another way of stating 5 is if the code compile using the shadower but doesn’t using the shadowed name (they don’t have the same type but are in practice compatible). In that case both pi are a valid choice and therefore a shadowing warnig should be issue.

Custom prelude is a pain because it forces you to hide something for the all project.

Depending on which people are contributing to the codebase, I would consider picking a synonym: if I see product in the wild I think about ∏, not about an accessor.

product was the first example which came into my mind. However when a data type is called Product (which happen lots in ecommerce context) , it’s already too late to find a synonym. You’ll get producs :: [Product] and sudently productSynonym :: Product.