Proposal: Conditional assignment _This is part of ...
# language-proposals
r
Proposal: Conditional assignment This is part of my quest to get
?
on more and more operators
If a property is not null, it would be nice to have a conditional assignment operator that would assign a nullable value only if it is not null. For example:
Copy code
map[KEY]?.let { value = it }
would become
Copy code
value ?= map[KEY]
Edit As @Dico pointed out,
value ?= map[KEY]
can be a bit confusing, as it seems to imply
value
is the nullable item instead of
=
. It may make more sense to use
value =? map[KEY]
, but I'm on the fence.
d
This places the question mark on the property, not on the expression.
Question marks are always trailing tokens, using them in a leading position would be confusing.
The confusion is not worth it, this change does not have much value at all because, as you demonstrate,
a ?.let { }
works the same
j
"works the same" is a poor argument against something because you can literally say that about every form of desugaring until you're staring at a page of x86 assembly
d
That's a bit extreme, but point taken
Interested to hear about other places where you could add ? though
j
i would add it to
return
Copy code
return? returnsNullable()
someSideEffect()
return? anotherNullableReturn()
otherSideEffect()
return whatever
๐Ÿ‘ 1
i hate reading
returnsNullable()?.let { return it }
โž• 4
d
And other keywords like
throw
?
r
My main two places I want it are on
get
and
invoke
operators, e.g.
Copy code
nullableMap?[KEY]
nullableLambda?()
d
Maybe
=?
would be a bit better
r
Why would that be better? it already comes before
.
in
?.
and
:
in
?:
.
d
I would like to be able to overload how
?
works on non-nullable values. For example in a project where I make heavy use of inline classes because the code would otherwise run extremely slowly.
Anyways getting off topic
r
I'm not a fan of that idea. I like the fact that anytime you see a
?
in Kotlin, it's for dealing with nullable values.
๐Ÿ‘Œ 2
d
It could be implemented in a way where there is a special value that isn't necessarily
null
. In other words, there would be heavy restrictions. As for your question, because it places
?
in a more trailing position, and it's more consistent with
return?
that Jake proposed.
?=
could be applied like: myVar?=nullableValue Which is confusing because it looks like ? is applied on the property more than with =? I would say.
j
How do you reconcile that argument with
myVar?.foo()
?
d
In
myVar?.foo()
the subject of the question mark is
myVar
. Not the . following it.
The condition is dependent upon
myVar
j
Got it. Makes sense.
d
I want to sleep, good talks, good night ๐Ÿ˜„
r
@Dico I see where you're coming from, and I could go either way. I just arbitrarily chose
?=
as I'm used to seeing
?
immediately before an operator.
๐Ÿ‘๐Ÿป 1
@jw That
return?
is interesting. I like it more than rust (where
?
creates an implicit return anywhere it pops up; I'm not a fan of implicit returns)
j
i mean, it's not really implicit since you denote it with
?
, right? also it's for the Result type and error propagation which is slightly different
r
For the result type, sure, I can see the argument, but it's also for the Option type.
d
The result type is an excellent example of when you may want to define behaviour of ? On non nullable type
Something like
operator fun Result<*>.isSpecialNullValue(): Boolean
Back to sleep (I cant sleep)
r
Adding the
?
operator onto results seems useless to me without some equivalent of Rusts
Into
trait, and Kotlin is (usually) designed against implicit conversion.
j
I've never used it with an Option that I can remember. I didn't know it applied to them. What I'm asking for is actually the opposite though. So maybe it's
return!
d
result??
I'm not too familiar with Rust
r
No, I think
return?
makes sense. I read that as "return if not null, else continue" as opposed to
return!
which I read as "return if not not null, else throw".
j
Don't learn it. It'll show you all the things about Kotlin that are annoying
๐Ÿ˜‚ 3
d
I think I know quite well given kotlin has been my go to language for everything for 2+ years
Upvote my keep proposal <shameless plug>
j
return someValueThatCouldBeNull ?: skip
and
x = someValueThatCouldBeNull ?: skip
, perhaps? Where
skip
is like
break
and
continue
and
return
in that it interrupts execution and skips to the end of the statement?
j
i don't like that the behavior is moved really far away from the
return
keyword. It would make reading
Copy code
return reallyReallyReallyReallyReallyReallyReallyReallyReallyLongLine() ?: skip
println("Hi!")
hard to understand when scanning vertically
j
I had the same thought - with returns I'd be less worried because it's fairly clear that if the function keeps going that the return could be skipped, but with assignments pushing that information to the back of the line seems problematic
In any case, it doesn't quite do what it needs to for readability.
j
Itโ€™s good that itโ€™s ugly to deal with nulls! We could all do with fewer nulls in our lives . . .
j
Instead we can allocate wasteful boxes on the heap and chase pointers for useless abstractions of optionality?
l
As I am thinking about Jake's example with returns, then with the current possibilities it maybe could be done like this as well:
Copy code
return sequence {
    yield(returnsNullable())
    someSideEffect()
    yield(anotherNullableReturn())
    otherSideEffect()
}.filterNotNull().firstOrNull() ?: whatever
This is a very raw sketch, so maybe it could be wrapped into something more simpler and meaningful ๐Ÿ™‚
Or another possibility could be to force the side-effect functions to always return null. Then it could be all chained by the Elvis operator:
Copy code
fun nullResult(action: () -> Any) = null.apply { action() }

fun returnsNullable() = null
fun anotherNullableReturn() = null
fun someSideEffect() = 1
fun otherSideEffect() = 1

fun main(): Any {
	val whatever = 1
    return returnsNullable() ?:
        nullResult { someSideEffect() } ?: 
        anotherNullableReturn() ?:
        nullResult { otherSideEffect() } ?:
        whatever
}
d
That's utterly unreadable.
l
Alright, I agree ๐Ÿ˜ , another option would be wrapping the side-effect function and successive function call into a lambda, which could look like:
Copy code
return returnsNullable() ?: run { 
            someSideEffect() 
            anotherNullableReturn()
        } ?: run { 
            otherSideEffect() 
            whatever
    	}
But I cannot think of any better version at the moment ๐Ÿ˜
d
I use this for conditional return value ?: { return error } or value ?.let{ return it }