Rust-style variable shadowing

This heavily depends on what you’re doing, and is definitely something pretty subjective. As you probably read here, there are many people that say the exact opposite. these parsed and validated variables still contain the exact same meaning as the data from before, the semantic meaning did not change. Shadowing the previous variables has obvious benefits, because it allows you to clearly focus on how your data is flowing through your function the same way as you could if you just did a method-chain without intermediate variables (which, in my examples, is mostly a viable option, but there are many cases where you don’t want that or where you’d have to do things in between, making it not an option). It also forces you to actually use the variable you want, making it impossible to accidentally refer to an old, unvalidated / normalized / parsed variable. It does, imo, not help to have variable names like val userInputString, val normalizedUserInput, val parsedUserInput, because they just make reading and typing the code harder for no real benefit. especially when these calls are directly next to each other, the data flow is more obvious when the names stay the same.
I do still get your point that there are also cases where it’s a bad idea, and I see that you personally dislike this, which is in some way understandable. But most comments and thoughts from rust-people suggest that having this exact way of giving the same name to a variable pre-parsing and post-parsing is a good and helpful thing.

I see. But why wouldn’t it be good practice? Do you have any arguments against it? If it’s necessary to have a mutable version of a function parameter, I’d say it’s a great idea to just shadow the original parameters name, as you don’t have any use for that parameter in its read-only form.

Also, the main point regarding mutability is not to change vals to vars, but the other way around. (I think shadowing function parameters is actually possible at the moment, altough warned against, which might be why you think it’s bad practice).
The idea is to constrain mutability as much as possible by shadowing a variable that needed to be mutable for some bit of code with it’s read-only form to disallow further unwanted mutation and to make it obvious to the reader that the variable is not supposed to change from that point on.

Always remember, “everyone can write code a computer can understand. The hard thing is to write code that Humans can understand”. Every hint as to what a variable is meant to mean is a good thing, and every unnecessary bit of boilerplate and noise (even in variable names, especially when it’s just their type appended to them) that you can avoid does help make the data-flow a bit clearer.

I don’t see how this relates to this feature. Kotlin does, in fact, equally want you to write safe code. Rust does, in fact, not force you to write low-level code. While you do face a few more low-level things in rust, most use-cases for variable shadowing in Rust that I’ve seen and heard had nothing to do with unsafe code, raw pointers or bit-streams. Nearly all are about the same things we are talking ab out here in kotlin: About parsing and validating data and unwrapping nullable (Option<T>) types.

I Don’t know how much experience you have with rust, but you definitely don’t work with “raw machine pointers or other low-level conversions” a lot. If you do, thats in unsafe blocks, which are pretty rarely needed, and which don’t have any special relationship with variable shadowing.

Please elaborate, as I actually don’t understand what your trying to say here.
How would you “bypass the process of conversion and validation”?
My proposal is not implying the ability to bypass anything. The idea is to have more readable and writable variable names and more safety by having guarantees that you’re not accidentally using old, unnormalized or unvalidated variables. This does not bypass anything.

I cannot really respond to this, as I don’t get the point behind the previous part. But I’ll repeat: this has nothing to do with low-level code. I’ll go into why, imo, modern high-level languages like Rust and Kotlin need this a lot more than languages like C later.

Seeing that multiple experienced Rust-developers have given examples of the benefits of variable-shadowing that are nearly identical to my example in the preface, I think it’s safe to say you’re just wrong here.
The programmer does already define the “difference” in source-code in a way, as he specifies what is done to the data. The semantic meaning stays the same. The only thing that changes is how far the data has gone through the necessary data-processing pipeline. would you say that when using call-chains (with map, filter, let, etc), you’d also want to give names - names that contain the type of the variable in some way - in the lambdas? Does your code look like this?

val myValidatedNormalizedParsedList = someStringList
  .map { str -> myParser.parse(str) } 
  .map { parsedData -> parsedData.normalize() }
  .filter { normalizedData -> normalizedData.isValid() } }
// or would you, as i would, just write
val myList = someStringList
  .map { myParser.parse(it).normalize() }
  .filter { it.isValid() }

and yes, I know I’ve condensed the parsing and normalization into a single map-call in the second example, whilst seperating them in the first. Because that’s what you imply: That you should always clearly show the current stage of data in the processing-pipeline in the name of the variable. Something that NEEDS seperate lines and variables for each step.

