Rust-style variable shadowing

After thought about it more, I strongly disagree.
(can’t remove like because it took to long…)

First, I don’t agree with var:
I see let as a midway between var and val:

var changes the access to a variableName for every code accessing the variableName.
let changes the access to the variableName for the code that comes after the reassignment.
val doesn’t allow the reassignment of the variableName.

Second, I disagree with disallowing: It would be a breaking change otherwise. Make it a compiler warning the most.

Third, because of the second, it would mean less.
Fourth. This would encourage to change every val into a let, inside a DSL. When you combine this fact with the fact that everything basically could become a DSL by using run/apply, this means every val should become let

Makes sense. (And who cares about likes anyway?)

But then we’d have the same inconsistency as we have now: for some inexplicable reason, it is allowed to shadow a variable in a nested scope, but not within the same scope. Only it’ll be worse: shadowing will be allowed in both cases now, but would require different keywords?! How does that make any sense? Which leads us back to the idea of simply allowing val to shadow an existing declaration. At least it’d be consistent. Still, I don’t like that idea very much either.

Any other thoughts?

Ah, naming, one of the 2 hard things in computer science.

How about this?

val unparsedFoo = "4".toString() // String
val parsedFoo = unparsedFoo.parse().ok() // Int?
val foo = parsedFoo ?: 1234 // Int

This allows you to use all 3 at the same time without confusion. And yes, it does require more typing, which some people seem not to like (except when they are on a forum :wink: ).

Lots of programming problems are easy to solve by a bit of discipline when naming things:

fun fetch(timeout: Int)
fun fetch(timeoutInSeconds: Int)
File createTempFile(String prefix, String suffix)
File createTempFile(String prefix, String optionalSuffix)
2 Likes

If you read the discussion, you would know the answer to your “solution”.
It is obviously the thing to do at the moment, but it’s not perfect.
“more typing” is not the main argument here, and it rarely should be.
The argument is “more reading” and “more thinking”.
While adding the type or “state” of a variable to the name makes it more explicit, it also makes it less readable. In this example, you already know what happened to your variable: it’s on the right side of the = sign. Adding “unparsed” and “parsed” to the variable name is redundant information.

Language design should mainly focus on two things, imo:

  • Safety and correctness:
    • make it hard to make mistakes
    • make it easy to avoid mistakes
  • Expressivity
    • make the difference between explaining what your code does and the code itself as small as possible
    • make the code as readable and as noise-free as possible
    • make it easy to write code that speaks the language of the domain

“Discipline when naming things” does not help this problem, as this problem is not “i’m to lazy to type” but “I want expressive code”

And have I wished for lots of that unreadable code during my career. Instead I got names like timeout and I had to go read the code (which usually means diving several classes deep), to figure out that the original developer meant optionalTimeoutInSeconds.

That unreadable code can save users of your API lots of time. You can avoid it in your internal code if you like, but I would appreciate it if you add all this “redundant” information to your API.

That is worse not better. Being able to remove variables from the current scope is a good thing that tells people that variable won’t be accessed anymore.

If it was easier to shadow variables maybe your timeout would have been a better type like a nullable duration. We should be using the type system to help us not reinventing hungarian notation.

Also sorry about the necroposting I guess I didn’t check the dates.

In Kotlin, of course. The example I gave was for Java code where the nullability cannot be specified in the type (and annotations are usually left out, and not quickly available during code completeion).

The work spent encoding type information into variable names is better spent encoding the information as the right type. The push should be to leverage the type system.

Edit: What I’m saying is that if any effort was going to be done to get that variable to be named something better I think that one of the first things you would look at is encoding it as a better type. If the idea is that the code is too old to change and you want to establish better naming practices for instance then I’m saying that instead the types/nullness annotations being used should be getting made better instead in this case.

That is a good goal. I think we should also push for infinitely fast computers with infinite memory :wink:

There is a trade-off between what the type system supports and how fast compilation is. IMO compiling is taking long enough. If a more advanced type system means even slower compilation times, no thanks.

I don’t think using a different type in a field or parameter or variable shadowing increases the type system complexity. I agree there is a trade off with compile time but I was under the impression that the worst offenders of this kind of thing are tricky rules being added to the language dealing with stuff like type inference. I guess I have a lot of question marks to pepper into my kotlin code.

Edit: I do also hate long compile times. My driving motivation in general with languages is to avoid C++ and anything related to it so long compile times is definitely something to watch out for. I can even probably agree with what you are saying in general about people who say to “leverage the type system”. But I think it depends on a case by case basis and I’m not sure if it applies with the specific things I’m meaning to talk about here. But on the other other hand I’m ignorant of the things that add to language compile times so idk.