Destructuring in when


#1

Currently it’s not possible to apply destructuring to branch conditions of when expression.

Suppose we have res object containing String and Int properties. Is it possible to modify when in the following way

when(obj) {
    ("a", 10) -> ...
    ("c", 13) -> ...
    else -> ...
}

instead of existing workarounds like that? (When we need extra covering with data class objects, etc.)

when(obj) {
    Pair("a", 10) -> ...
    Pair("c", 13) -> ...
    else -> ...
}

Saying about Kotlin 1.1 features, usage of underscores for unused parameters can be useful - as well as short and clear - if we’d like to check what branch to execute by partial set of object’s properties.

when(obj) {
    (true, "a", _) -> ...
    (false, _, 42) -> ...
    (false, "b", 30) -> ...
    else -> ...
}

Any thoughts about that?


#2

You basically want when to do the full blown pattern matching that Scala offers, but that is one of the features that JB decided was too complex (and probably really hard to implement).

While this will technically work it is not very efficient as it creates throw away objects for comparing.

I certainly agree that there are some definitely needed improvements to when, I don’t think we want full blown pattern matching.


#3

Do you happen to have a link to this? I’d be interested in reading more about their decision.


#4

Nothing specific. Think I heard it in one or more talks on Kotlin that implicits and pattern matching were deemed complex and they avoided them.


#5

Pattern matching is one the best things about Scala, and it’s something that’s easy to understand while being extremely useful. Sometimes I think people think all features of Scala are over complex functional programming idioms. While there are features that maybe fit into that category, pattern matching is not one of them. When I re-wrote a Scala library in Kotlin, the lack of pattern matching was the biggest annoyance to me.


#6

What are the key differences between pattern matching and the when expression?


#7

In pattern matching you can a) match against values inside a structure (hence the term) and b) extract values from the matched expression.

So if we had a data class called Person, we could do

when(person) {
  Person("sam", age) -> println("This matches all sams and makes $age available as a parameter")
  Person(name, _) -> println("This matches all persons, and makes $name a parameter and ignores the age")
}

and various combinations of that kind of thing.

In Scala if you use a literal, then it will be matched against that value, if you use a variable name it will extract the value and assign it to the variable, and if you use a variable name inside backticks it will match against the value of an already defined variable (but that variable must be ‘constant’).


#8

I’ve been using Kotlin and Rust side by side for a while now, and have really missed proper pattern matching from Rust, while missing proper IDE support from Kotlin :slight_smile:


#9

To not create temprorary objects, I propose following syntax:

when (obj) {
    is Pair<*, *> -> when((k, v) *obj) {
        (1, "5") -> ... //  k, v available here
        ("asdfa", true) -> ...
        (variable, _) -> ...
    }
}

if ((k, v) *obj == ("asdfa", 42) {
    // k, v available here
}

and would be nice to see matched value as it

when (obj) {
    is Pair<*, *> -> when((k, v) *obj) {
        (1, "5") -> it::run { println("[$key=$value]") }
    }
}

with => arrow to make it available as this

(1, "adfa") => println("[$key=$value]")

#10

Some very preliminary (and interesting) discussion from Brian Goetz on pattern matching in Java.

http://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html


#11

I don think that full blown pattern matching would be absolutely necessary. When can already do a lot of powerful things. What I’d like to see especially is something like this

when(thing) {
    is Pair(a, b) -> { }
    is OtherDataClass(a, _, c) -> {}
}

In that case, all that’d need to do is desugar it to a traditional data class destructuring.

when(thing) {
    is Pair -> {
        val (a, b) = thing
    }

    is OtherDataClass -> {
        val (a, _, c) = thing
    }
}

Would make that sort of code look much nicer, imo.


#12

Hi,
Another useful usage would be the collection deconstruction:

 when (coll) {
                (first, second, third) -> // do smth with the first 3 elements
                else -> // do something else 
    }

The above first case would correspond to having a collection with at least 3 elements.
In scala ::Nil helps to restrict the case to exactly 3 elements


#13

I really miss this feature from Haskell & Scala. I hate introducing local vals just for the purpose of referring to them in when branches.