I think this is a good example, as it shows how we already have reached the same “solution” in method-chains: implicit it parameter, condensing multiple conversions into a single line and chain, without specifying the type in between. All I’m trying to do is bring the same ease of use to places where call chains are undesirable. It would otherwise be identical in use-case and “danger”, except for the problems of accidentally overwriting the variable later on in the code - which I obviously recognize and understand, and for which we’d need to find at least some sort of solution, or evidence that it does not matter as much as most people think.

Why Rust needs this, and how it matches why Kotlin could want it.

Rust, Kotlin, etc are all modern languages that have a complex and expressive type-system.
While languages like C would just return null and have you deal with it, Kotlin and Rust wrap these cases as dedicated types (in Rust this is Option<T>, in Kotlin it’s T?).
These wrapper types (Functors / Monads) are a bit more common in Rust than in Kotlin, because Rust also uses them as a replacement for recoverable Exceptions. In Rust you have Result<T,E>, which is used to represent failure state. Where Kotlin sadly still relies on Exceptions, Rust uses the type-system yet again, so there are more cases of “unwrapping” data from a Monad-like than in Kotlin, making this feature a bit more necessary and helpful in Rust, as in Kotlin you don’t need it as much.
BUT: Kotlin does have Result<T>, which will see more use in the near future. This, too, is a wrapper-type for values that replaces exceptions, making unwrapping values from containers such as Result<T> even more relevant.

Another reason Rust strongly benefit’s from these is how well it integrates with other language constructs like if let, which allows you to do a kind of pattern-match-if-combination like

if let Some(foo) = foo {
...
}

allowing you to extract the value of foo: Option<T> and use it without having to name it fooActual or having to name the option fooOption. Rust does not have implicit it or anything simmilar, so you have to name every variable you use, regardless of the context. In kotlin you’d do .let and use the implicit it.

These are all reasons Rust already has this, and Kotlin has not yet had a strong desire to add it. It is not strictly necessary, but just helpful.

I’d dislike “unsetting” variables in a direct way as that

  • would require new syntax, raising the barrier for this feature a lot
  • as you said, is a sign of a function that should be split up into smaller parts and should imo nearly never be necessary.

Another use-case:
There was a recent discussion about accessing nullable vars and how anoyying it is, because the compiler doesn’t allow you to use them even after if (foo != null) because “the variable could have changed in between statements”.
If you only want to read a variable, having the option to do

var myValue: String? = "hi"
fun foo() {
  val myValue ?: return
  println(myValue)
}

using this as a replacement for the usual

myValue?.let { println(it) }

construct would be a great improvement to readability.

I agree that this feature is awesome. When I discovered it in Rust, I wished that I had it everywhere. I’m often end up with stupid suffixes like ageString = ... ageInt = parseInt(ageString) and so on.

1 Like

I’m on the exactly opposite camp. I’d rather have name shadowing removed completely from Kotlin. I’m not sure about shadowing a field with a local variable, but shadowing a local variable with another local variable was a rather unpleasant surprise when coming from Java to Kotlin. Two main reasons.

First, when refactoring code like

fun foo() {
    val x = 123
    val y = 456
    val a = x + y
    bar(a)
}

fun bar(a: Int) {
    if (a < 0) {
        val x = abs(a)
        // ...
    }
}

if I want to move the if (a < 0) part from bar to foo, I can just copy-paste it. When I see that x is already defined in bar, I start thinking: what now? Are these two completely different things that accidentally happened to have the same name? Or should I reuse the same variable? If so, should I reassign a new value to it or just keep using the same one? Of course, the code above is simply made up, but it’s very often the case in real code, when local variables often have simple short names like name, or value, or sum.

Second, readability. Name shadowing hurts readability a lot. I have nothing about Rust, maybe things are very different there, but with Java… really. I often have to deal with legacy code. And I mean really legacy, like single-6000-lines-God-class, or hundred-fields-half-of-which-should-be-local-vars. When I see a variable, I just have to look up its definition, and I instantly have an idea of its scope. This helps. A lot. Of course, with clean code, it’s not that much of a problem, but still, when a newbie developer writes a piece of code that is not very clean, I can understand it much faster when I don’t have to keep track of all possible variable receclarations, among other things.

1 Like

So there are a couple points here:

The copy pasting (non)-issue

