? KopyKat

When the author figures out how to upload the artifact to Maven, instructions on how to use the library will appear here.

One of the great features of Kotlin data classes is their copy method. But using it can become cumbersome very quickly, because you need to repeat the name of the field before and after.

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

val p1 = Person("Alex", 1)
val p2 = p1.copy(age = p1.age + 1)  // too many 'age'!

This plug-in generates a couple of new methods that make working with immutable data classes much easier.

Mutable copy

This new version of copy takes a block as parameter. Within that block mutability is simulated; the final assignment of each (mutable) variable becomes the value of the new copy.

val p1 = Person("Alex", 1)
val p2 = p1.copy { 
  age++
}

You can use old to access the previous (immutable) value, before any changes.

val p3 = p1.copy { 
  age++
  if (notTheirBirthday) {
    age = old.age  // get the previous value
  }
}

Mapping copyMap

Instead of new values, copyMap takes as arguments the transformations that ought to be applied to each argument.

val p1 = Person("Alex", 1)
val p2 = p1.copyMap(age = { it + 1 })

Note that you can use copyMap to simulate copy, by making the transformation return a constant value.

val p3 = p1.copyMap(age = { 10 })

Value class copy

Value-based classes are useful to create wrapper that separate different concepts, but without any overhead. A good example is wrapping an integer as an age:

value class Age(val age: Int)

The plug-in generates a copy method which takes the transformation to apply to the single field as parameter.

val a1 = Age(1)
val a2 = a1.copy { it + 1 }

Customizing the generation

You can disable the generation of some of these methods by passing options to KSP in your Gradle file. For example, the following block disables the generation of copyMap.

ksp {
  arg("mutableCopy", "true")
  arg("copyMap", "false")
  arg("valueCopy", "true")
}

By default, the three kinds of methods are generated.

What about optics?

Optics, like the ones provided by Arrow, are a much more powerful abstraction. Apart from changing fields, optics allow uniform access to collections, possubly-null values, and hierarchies of data classes. You can even define a single copy function which works for every type, instead of relying on generating an implementation for each data type.

KopyKat, on the other hand, aims to be just a tiny step further from Kotlin’s built-in copy. By re-using well-known idioms, the barrier to introducing this plug-in becomes much lower. Our goal is to make it easier to work with immutable data classes.

GitHub

View Github