Leon K
02/14/2020, 1:36 PMlet foo: String = "4".to_string();
let foo: Option<i32> = foo.parse().ok(); // parse foo into the equivalent of Int?
let foo: i32 = foo.unwrap_or(1234); // equivalent of foo ?: 1234
i'd really love this for many reasons:
- it keeps you from having to come up with either unreadable or needlessly complex names (i.e. here: fooString
, fooMaybeInt
, foo
) or
- keeps you from alternatively having to combine everything into a giv method chain (which many times is preferable, in my example it would absolutely be preferable. but there are cases like more complicated in-between-steps where forcing everything into a single method-chain hurts readability badly (-> deep nesting, functions that take the value as an argument instead of as a receiver)
- it would even be great to make this good-practice for nullable-let-stuff (i.e.: foo?.let { foo -> doSomething(foo) }
is currently a warning, and using it
is not a good option when this turns into more complex nested chains or doSomething
is a longer call
some arguments against it i've considered:
# "just use var and mutate"
- No. I do not want to use var where i dont need to dynamically mutate a variable. mutation should only be used when it actually represents mutation, not just to make code more readable in cases like this. also this does not allow for type-changes like in my example
# "This hurts safety by allowing you to accidentally try to use a variable that has been shadowed "
- Maybe, but it would not be a big problem. Most of the time, you'd use this to actually change the types like in my example. this would mean that you will get compiler errors immediately nearly every time. This is still by far the biggest argument against it, as i could see it making things less obvious in some rare edge-cases. but it does not hurt your actual safety as this is NOT mutation, but name-shadowing.
# "This hurts readability, i dont like it"
- this is subjective, but imo, No. First of all, rust get's away with it very well, and it does help rust a lot. (but rust does also work with wrapped types like Option<T> and Result even more than kotlin, so it IS more necessary there)
- While shadowing variables can make for ambiguity on first sight, consider the ways this could help:
- reduce the amount of nested methods and indents by encouraging putting things in seperate lines (one of the biggest reasons people like using method chains is that they don't need to think of names for their intermediate variables)
- reduce the need for mutable variables. this is a big one. Using mutable vars where they are not strictly necessary does hurt your compile-time guarantees a lot more.
- another thing about mutables: sometimes you NEED mutable vars if you work with stuff in loops (most of the time it's better to replace these with map, filter, etc, but there are cases where a simple loop is better). now, after your loop, your variable is still mutable. with this feature, you could then do var foo: Int = 0; for(...){/*change foo*/}; val foo = foo;
and have it be immutable after the loop, again helping your compile-time guarantees.
# "What would happen with nested scopes? wouldn't this be mutation?"
- No. shadowing stays shadowing. if you shadow your variable inside a nested scope, this does not change the original variable.
are there any points i've missed or any big arguments against it?stantronic
02/14/2020, 2:49 PMLeon K
02/14/2020, 5:15 PMHanno
02/15/2020, 5:03 PMLeon K
02/15/2020, 5:27 PMcedric
02/16/2020, 7:27 AMval a = 3
run {
val a = "s"
println(a) // "s"
}
println(a) // 3
I was thinking maybe "erase" because each step really makes the previous value disappear, but "erase" obviously has some baggage too.Leon K
02/16/2020, 8:50 AMcedric
02/16/2020, 2:15 PMLeon K
02/16/2020, 2:26 PM