first of all, if your variables are named name, and there are multiple names in your scope, you most likely want to factor that part out into a second function, no matter if variable shadowing is allowed or not. I’d say the amount of times I have had variables that are named the exact same, yet meaning different things, in two scopes that I copy-pasted into each other - like you described - is zero. It just has never happened. Because most likely, you’ll either have better names than x, or the function where your variable is literally named x is a small helper function (for which imo it can make sense to have very short, non-descriptive variable names). But if it is one such helper function, I cannot see a point where you would copy-paste the content from one of these helpers into another one.

I’d need to see some actual real-world examples for how that could happen to believe it’s actually a realistic problem. Also, I’d say that it would be a good idea to have this be a compiler warning, at least at the beginning. That way you’d directly know that there is variable-shadowing going on, and could fix the issue by giving better names.

Also: Most redeclarations would change the type of the variable, making it impossible to “accidentally” use the shadowed one. And, because your copy-pasted variables will likely have a different type too, the copy-pasting issue is even less of a concern.

About readability

There are multiple points here as well:

“6000-lines-God-class legacy files”

  • You won’t find 6000 lines long legacy kotlin classes. Kotlin simply isn’t old enough to really have a probem with legacy codebases.
  • Even if it had, even if there are gigantic files, these will most likely not be one 6000 line long function, but rather many, mostly unrelated functions that should really be seperated into completely different classes and files. These unrelated functions will rarely share names, and even if they do, you’ll never copy-paste from one into the other.

“100 Fields that should really be local variables”

I see the problem, but it is unrelated to my proposal, as shadowing Class-members in local scopes is already possible, which will never change - for backwards-compatibility reasons, and because it can be very helpful (altough I’d say a class with many fields is most likely a bad code-smell anyways. Another sign of very bad coherence.)

Variable Scope

As I said, the problem with very big classes that have far to many fields is rarely seen in Kotlin. Most Kotlin code is pretty well structured, because Kotlin makes it a lot easier to have well-structured code than, say, Java would. Most Kotlin functions are either short - thus have no problem with unclear variable scope - or are long - thus should either be factored out into shorter functions, or - if for example they execute a series of actions that are tightly coupled - would benefit from restricting the scope of variables more using shadowing. These long functions are one of the main use-cases for Shadowing. Say you have a database-change to do. You change your immutable variable, so you’ll store it in a new variable myStateNew. now, a few lines later, you’ll do db.updateState(myState.id, myState). And there you have your incredibly hard to find bug. You’ve now just stored the old, unchanged state in your database. This would be impossible if you were able to just do val myState = myState.withSomeChanges(). It would have fixed your readability and made your code safer.

Also, I’d doubt that many beginners would misuse that feature, but I could be wrong. Maybe that’s something to - again - ask the Rust community about. While they most likely have less young university students that are completely new to programming that Kotlin does, they could still have valuable experience with regards to beginners.
I would guess that most beginners - or especially people coming from Java - would use var to begin with (not a good idea, but more likely than them reassigning variables “on accident”.)

Maybe (and this is just a quick thought, I’d most likely dislike it if this was necessary) a solution would be to introduce a soft keyword.

val foo = "4"
shad val foo = foo.toIntOrNull()
shad val foo = foo ?: 0

This would completely remove the main issue (accidental use of wrong variables), but would also raise the barrier of entry of this feature by a huge margin - introducing new syntax is a big step.

I think making it a compiler-warning would avoid 99% of the issues that it could bring, and it could allow people that know how to handle it to use it by just supressing that warning.

The cut-pasting issue

(Not copy-pasting).

A perfectly valid example.


fun foo(obj: MyClass) {
    val name = obj.name
    debug("Saving $name")
    saveToFile(obj)
    debug("Processing $name")
    // do something
}

fun saveToFile(obj: MyClass) {
    val name = obj.name
    Files.newOutputStream(Paths.get(name)).use { outp ->
        // save the object
    }
}

In both cases, name makes perfect sense. In the first case it refers to the name of an object, in the second case it’s a file name. They just happen to have the same name, type and value. Now I figure I need a better name for a file than just object’s name. But I don’t want to compose it in saveToFile() because it’s none of its business. So I’ll just pass an object and the name of the file to save it into, and compose the name outside of saveToFile(). So I refactor that part of the logic (right now represented by just val name = obj.name) outside first:


