Conditional assignment operator proposal


#1

Hi
I don’t really like constructions like this:

(record["speed"] as Int?)?.let { speed = it }

Maybe it would be better to make something like this work:

speed ?= record["speed"] as Int?

I think ruby have something similar already in the form of:

a ||= b

#2

Upd: I was wrong about ruby. It checks left side and if it is not null then assigns


#3

I don’t even understand what your Kotlin example should be doing and I don’t think this will actually run. Unless you mean something like this

var speed = someValue
(record["speed"] as Int?)?.let { speed = it }

Why not just

speed = (record["speed"] as Int?) ?: speed

#4

Maybe the cast in your example isn’t really great, but I agree with your issue. It is ugly although it works. Your syntax is an interesting approach.


#5

Yeah, I should define what speed is. It is some property in the current scope. I’m filling an object with incoming data in the form of a map


#6

I like the idea of speed ?= someValue assigning only if not null. I just was confused by the cast :wink:


#7

EDIT:

I think I misunderstood.

The correct alternative is something like

if (someValue != null) speed = someValue

or

speed = someValue ?: speed

Just like my original reply, I think most people would assume speed ?= someVlue to perform just like the Ruby a ||= b (if left side is null, assign some value).


ORIGINAL:

I think this would have limited gain.

Something like

speed ?= someValue

is only short for

if (speed == null) speed = someValue

or

speed = speed ?: someValue

IMO, although speed ?= someValue is slightly shorter than the current options, the current options work fine as they are now.

All of these options assume we’re working with non-final fields. Maybe I’m missing some use case where you have a ton of null properties that need to be set to default values.


#8

Its like

val maybeSpeed: Int? = record["speed"] as Int?
if(maybeSpeed != null) speed = maybeSpeed

I was wrong comparing it to ruby. Ruby’s a ||= b is more like speed = speed ?: defaultValue
But what I want is compare right side expression to null, and only if its not null then assign.
And yes I have a lot of code like that. Its mostly untyped data transfer maps in which most keys can absent


#9

The meaning is to assign only if not null (maybe it would work on either side - aka a field reference on a null object). I can see how ?= may be confusing though. I like the concept, I’m not sure on the syntax.


#10

What does the declaration of speed look like. Is a default value assigned to is if maybeSpeed is null?


#11

I think no, the value should stay the same, and not reassigned or reset to default when right-hand side is null.
speed is just a var property, in this particular case it is delegated to some ORM-like storage like var speed: Int? by Storage.attribute<Int?>("columnName") but it doesn’t actually matter


#12

So speed is a nullable var.

Normally you would use one of the proposed solutions above (using ?:) to provide a default value, but in your case you want to keep the current value unless the new value is not null.

The version with let is the shortest way to write what you need at this moment. If you find it hard to read, you could write a function yourself. But this will make the code a bit longer:

fun <T: Any> assignIfNotNull(value: T?, block: (T) -> Unit) {
    if (value != null) {
        block(value)
    }
}

fun main(args: Array<String>) {
    val record = mapOf("speed2" to 2)
    var speed: Int? = 1
    assignIfNotNull(record["speed"] as Int?, { speed = it })
    println(speed)
    assignIfNotNull(record["speed2"] as Int?, { speed = it })
    println(speed)
}

A shorter syntax would be nice, but I agree that ?= is not appropriate, because the question mark currently always evaluates the expression before it. Maybe reverse it: =?


#13

I think you are right, =? is better


#14

What about something like this:

import kotlin.reflect.KMutableProperty0

fun <T> KMutableProperty0<T>.setIfNotNull(value: T?) {
    if (value != null) this.set(value)
}

var speed = 1
fun main(args: Array<String>) {
    //sampleStart
    println(speed) // speed is 1

    var maybeSpeed: Int? = null
    ::speed.setIfNotNull(maybeSpeed)

    println(speed) // speed is 1

    maybeSpeed = 2
    ::speed.setIfNotNull(maybeSpeed)

    println(speed) // speed is 2
    //sampleEnd
}

#15

@arocnies, reflection is too costly.
I think, let function is enough for most cases.


#16

I know reflection can be costly at times, but in this case I wonder how much of an impact it would be on the application. It runs fine in JavaScript as well.

I’d love to see some performance metrics to show if this use case really is costly.


#17

Not expressing an opinion on the proposal, but that is subtly different in that it always sets speed. For standard setters, it doesn’t matter but with custom setters it could be a big difference.


#18

point taken. Did not think of that.