Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variable shadowing #13

Open
LPeter1997 opened this issue Feb 2, 2022 · 0 comments
Open

Variable shadowing #13

LPeter1997 opened this issue Feb 2, 2022 · 0 comments
Labels
Language idea One single idea which may be a start of a design document
Milestone

Comments

@LPeter1997
Copy link
Member

LPeter1997 commented Feb 2, 2022

Introduction

Shadowing happens, when two variables are visible within the same scope, but one is inaccessible due to the others definition with the same name. For example:

int x = 0;
{
    string x = "hello";
    // Here only the string typed x is available
}
// Here we can access the integer x again

Shadowing in C#

C# only allows shadowing between field members and locals, but not between locals themselves. Meaning that the following is valid:

class Foo
{
    int x;
    public void Bar()
    {
        string x; // int x; shadowed
    }
}

But this is not:

class Foo
{
    public void Bar()
    {
        int x;
        {
            string x; // ERROR
        }
    }
}

I believe this is a very annoying constraint. It disables the useful cases for shadowing, but allows for the age-old typo:

class Foo
{
    int x;
    
    public Foo(int x_)
    {
        x = x; // Oops
    }
}

Note, that this can be solved by always requiring a this./self. prefix - like Python, Rust, ... - but that should be a design document on it's own.

Shadowing in C++

C++ is a bit less restricted, allowing nested scopes to shadow variables in the outer scope:

int x = 0;
{
    std::string x = "Hello";
}

This is useful sometimes, but can lead to some confusion. When you leave the inner scope, you have to keep in mind that you access that old variable again.

A case for shadowing same-scoped variables (how Rust does it)

I believe that if shadowing is allowed, it should allow to shadow same-scoped variables as well. I'll bring up two use-cases for it.

Type conversions

In many cases I have a pattern like this in C#:

public static Pattern LoadPattern(string fileName)
{
    string patternText = File.LoadAllText(fileName); // Need the 'Text' suffix, even though it's the same thing, but in a different type/state
    Pattern pattern = ParsePattern(fileName);
    // ...
}

After I'm done with the conversion from text to the object model, I'd probably not want to ever refer to the text. I should be able to name them the same, since they describe the same thing in a different shape:

public static Pattern LoadPattern(string fileName)
{
    string pattern = File.LoadAllText(fileName);
    Pattern pattern = ParsePattern(pattern); // Refers to the old pattern variable
    // From here it's Pattern type
    // ...
}

After the conversion, pattern is only visible as a Pattern type, hiding the string version forever.

Mutability

There are algorithms, where I calculate some helper variable (like a LUT), but then I expect it to never change again. Allowing shadowing would allow us to re-bind the value as an immutable (using the notation from #12):

var helper = ...;
// mutating helper
val helper = helper;
// from here on 'helper' is immutable

This ensures that after a certain point helper can not be mutated. Note that again, there's no scoping confusion, the mutable helper is hidden forever.

To be decided

I believe we should either completely disallow shadowing, or allow shadowing in the same scope. Both have their benefits and annoyances. I can't whole-heartedly put my vote for either, but I'm kind of leaning towards allowing arbitrary shadowing.

An alternative

If we find enough use for the same-scoped shadowing, but not the nested ones, we could even think of some unique constraints, like only allowing the same-scoped case. We could even put further restrictions, like only allowing to re-bind to make a variable immutable, but not change the shadowed type. I don't know of any language that does this. Allowing re-binding an immutable to a mutable variable might be dangerous.

@LPeter1997 LPeter1997 added the Language idea One single idea which may be a start of a design document label Feb 2, 2022
@WhiteBlackGoose WhiteBlackGoose added this to the 0.1 milestone Jun 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Language idea One single idea which may be a start of a design document
Projects
None yet
Development

No branches or pull requests

2 participants