Conditional assignment operator proposal

Checking whether the left side is not null is quite a use case. It would allow program flows like the following:

var x: Foo
x ?= foo?.let{
  // complex expression 1
}
x ?= bar?.let{
  // complex expression 2
}
x ?= baz?.let{
  // complex expression 3
}

Doing that with Elvis would be a very complex expression.

In this case I would like if the variable can be declared as a val. It is effectively final, assigned only once and never re-assigned.

@fatjoe79, what if all of the assignments give null
You’ ll need ensure not null via x!! at the end. Then yo kinda already have non-nullable type after this line via narrowing

This case looks kinda cool to me, but I’m not sure if having twin elvis assignments

a ?= b // for
if(a == null) a = b
//and
a =? b // for
if(b != null) a = b

would not make it too perl’ish

Or even third option to have both

a ?=? b // for
if(a == null && b != null) a = b

:slight_smile:

1 Like

It will need to look like this then:

var x: Foo
x ?= foo?.let{
  // complex expression 1
}
x ?= bar?.let{
  // complex expression 2
}
x ?= baz?.let{
  // complex expression 3
}
x ?= defaultValue

ok, I think this way compiler could ensure non-nullability. but in this case it’ll have to look forward, not sure if this possible.
I think having var x: Foo? here is good enough since x will be narrowed after last conditional assignment anyway

is this a duplicate of: Nullable assigner?

if so, some reasons to include:

Seems similar
Looks like in groovy they made what you proposed
I also wanted to have a simpler syntax for something like this

val b = getValueOrNull() // map["key"] for example
if(b != null) a = b

@tieskedh, didn’t you create a KEEP or task for this feature?

Nope, at the moment of creation, I didn’t get a lot of likes, so I didn’t continue the topic.
The biggest part of the conversation was about if you needed to write everything as a one-liner :wink:

Maybe I didnt catch the idea, but any conditional assignment could be done using the when-syntax, e.g.:

val str: String? = "c"
   val n: Int =
	when(str){
		null -> 0
		"a"  -> 1
		"b"  -> 2
		else -> -1
	} 

-T

WDYT about this three elvises ?=, =? and ?=? ?
One of them would be familiar to groovy coders (the one that checks receiver), another one checking the message, and last one well for symmetry

Now I see we had different use-cases: I wanted to have “set if not null and get always”.

I’m interested in a use-case where you would like to use ?= , but not ?=?

They are for one-liners, to save one null check. Kinda like what elvis does
In my project I have a lot of untyped input data which needs to be assigned to typed DTOs before any further usage.

My final proposal for now is to make 3 new operators (one of which will be borrowed from groovy 3):

a ?= b // instead of
if(a == null) a = b

a =? b // instead of
if(b != null) a = b

a ?=? b // instead of
if(a == null && b != null) a = b

question mark here is on the side which is to be checked for null

Yeah, on second thought it kinda looks useless by itself. The only difference here is whether a setter should be called when null passed to a null receiver
so then let it be

a ?= b // instead of
if(a == null && b != null) a = b

a =? b // instead of
if(b != null) a = b

The problem at this point is that the syntax doesn’t do what it seems to do

in case of ?= it will do the same as in groovy, assign default value, except assigning null as a default which is pointless
in case of =? its just ‘assign if there is something to assign’
and “there will be a null check on the side where ‘?’ is”
dunno, seems ok to me

Isn’t this equivalent of:

It seams even more clean and readable this way. Also type can be calculated automatically (nullable or not).

Yes, that’s what I would code today.

Cool? Yes. It is amazing what can be done in a single expression in kotlin.

But I disagree that it is more clean. It can get out of control quickly.

Is it? Are we talking about the same code samples here?
If I understand you correctly you said that

// from fatjoe79
var x: Foo    // I guess that would need to be Foo? = someInitlialValue
x ?= foo?.let{
  // complex expression 1
}
x ?= bar?.let{
  // complex expression 2
}
x ?= baz?.let{
  // complex expression 3
}

is the same as

// from cabman
val x = foo?.let {
    // complex expression 1
} ?: bar?.let {
    // complex expression 2
} ?: baz?.let {
    // complex expression 3
}

But based on my understanding the code @fatjoe79 posted translates to

var x: Foo? = someValue
if(x == null) x = foo?.let{ ... }
if(x == null) x = bar?.let{ ... }
if(x == null) x = baz?.let{ ... }

So @cabman’s code would need to be

val x = x ?: foo?.let {...} 
//etc

Which is still not the same exactly as the setter is called even if x is not null, which might in some cases have side effects.

… as your solution.

I think this syntax sugar is unnecessary and will make language come complicated then needed.

I recall the idea was to make val x, so there is no setter AT ALL.

I didn’t suggest val x = x ?: foo?.let {...} in which case the getter is called unlike original code.

If you want var x then setter will be called anyway as the initial value must be assigned. @Wasabi375, your code would be converted into:

var x = someValue ?: foo?.let{...} ?: bar?.let{...} ?: baz?.let{...}

In both case there is only 1 assignment will be executed (except in your code and not one from @fatjoe79) the original value.

Still don’t see advantage of these extra sugar.