fun foo(obj: MyClass) {
    val name = obj.name
    debug("Saving $name")
    val name = obj.name
    saveToFile(obj, name)
    debug("Processing $name")
    // do something
}

fun saveToFile(obj: MyClass, name: String) {
    Files.newOutputStream(Paths.get(name)).use { outp ->
        // save the object
    }
}

Oops! Name clash! Undo, rename, repeat:

fun foo(obj: MyClass) {
    val name = obj.name
    debug("Saving $name")
    val fileName = obj.name
    saveToFile(obj, fileName)
    debug("Processing $name")
    // do something
}

fun saveToFile(obj: MyClass, fileName: String) {
    Files.newOutputStream(Paths.get(fileName)).use { outp ->
        // save the object
    }
}

Now I can safely change the value of fileName without touching name. With name shadowing I’d just open a whole can of worms.

Readability

The 6000-lines and 100-fields examples have nothing to do with the topic, just examples of how bad code can be. And I see no reason why Kotlin code is any better. If the same developer knew Kotlin, they would do exactly the same. The main argument here is that people do write bad code and the more terrible things the language allows them to do, the worse their code will be. In fact, when the same 6000-line-class guy wrote in C++, he would randomly abuse unions and cast pointers without giving a thought about strict aliasing (which he never heard of anyway) and even making up macros like begin_if and end_if that stood for { and } (he never bothered to update a 10-years-old IDE to something that tries to do some syntax highlighting). And I’m talking about ISS mission control software here, not some random throwaway stuff… Thankfully, he couldn’t do any of it with Java, which made things much simple when dealing with Java legacy code as compared to C++.

Now, the question is, of course, if name shadowing is really a terrible thing or not. Because if not, then the argument “people can abuse it” is, of course, wrong. But in 15 years of coding in C++ and Java I have never encountered a single case where I’d actually want to shadow a variable. Of all the examples I’ve seen here the only one I can relate to is reusing names in long functions, but I just use independent blocks for that (like one’d do with typical loop variables). When scopes don’t intersect, I’m completely fine with reusing the same name.

The myStateNew example

I don’t think I understand it at all. I’d just make that state a var and simply do myState = myState.withSomeChanges() without any redeclarations. Why stick to val for something that actually does change? If it varies, it’s certainly a variable, right? Why is it “not a good idea”?

And why this bug is so hard to catch? Looks like something that could be easily caught by a simple unit test. And at least it’s 100% reproducible and predictable, unlike some subtle threading issue that pops up once a few months and brings down the entire system.

1 Like

Possible bug

I see your example as something that could happen, but in this case:

  • you wouldn’t actually have introduced a bug, as you never meant to use the original variable afterwards.
  • You would immedately see the warning
  • You’d immediately see the variable shadowing, because it’s a small function

Readability

IDEs and bad Code

I see your point that people write bad code, yes. But I doubt that variable-shadowing is a tool that would noticably worsen the already bad code. especially when it’s a warning. I see that there are people not using IDEs, but I think those are rare, especially in Kotlin - where most people come from Android and are pretty much all using Android Studio.

Is it terrible

No, it is definitely not. What would be terrible is if you actually wanted to use it to “reuse variables in a long function”, because if you did need to do that, you definitely want a seperate function. You should never have a function that big that you’d need to reuse the same variable name in two different contexts, at least in my opinion.

myStateNew

The idea is that var, where variables don’t dynamically change, is a bad thing. var is good when you need to mutate a variable during a loop as some sort of aggregator, or to save your state.
Using var to allow for a variable to go through a non-dynamic (meaning that it is always the same, no calculations change how often or when the variable changes) is imo a code smell.
This is for several reasons:

  • var makes it possible to change the variable in places you had not intended to
  • var makes the variable changeable for the whole function, which is the exact thing we would try to avoid by having shadowing
  • you are not mutating here, you’re calculating a new one.

This bug is hard to catch not because it’s hard to see that there is a bug, but because this is one of the thing’s you’d incredibly easily overlook even when checking the function multiple times. because, especially for a function that is a bit longer, everything looks correct. you’re storing the state, you’re updating the state, etc. it doesn’t look wrong.

Some questions

Have you never had any point where you had to name variables ageStringNullable, ageString, age? where you had to encode additional information about the “type” of data it was in the name, because shadowing was disallowed?

