5 ms·
When did shadowing become a feature? I was under the impression it's an anti-pattern. As per the example in the article > const foo = Foo.init(); > const foo2
by lnenad 2y ago
When did shadowing become a feature? I was under the impression it's an anti-pattern. As per the example in the article
> const foo = Foo.init();
> const foo2 = try foo.addFeatureA();
> const foo3 = try foo.addFeatureB();
It's a non issue to name vars in a descriptive way referring to the features initial_foo for example and then foo_feature_a. Or name them based on what they don't have and then name it foo. In the example he provided for Rust, vars in different scopes isn't really an example of shadowing imho and is a different concept with different utility and safety. Replacing the value of one variable constantly throughout the code could lead to unpredictable bugs.
- saithound 2y agoShadowing always has been a feature, doubly so in languages which lack linear types. It is a promise to the reader (and compiler) that I will have no need of the old value again. Notice that applying the naming convention you suggest does nothing to prevent the bug in the code you quoted. It might be just as easy to write const initial_foo = Foo.init(); > const foo_feature_A = try initial_foo.addFeatureA(); > const foo_feature_B = try initial_foo.addFeatureB(); but it's also just as wrong. And even if you get it right, when the code changes later, somebody may add const foo_feature_Z = try foo_feature_V.addFeatureX();. Shadowing prevents this.
- nine_k 2y agoSaid promise should also be checked for sanity. E.g. for i in range(N) { for i in range(M) { # Typo; wanted j. # The compiler should complain. } }
- Maxatar 2y agoThe Rust compiler would complain in this case that the initial i variable is unused. Unused variables should be named with an underscore, _.
- dpc_01234 2y agoShadowing is a feature. It's very common that given value transforms its shape and previous versions become irrelevant. Keeping old versions under different names would be just confusing. With type system there is no room for accidental misuse. I write Rust professionally for > 2 years, and years before that I was using it my own projects. I don't think shadowing ever backfired on me, while being very ergonomic.
- lnenad 2y agoDepending on which language you are using shadowing could lead to either small issues or catastrophic ones (in the scope of the program). If you have Python and you start with a number but end up with a complex dict this is very different than having one value in Rust and a slightly different value which is enforced by the compiler.
- Maxatar 2y agoDon't see how it could introduce bugs. The point of replacing a variable is precisely to make a value that is no longer needed inaccessible. If anything introducing new variables with new names has the potential to introduce subtle bugs since someone could mistakenly use one of the variables that is no longer valid or no longer needed.
- sjburt 2y agoWhen you are modifying a long closure and don’t notice that you are shadowing a variable that is used later. I know “use shorter functions” but tell that to my coworkers.
- zamalek 2y agoThe example given isn't that great. Here's a significantly more common one: var age = get_string_from_somewhere(); var age = parse_to_int(age); Without same-scope shadowing you end up with the obnoxious: var age_string = get_string_from_somewhere(); var age = parse_to_int(age_string); Note that your current language probably does allow shadowing: in nested scopes (closures).
- chrisco255 2y agoChanging the type on a value is an anti-pattern, in my opinion. It's not obnoxious to be explicit in your variable names.
- antonvs 2y agoIt’s been a feature in languages for at least half a century. Scheme’s lexical scoping supported it in 1975, and Lisp adopted that.
- lnenad 2y agoYeah, it's a feature of a language, doesn't mean we are forced to use it.
- antonvs 2y agoYou asked when it became a feature. I answered that. But your antipathy towards the feature is misplaced. Several languages with the most rigorous foundations support shadowing: SML, Ocaml, Haskell, Scheme, You're probably more familiar with languages that have unrestricted mutation, in which case something much worse than shadowing is allowed: changing the value of an existing variable.
- physicles 2y agoOver the years, I’ve wasted 1-2 days of my life debugging bugs caused by unintentional variable shadowing in Go (yes, I’ve kept track). Often, the bug is caused by an accidental use of := instead of =. I don’t understand why code that relies on shadowing isn’t harder to follow. Wish I could disable it entirely.
- lolinder 2y ago> Often, the bug is caused by an accidental use of := instead of =. This is a distinctly Go problem, not a problem with shadowing as a concept. In Rust you'd have to accidentally add a whole `let` keyword, which is a lot harder to do or to miss when you're scanning through a block. There are lots of good explanations in this subthread for why shadowing as a concept is great. It sounds like Go's syntax choices make it bad there.
- lnenad 2y ago> There are lots of good explanations in this subthread for why shadowing as a concept is great Not really. All of them boil down to ergonomics, when in reality it doesn't bring a lot of benefit other than people hating on more descriptive variable names (which is fair).
- lolinder 2y agoYou're saying ergonomics aren't a good explanation? The entire point of a programming language over writing machine code boils down to ergonomics! You've got to do better than "it's just ergonomics" when your argument for a language to ban shadowing is also "just ergonomics". The debate here is about which one truly has better ergonomics!
- pcwalton 2y agoYou can (assuming you're talking about Rust)! Just use Clippy and add #[deny(clippy::shadow_reuse)]: https://rust-lang.github.io/rust-clippy/master/#shadow_reuse https://rust-lang.github.io/rust-clippy/master/#shadow_reuse My position on shadowing is that it's a thing where different projects can have different opinions, and that's fine. There are good arguments for allowing shadowing, and there are good arguments for disallowing it.
- lolinder 2y ago> Replacing the value of one variable constantly throughout the code could lead to unpredictable bugs. Having variables with scopes that last longer than they're actually used and with names that are overly long and verbose leads to unpredictable bugs, too, when people misuse the variables in the wrong context later. When I have `initial_foo`, `foo_feature_a`, and `foo_feature_b`, I have to read the entire code carefully to be sure that I'm using the right `foo` variant in subsequent code. If I later need to drop Feature B, I have to modify subsequent usages to point back to `foo_feature_a`. Worse, if I need to add another step to the process—a Feature C—I have to find every subsequent use and replace it with a new `foo_feature_c`. And every time I'm modifying the code later, I have to constantly sanity check that I'm not letting autocomplete give me the wrong foo! Shadowing allows me to correctly communicate that there is only one `foo` worth thinking about, it just evolves over time. It simulates mutability while retaining all the most important benefits of immutability, and in many cases that's exactly what you're actually modeling—one object that changes from line to line.
- lnenad 2y ago> When I have `initial_foo`, `foo_feature_a`, and `foo_feature_b`, I have to read the entire code carefully to be sure that I'm using the right `foo` variant in subsequent code. If I later need to drop Feature B, I have to modify subsequent usages to point back to `foo_feature_a`. Worse, if I need to add another step to the process—a Feature C—I have to find every subsequent use and replace it with a new `foo_feature_c`. And every time I'm modifying the code later, I have to constantly sanity check that I'm not letting autocomplete give me the wrong foo! When you have only one `foo` that is mutated throughout the code you are forced to organize the processes in your code (validation, business logic) based on the current state of that variable. If your variables have values which are logically assigned you're not bound by the current state of that variable. I think this a big pro. The only downside most people disagreeing with me are mentioning is related to ergonomics of it being more convenient.
- lolinder 2y ago> When you have only one `foo` that is mutated throughout the code you are forced to organize the processes in your code (validation, business logic) based on the current state of that variable. If your variables have values which are logically assigned you're not bound by the current state of that variable. If I'm understanding you right, this is just restating what I said as a positive thing. I stand by my assertion that it's not positive: you can always choose to leave previous states accessible by choosing different names. But if a language doesn't support shadowing then I don't have the capability to intentionally restrict myself from accessing those states. That means your language has less expressive power and fewer opportunities for me to build myself guardrails. In some ways it's the opposite of unused variable warnings: if you disallow shadowing, the compiler is forcing you to leave variables accessible long after you need them. You're given no choice but to leave unused variables hanging around. With shadowing, I can choose the right path based on the situation. > The only downside most people disagreeing with me are mentioning is related to ergonomics of it being more convenient. As I said elsewhere, literally everything to do with programming languages is about ergonomics. Your arguments against shadowing boil down to ergonomics. You can't avoid having a debate by just saying "it's just ergonomics" when the debate that you started is which feature is more ergonomic!