[Feature Request] "pesudo-assign" operator

Proposal

Add a “pseudo-assign” operator <- (hopefully with a better name) .

The function would be:

operator fun pesudoAssign(value: <T>): Unit

It has the same structure as the plusAssign function.

This allows for adding behavior to “assignments” without changing the behavior of the = operator.

This is already possible by overloading an augmented assignment operator (e.g. +=) or even get or invoke, but using += for this behavior is ugly and ambiguous. I think pseudo-assignment would have enough use cases to be worth giving its own operator (given that there is enough uses to add inline classes, and these are extremely useful for them).

Inline classes could even be given a <- operator by default for their internal property.

Examples

data class Wrapper(var v: Int){

     operator fun pesudoAssign(value: Number){ v = value.toInt() }

     operator fun pesudoAssign(value: String){ v = value.toInt() }
}

data class Example2(var v: Int, val map: MutableMap<String, String>){

    operator fun pesudoAssign(value: Number){ v = value.toInt() }

    operator fun pesudoAssign(value: Map<String, String>){

        map.clear()

        map.putAll(value)

    }
}

// innerValue is a placeholder for however this would be done in the Int class
operator fun Double.pesudoAssign(value: Number){ innerValue = value.toDouble() } 

fun main(args: Array<String>){
    val wrapped = Wrapper(4)

    wrapped <- 3
    println(wrapped.value) // prints 3

    wrapped <- "90"
    println(wrapped.value) // prints 90

    val doub = 4.5
    doub <- 7
    println(doub) // prints 7.0

}

Use Cases

Wrapper types (especially with upcoming inline classes).

Pesudo-assign to different properties depending on type.

Use a single pesudo-assignment to set multiple properties (e.g. pesudo-assign a pair, assign the first value to one property, and the second to a different property).

Optional, explicitly defined (pseudo-)assignment compatibility (e.g. pseudo-assign a Double to an Int).

Additional behavior on pesudo-assignment (e.g. pesudo-assign an id, make a database request to get property values for that id).

In general, there seem to be two main use cases: assigning to an internal variable or allowing assignment compatibility with a different type.

Similarity to Implicit Casting

This is notably similar to implicit casting, which is (thankfully) not going to be part of Kotlin. There are a few notable differences that keep this from having the same problems:

  • The possibility for other behavior (like accessing properties, see the Wrapper example).
  • No actual casting occurs.
  • There much less ambiguity, no more than other operators.

Plus, pseudo-assignment is already achievable using existing operators, this would just give it its own operator.

Caveat: Object Creation

There are cases where you would want to pesudo-assign an entirely new object. For example, if you have a class that queries data for an id (such as an Exposed DAO class) and a ← operator that changes the id, it would be easier to return an entirely new object for the new id, that then would be assigned to the left-hand variable. However, I didn’t see an easy way to implement that (without having two very very similar operators), and there aren’t that many use cases (and they are all doable by changing properties, although it can get messy).

Please don’t turn Kotlin into Scala. Adding hundred of operators won’t make it better.

4 Likes

In my opinion it brings semantics from other languages, which is not necessary.
This <- is used in Go for channel operations, and there it’s also not very comfortable.

If it works like += it should be +=

@Beholder, I don’t disagree with you. The case I’m making is that this particular operator is useful enough and fills a distinct enough niche that it should be included. Kotlin’s support for wrappers (see inline classes) provides a use case that this operator is extremely useful in, to the point where it wouldn’t be out of place as a default method in inline classes.

@AlexeySoshin the example with += is to show that the operation can be done with += (because the method signature is the same), but the meaning is different. Using += for this would be like using /= to add an item to a list. Its possible, but unreadable and misleading.

I think a two way operator that would allow both get and set would be better.

I propose a no-argument operator get and set.

Basically, instead of writing:

wrapper.value = 42
println(wrapper.value)

write:

wrapper[] = 42
println(wrapper[])

I could get behind that, although I like the <- syntax better than [] = . However, the issue I see, and the reason I didn’t want to make <- two way, is that you can only have a [] get for one type, while the set can have multiple (although this already exists for get/set operators). It’s definitely worth considering.

What you are proposing can actually be done by using property delegates. Make the object its own delegate and you can override assignment and getting all you want.

1 Like

Interesting idea. It sounds like what the OP wants is to be able to override the as operator as can be done in Groovy.
Seems this might be doable in a future version of Kotlin in a couple of ways: extension functions with as an operator that can be overridden.

operator fun String.as<MyClass>() = MyClass(this)

I really don’t like the idea of overloading the as. This will just lead to code doing unexpected stuff.
If I see as I expect a simple cast, nothing else. Allowing casts to execute codes sounds like a terrible idea. Your example should be solved with a conversion method.

fun String.asMyClass() = ...
fun String.toMyClass() = ...

If possible you should also follow the convention set in Kotlin.
foo.toX() creates a new object out of the original, while foo.asX() creates a wrapper around the original so that changes are mirrored on both sides.

1 Like