Have you never had a nullable var field in your class that you needed to use?
In that case, you’d either have to .let your way through the usage of the variable, but then still find a better name like myVarNotNull for the lambda, or you’d have to do

val myVarNotNull = myVar ?: return

have you never had a point where you did not want to use a method-chain because it got less readable, but still used one to safe yourself from having to think of “good” names where no good names actually exist, because the meaning of the variable has not changed between this binding and the last one?

If you actually never where in those situations, I’d be surprised.

About Code quality

I see you come from a C++ background, where code seems to be a lot messier than in Kotlin or Rust.
Kotlin does, IMO, make it a lot easier to write good code, and a lot harder to write bad code. Null-safety forces you to either only have non-null fields that get immediately initialized, or to have null checks everywhere. It’s hard to have large amounts of hardly connected fields in a class because it makes writing code harder.

Also, Kotlin strongly encourages small, immutable data-classes, another thing that makes it less likely for big, unwieldly classes to appear.

As I said, maybe I’ll get some thought’s from beginner rust-programmers ( or from people that have worked with beginners in Rust) about the topic. It might make the situation a bit more clear.

I’d really recommend you to check out Rust, you’ll see what we’re talking about once you’ve used it. It’s one of these things you never know you missed.

If something can’t be changed we’ll, most of the time you could create a nice extension function for it.

When you construct nice chains like these, you probably will get to use descriptive names, where it also includes the name.
But if you look at such a name, you will immediately know the state of dust variable and don’t need to see the other process.

I do agree that it isn’t great that you have still access to the former versions, however, I definately don’t agree that the solution to this problem is that you should give the variables the same name…

Possible bug

No, I actually mean to use the original variable afterwards (the “do something part”). And no, it doesn’t need to be short either. Which leaves only the “warning“ argument. Which leads us to the question: if it’s a good practice, then why warning? If not, then why introduce it at all? The idea to have a warning that is intended to be suppressed or ignored, not fixed is… weird at least.

myStateNew

I still don’t get it. It changes, but it’s not dynamic? What the…? How is shadowing any different from mutating a variable? Harder to do accidentally? Perhaps. But Kotlin doesn’t allow assignments in the middle of an expression (like Java and C/C++ do), so that’s a question between accidentally writing state = vs val state = .

Of course I’m not mutating the object itself, I’m mutating the variable referring to it. It held a reference to one state, now it holds a reference to another.

Different names for the same thing

Of course I did encounter cases when apparently the same thing was named differently. However, many of those were solved in a way that didn’t require name shadowing, and some others can’t be solved with name shadowing anyway.

Usually if I have something like age and ageString, then I need to use both anyway. So shadowing is out of question. If I don’t need both, then I can separate the code into two functions: one that does the parsing (and works with strings) and another one that works with the parsed data. The first one will probably have both age and ageString, but that’s now a good thing because it’s easy to see what’s the input and what’s the output. In other words: when I need to encode the type in the variable name, I actually need it there because that makes code more readable, not less.

Regarding nullability, Kotlin has smart casts for that. I’d just write myVar ?: return and then continue to use myVar, which is now magically not nullable.

Anyway, I can’t seem to remember a single situation when name shadowing would be useful.

A (complementary?) alternative would be to implement support for the do block.

How would a do block be different from the normal lambda expressions like run {}?
Here’s an example with run:

fun main() {
//sampleStart
val x = run {
    val tmp= 5
    tmp * tmp+ 1
}
println(x)
//sampleEnd
}

about your myVar ?: return: that only works when myVar is val. If it is var, you cannot do that, as “the variable might have been changed by now” (and thus could be null again.)

It is different from mutation. Very different in fact. Mutating something is about “changing state”. It’s about having a variable that get’s changed as a result of different events (in the case of fields) or as some part of some algorithm (in the case of local vars).

This is mutation:

var sum = 0
for (i in 0 until 10) {
    sum += 10
}
println(sum)

This is shadowing:

val myNumber = "12"
val myNumber = myNumber.toInt()

The big difference is that shadowing does not allow for dynamic or conditional reassignments.
This is possible with mutation:

fun foo(addOne: Boolean): Int {
    var sum = 10
    if (addOne) {
        sum++
    }
    return sum
}

but not with shadowing:

fun foo(addOne: Boolean): Int {
    val sum = 10
    if (addOne) {
        val sum = sum + 1
    }
    return sum // sum will be 10
}

