Destructuring in when

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?

14 Likes

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.

1 Like

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

2 Likes

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.

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.

8 Likes

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

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’).

4 Likes

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:

3 Likes

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]")

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

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

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.

5 Likes

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

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

9 Likes

Having something like but not really pattern matching feels so off to me, especially in the presence of sealed and data classes. I couldn’t believe it and kept searching for what I was doing wrong. Finding out that it’s just not there in Kotlin was a big disappointment; the advantages of pattern matching in terms of explicit brevity are so big that its lack is noticeable.

I keep comparing Kotlin to Swift, and Swift has pattern matching for enums with associated values (which are really ADTs). The Scala approach to implementing it on top of the JVM seems prudent to me.

I’d appreciate some clarification about what was deemed “too complex”. The implementation in the compiler? The language feature for programmers?

I shifted some things around in my head and in a scratch, and I think pattern matching could be an additive, mostly-sugar extension. That is, programmers wouldn’t have to deal with it unless they want to, and huge parts of the implementation beyond desugarisation might be covered by the existing compiler. I’m sure I’m missing things, but I don’t understand why this is apparently not a discussion worth having.

4 Likes

I share exactly the same thoughts, and I’m also coming from a Swift background.
If programming complexity is an issue, Swift being open source, compiler programmers could take a glance at what was done in Swift :smile:

I often talk of Kotlin with a Scala developper and the lack of pattern matching is a major issue for him.

He is probably not the only scala dev in that case.

Here is the JEP for pattern matching in Java
JEP 305: Pattern Matching for instanceof (Preview)

If Java is proposing to add it, then it’s got to at least be on the radar for Kotlin, lest Kotlin be left behind.

1 Like

It will take many years till it is fully introduced in Java. They plan to introduce it in small steps. The first one being switch expressions.

Of course, it will take a long time. Point is, its on the roadmap.

1 Like

Yes, please, I miss pattern matching. +1 to what sksamuel and marikka said above.

IMO, destructuring is one of those “Aha!” moments in programming that is hard to do without once you’ve gotten used to it. And several other languages do have programmers getting used to it. So true matching would be awesome, but even if it were only allowed to go one level deep, binding names to parts would really improve the ergonomics of when usage.

4 Likes