New conditional operator: a equals b and/or c

Instead of this:

if (a == b || a == c)

do this:

if (a == b | c)

also works with ‘and’. instead of this:

if (a == b && a == c)

do this:

if (a == b & c)

More examples:

(a != b || a != c) -> (a != b | c)
(a != b && a != c) -> (a != b & c)

(a == b && a == c && a == d) -> (a != b & c & d)
(a == x || b == x || a == y || b == y) -> (a | b == x | y)

(a == b && a != c) -> (a == b !& c)
(a != b || a == c) -> (a != b !| c)

If you need the || case a lot (which I think few of us do), you can already do:

if (a in setOf(b, c))

(Preferably storing that set in a field somewhere to avoid creating a new copy each time. And probably being more efficient than multiple equality checks, especially if the set is large.)

Or:

when (a) {
    b, c ->
}

The && case is harder (e.g. if (setOf(b, c).all{ a == it })) – but I’m struggling to think of any realistic situation where you’d want to do that…

Neither seem to justify new syntax. Especially as the syntax gets ambiguous pretty quickly. (For example, if c is Boolean, then that syntax already has a valid meaning, which is incompatible.)

And your more complex cases just look confusing to me! I’d hate to have to read code written that way. (But that wouldn’t be likely, seeing how rarely that sort of situation occurs.)

What makes Kotlin such a great language isn’t the number of operators, nor the complexity of its syntax. Quite the reverse: it’s the simplicity and regularity. Every feature, every keyword, every syntactic element has been carefully chosen to give the most benefit for the least complexity. Even if Kotlin were young enough to be considering lots of new syntax (which it’s not), IMO these operators simply don’t give enough benefit often enough to be justified.

8 Likes

You can pretty much do it yourself with infix functions.

Take a look at this simple example:

open class ValueSet<T> : TreeSet<T>() {

    fun add(v1: T, v2: T) {
        add(v1)
        add(v2)
    }

    fun add(v1: ValueSet<T>, v2: T) {
        addAll(v1)
        add(v2)
    }

}

class AndSet<T> : ValueSet<T>()
class OrSet<T> : ValueSet<T>()

infix fun <T> T.equals(list: ValueSet<T>): Boolean {
    if (list is OrSet<*>) return this in list
    if (list is AndSet<*>) return list.find { it != this } == null
    error("Unsupported set type: ${list::class.simpleName}")
}

infix fun <T> T.or(second: T) = OrSet<T>().also { it.add(this, second) }
infix fun <T> OrSet<T>.or(second: T) = OrSet<T>().also { it.add(this, second) }

infix fun <T> T.and(second: T) = AndSet<T>().also { it.add(this, second) }
infix fun <T> AndSet<T>.and(second: T) = AndSet<T>().also { it.add(this, second) }

fun main() {

    val test1 = "a"
    val test2 = "b"
    val test3 = "c"
    val test4 = "d"

    println(test1 equals ("b" or "c" or "d")) // Prints: false
    println(test2 equals ("b" or "c" or "d")) // Prints: true
    println(test3 equals ("b" or "c" or "d")) // Prints: true
    println(test4 equals ("b" or "c" or "d")) // Prints: true

    val test5 = "a"
    val test6 = "a"
    val test7 = "a"
    val test8 = "b"

    println("a" equals (test5 and test6 and test7)) // Prints: true
    println("a" equals (test5 and test6 and test8)) // Prints: false

}

It can be polished and made much better; this is just a quick idea. Maybe, you can introduce operator overloading for even shorter syntax and a negation with ! operator.

3 Likes

I don’t really see a need for this as when is usually a better alternative, but syntax-wise this is not a good choice and I’m sure would lead to ambiguities. You would need a slight variation on the syntax like using another character in the operator to distinguish it from the regular operator

if (a == b |= c)