(btw.: this is already valid Kotlin, there is only a warning here. My goal is to have shadowing that does not need a nested scope to also just be a warning, because, imo, it’s an arbitrary limitation that limit’s expressiveness and readability in some cases, where the warning could be surpressed.)

This difference is really important. By making your variable var as a (bad) replacement for shadowing, you allow for such conditional mutations to happen. These can introduce bugs. Please don’t make me explain why constraining / avoiding mutability (so, in this case, local vars) is considered a good idea by many (I know, not everyone thinks unnecessary mutation is bad, but many do, I’m one of them. There have been whole paradigms built upon avoiding mutability (-> Haskell, Elm, Lisp, etc)).
Shadowing allows you to have the expressivity that var can sometimes allow for without the possibility of introducing new bugs because of mutation. It allows the compiler to still give you

  • more guarantees
  • possibly better optimization, because it can know more about how a variable (doesn’t) change and can thus do more inlining

Why warn if it’s a bad practice?

Multiple reasons:

  • This is definitely not one of these things where you can definitely say it’s good or bad practice. It’s heavily dependant on the situation and the conventions that establish (personally, within a Team / Company, or even within the whole community).
  • It can be dangerous, as you described. If you warn against it, that danger pretty much completely vanishes, further reducing the barrier of adoption for this proposal. If you can avoid the possible dangers, there is pretty much no argument against it, but as you’ve seen here (and also in the rust community) many arguments in favour of it.
  • Why not? A warning is just that: a warning. It’s not saying “This is bad, this will make your code break”, it’s saying “Are you sure this is something you want to do? There is a possiblity of you actually intending to do something else. Please verify!”. A warning that should never be ignored should be an error. A warning is meant to be a “hey, watch out, you’re doing something that could be dangerous if you don’t know what you’re doing”, not “NEVER DO THIS!”.
    Ever used Experimental APIs before? warning. Ever had to use an unchecked cast, but you were actually sure that it would be fine? warning. Warning, because it’s a place where the compiler needs to trust you to know what you’re doing.

OK, I see you point that shadowing can really be useful, and used as a better alternative for mutation. I really do think that mutation is bad, and if shadowing can be used to change the code in a way so there are more vals than vars, then it’s definitely worth considering.

I, however, completely disagree with the “Let’s make it a warning so people know it’s dangerous”. I wish I had a penny every time a developer ignores a warning without giving a second thought…

So! If we have shadowing at all, I’d rather have it as a keyword instead! True, there will be a rather ugly difference between the first and the remaining declarations of a variable, but certainly not uglier than a compiler warning.

If I were to choose a keyword, I’d settle with new. It’s not used in Kotlin for memory allocation anyway, but it’s invalid identifier in Java, so you’ll never have a name clash with an imported static function or something; it’s short to type; and it’s used in a similar way in C# (for method shadowing as opposed to overriding). So it would look like this:

val state = ...
new val state = ...

The only way I would like it, is if everywhere a keyword is added :

shad val state = ...
shad val state = ...

The reason is that when you would like at the code, you can see everywhere that you have to watch out for possible recoupling of the variable-name, which removes my biggest argument against this feature.

In that case I’d prefer it to be a keyword that replaces val and still has 3 character length. One possibility comes to my mind:

let state = ...
let state = ...

Like val it does not allow mutation. The difference to val would be that it allows shadowing.

1 Like

I’m not sure. What I really don’t want is to have a single shad var with no actual shadowing and no compiler warning. That would kill the whole point of “see the keyword — look out for shadowing”.

With the alternate keyword… Should a single let be allowed at all? Because I’m afraid people will start randomly use it as an alternative for var.

I think you could be right: there could be a rule in a style guide which tells to use let everywhere, as shadowing would then only change one line in a git commit instead of multiple.

However, I do think this is in the same line as changing a val into a var. Therefor it should be treated the same, so a compiler warning for a single let is indeed a good idea.

Furthermore, even if some companies say this, the remaining code where the normal val is used, is not weakened, so other code bases for sure remain safer…

(@stachenov I think you mean for val, for var would be great, wouldn’t it?)

Right. Of course “alternative for val”.

By the way, if this introduced into the language at all, I’d like it to work the same way for nested scopes as well. That is, disallow val/var shadowing, just like it’s disallowed in Java.

1 Like