Provide a "wither" function on data classes

Kotlin supports a lot of ways to write immutable data structures and will even provide real immutable collections in the future.

What I’m missing is a good way to “update” an immutable data class, like you can do in, for example, F# which has it as a keyword.

The idea

Lets say I have this data class:

data class Person(val name: String, val age: Int)

at the moment I have to write a boilerplate function for every data class to get the “wither” that I want:

fun Person.with(name: String? = null, age: Int? = null) = Person(name ?: this.name, age ?: this.age)

which allows me to use it like this:

fun main(args: Array<String>) {
    val tom = Person("Tom", 15)
    val max = tom.with(name = "Max")
    println(tom)
    println(max)
}

Output:

Person(name=Tom, age=15)
Person(name=Max, age=15)

Since it really is just a boilerplate function (it takes all properties as nullable optional arguments) it could be something compiler generated.

Problems

Having multiple constructors might be a problem, which I haven’t stumpled upon yet since I always write the function for the constructor with the most arguments.

However, generating a wither for every constructor works, as it is not ambigious:

data class Person(val name: String, val age: Int) {
    constructor(name: String) : this(name, 0)
    constructor(age: Int) : this("", age)
}

fun Person.with(name: String? = null, age: Int? = null) = Person(name ?: this.name, age ?: this.age)
fun Person.with(name: String? = null) = Person(name ?: this.name)
fun Person.with(age: Int? = null) = Person(age ?: this.age)

Alternative Syntax

An alternative could be to create a wither for every property:

fun main(args: Array<String>) {
    val tom = Person("Tom", 15)
    val max = tom.withName(name = "Max")
    println(tom)
    println(max)
}

data class Person(val name: String, val age: Int)

fun Person.withName(name: String) = Person(name, this.age)
fun Person.withAge(age: Int) = Person(this.name, age)

Output:

Person(name=Tom, age=15)
Person(name=Max, age=15)

@maggges why don’t you just use copy() method?

6 Likes

You’re right, thanks!

I’ve been writing my own implementations since weeks because I “intuitively” tried the stuff I knew from other programming languages :slight_smile:

2 Likes

It’s worth just reading the entire Kotlin website. It’s not that large and after you read the docs, you’ll know every feature. You can probably read most of it in the same amount of time it took you to write this post so it’s time well spent.

That’s how I started with Kotlin and most new technologies I get interested in: just read the user guide from back to front in one sitting.

4 Likes

I would be nice to support it for the Java code. It’s not possible to call copy method with default arguments from the java side. A solution would be some special annotation that generates those methods for the java code.