How to get rid of this boilerplate code in this sealed class hierarchy? - kotlin

Suppose I've got a sealed class hierarchy like that:
sealed class A {
abstract val x: Int
abstract fun copyX(x1: Int): A
}
data class A1(override val x: Int, val s1: String) : A() {
override fun copyX(x1: Int): A {
return this.copy(x = x1)
}
}
data class A2(override val x: Int, val s2: String) : A() {
override fun copyX(x1: Int): A {
return this.copy(x = x1)
}
}
All the data classes have field x and should provide method copyX(x1: Int) to copy all the fields but x and override x with x1. For instance,
fun foo(a: A): A { a.copyX(100) }
The definitions above probably work but the repeating copyX across all the data classes seem very clumsy. How would you suggest get rid of this repeated copyX ?

First, you can implement copyX as an extension (or even A's member) so as to concentrate the code in one place and avoid at least duplicating the copyX function in the sealed class subtypes:
sealed class A {
abstract val x: Int
}
fun A.copyX(x1: Int): A = when (this) {
is A1 -> copy(x = x1)
is A2 -> copy(x = x1)
}
data class A1(override val x: Int, val s1: String) : A()
data class A2(override val x: Int, val s2: String) : A()
If you have a lot of sealed subtypes and all of them are data classes or have a copy function, you could also copy them generically with reflection. For that, you would need to get the primaryConstructor or the function named copy from the KClass, then fill the arguments for the call, finding the x parameter by name and putting the x1 value for it, and putting the values obtained from component1(), component2() etc. calls or leaving the default values for the other parameters. It would look like this:
fun A.copyX(x1: Int): A {
val copyFunction = this::class.memberFunctions.single { it.name == "copy" }
val args = mapOf(
copyFunction.instanceParameter!! to this,
copyFunction.parameters.single { it.name == "x" } to x1
)
return copyFunction.callBy(args) as A
}
This works because callBy allows omitting the optional arguments.
Note that it requires a dependency on kotlin-reflect and works only with Kotlin/JVM. Also, reflection has some performance overhead, so it's not suitable for performance-critical code. You could optimize this by using the Java reflection (this::class.java, getMethod(...)) instead (which would be more verbose) and caching the reflection entities.

Related

Kotlin idiomatic interface method cache WITHOUT spring's cache?

Is there any way to intercept method from interface and cache result “WITHOUT” spring’s method cache ?
Take a simple calculator for example :
interface ICalculate {
fun multiply(a: Int, b: Int): Int
}
It just multiplies a and b , and return the result . Suppose it is a heavy computation work . And there are two implementations :
class CalculatorDumb : ICalculate {
override fun multiply(a: Int, b: Int): Int {
var sum = 0
(1..a).forEach {
(1..b).forEach {
sum++
}
}
return sum
}
}
The dumb implementation just add one by one .
And there is a smart implementation :
class CalculatorSmart : ICalculate {
override fun multiply(a: Int, b: Int): Int {
return a * b
}
}
This smart implementation just returns a * b .
OK , here is the point . I hope client can initialize no matter dumb or smart implementation , and can get result if the parameter is identical.
There is a Memoize pattern , described here : https://jorgecastillo.dev/kotlin-purity-and-function-memoization :
class Memoize<in T, out R>(val f: (T) -> R) : (T) -> R {
private val values = mutableMapOf<T, R>()
override fun invoke(x: T): R {
return values.getOrPut(x) { f(x) }
}
}
fun <T, R> ((T) -> R).memoize(): (T) -> R = Memoize(this)
I can use it in the implementation class , like this :
class CalculatorSmart : ICalculate {
data class CacheKey(val a: Int, val b: Int)
private val innerCalculate: (CacheKey) -> Int = { key: CacheKey ->
println("cache miss")
key.a * key.b
}.memoize()
override fun multiply(a: Int, b: Int): Int {
return innerCalculate(CacheKey(a, b))
}
}
But it seems it’s hard to apply it in the interface layer.
I wonder if there are any patterns to achieve :
Each implementation class ( dumb or smart in this example) doesn’t need to implement its own cache .
There are NO two public versions of method defined in interface ( for example : actual multiply() and cachedMultiply() while cachedMultiply checks cache and redirect to multiply if necessary )
Client only knows one method of the interface , No matter the client initialize smart or dumb class , the result of the same parameter will be cached and returned.
For example : such scenario is OK
val calSmart: ICalculate = CalculatorSmart()
println(calSmart.multiply(3, 7)) // cache miss
println(calSmart.multiply(3, 7)) // cache hit
val calDumb: ICalculate = CalculatorDumb()
println(calDumb.multiply(3, 7)) // cache miss
println(calDumb.multiply(3, 7)) // cache hit
It will work like Spring’s method cache . but I hope there will be a kotlin-idiomatic style , maybe more functional , just like the memoization pattern above .
Is there any idea ?
Thanks.
I don't believe there is anything in the language to support that at the interface level, but I see 2 "obvious" alternatives:
an abstract class doing the caching, implementations would just extend it and implement particular protected methods
a decorator class to wrap any implementation to add caching (so implementors don't need to know about caching at all, but clients opt-in by instantiating a wrapper)
Using an abstract class
The interface is about defining the function itself, while caching is an implementation detail. If you want to "share" this implementation detail across implementations, it seems to me the most appropriate would be to have an abstract class implementing this interface and providing an abstract method for the actual computation in subclasses:
interface ICalculate {
fun multiply(a: Int, b: Int): Int
}
abstract class CachedCalculator {
override fun multiply(a: Int, b: Int): Int = computeMultiply().memoize()
protected abstract fun computeMultiply(a: Int, b: Int): Int
}
Now subclasses extending CachedCalculator would benefit from caching:
class CalculatorSmart : CachedCalculator() {
override fun computeMultiply(a: Int, b: Int): Int {
return a * b
}
}
Using a decorator
class CachedCalculator(private val calculator: ICalculate): ICalculate {
override fun multiply(a: Int, b: Int): Int = calculator.multiply().memoize()
}
Then on client side:
val calSmart: ICalculate = CachedCalculator(CalculatorSmart())
println(calSmart.multiply(3, 7)) // cache miss
println(calSmart.multiply(3, 7)) // cache hit

How to call function from generic function without knowing the type?

Is there a way to do the following?:
class Someotherthing
class SomeotherthingDTO
class Something
class SomethingDTO
fun convert(entity: Someotherthing): SomeotherthingDTO = SomeotherthingDTO()
fun convert(entity: Something): SomethingDTO = SomethingDTO()
fun <T, D> generic(entity: T): D {
// TODO: check here if there is convert() that accepts type T?! somehow? reflection? reification? or it will be possible only in the future by using typeclasses (KEEP-87)?
return convert(entity)
}
fun main() {
val x: SomethingDTO = convert(Something())
println(x.toString())
}
Currently, the result is: none of the following can be called with the arguments supplied...
You'll need multiple receviers for this to work (KEEP-87). With those you'll be able to "find" the receivers properly.
Until then what I usually do is to put Converters in a ConverterRegistry to do the conversion like this:
interface Converter<A, B> {
val fromClass: KClass<A>
val toClass: KClass<B>
fun convert(from: A): B
fun convertBack(from: B): A
}
interface ConverterRegistry {
fun <A, B> tryConvert(from: KClass<A>, to: KClass<B>): B?
}

Kotlin generics with in produces Type mismatch when compiling

I´m working on a code with generics and when I use an in I got a TypeMismatch when compiling.
The code is the following:
open class A
class B:A()
data class DataContainer(val a:String,
val b:A)
interface Repo<T:A>{
fun setParam(param:T)
fun getParam():T
}
abstract class RepoImp<T:A>:Repo<T>{
private lateinit var parameter:T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class BRepo:RepoImp<B>()
class Repo2(val repo: Repo<in A>){
fun process(b:DataContainer){
repo.setParam(b.b)
}
}
val repoB = BRepo()
val repo2 = Repo2(repoB)// Here I got: Type mismatch: inferred type is BRepo but Repo<in A> was expected
I also tried changing the attribute repo from Repo2 to Repo<*>
Since BRepo is a Repo<B>, it is not a Repo<in A>, (but it would satisfy Repo<out A>).
In other words, a Repo<in A> must be able to accept setParam(A()), but BRepo.setParam() can only accept a B or subclass of B.
Or to put it another way, BRepo is a Repo<B>, which is a tighter restriction on the type than Repo<A> when it comes to writing values (but looser restriction when reading values).
The reason class Repo2(val repo: Repo<*>) doesn't work is that Repo<*> is essentially a Repo<in Nothing/out A>. You can't call setParam() on a Repo<*> with any kind of object.
There's a design flaw in your code that you can't fix simply by changing Repo2's constructor signature. As it stands now, Repo2 needs to be able write A's to the object you pass to it, and a BRepo by definition does not support writing A's, only B's. You will need to make at least one of your class's definitions more flexible about types.
It might be easier to understand the covariance limitation with more common classes:
val stringList: MutableList<String> = ArrayList()
var anyList: MutableList<in Any> = ArrayList()
anyList.add(5) // ok
anyList = stringList // Compiler error.
// You wouldn't be able to call add(5) on an ArrayList<String>
Basically MutableList<String> is not a MutableList<in Any> the same way Repo<B> is not a Repo<in A>.
The Repo2 class expect to consume only type A, use Repo2<T : A>(val repo: Repo<in T>)
open class A
class B : A()
class C : A()
class D : A()
class BRepo : RepoImp<B>()
class CRepo : RepoImp<C>()
class DRepo : RepoImp<D>()
interface Repo<T : A> {
fun setParam(param: T)
fun getParam(): T
}
abstract class RepoImp<T : A> : Repo<T> {
private lateinit var parameter: T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class Repo2<T : A>(val repo: Repo<in T>) {
fun process(b: DataContainer<T>) {
repo.setParam(b.b)
}
}
data class DataContainer<T : A>(
val a: String,
val b: T
)
fun main() {
val repoB = BRepo()
val repoC = CRepo()
val repoD = DRepo()
val repo2 = Repo2(repoB)
val repo3 = Repo2(repoC)
val repo4 = Repo2(repoD)
repo2.process(DataContainer("Process B type", B()))
repo3.process(DataContainer("Process C type", C()))
repo4.process(DataContainer("Process D type", D()))
println(repo2.repo.getParam())
println(repo3.repo.getParam())
println(repo4.repo.getParam())
}

When should I use delegation in Kotlin?

I have been going through multiple links (One, Two) and documentation regarding the delegate pattern and somewhat understand the advantage it brings in the form of "composition over inheritance". I can see how the inbuilt delegate properties (lazy, vetoable, map, observable) are useful; but having a hard time understanding 2 areas:
1. Why/When should I write a custom delegate for property? How is it better than overriding getter/setter of that property?
Comparing the 2 approaches:
private var withoutDelegate: String = ""
get() = DataHelper.getLatestData(::withoutDelegate.name)
set(value) {
DataHelper.setLatestData(value)
field = value
}
val withDelegate by StringDelegateProvider()
class StringDelegateProvider {
operator fun getValue(thisRef: String?, property: KProperty<*>): String {
return DataHelper.getLatestData(property.name)
}
}
2. At the class level, how is delegation better than traditional composition patterns?
Comparing the 2 approaches - composition without delegation seems much more concise:
interface Base {
fun print()
}
class BaseImpl1(val x: Int) : Base {
override fun print() { print(x) }
}
class BaseImpl2(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun clientFunctionWithDelegation() {
val i1 = BaseImpl1(10)
val i2 = BaseImpl2(10)
val b1 = Derived(i1)
val b2 = Derived(i2)
b1.print()
b2.print()
}
fun clientFunctionWithoutDelegation(){
//wihtout extending Base, we can still create multiple types of Base and use them conditionally.
val i1: Base = BaseImpl1(10)
val i2: Base = BaseImpl2(10)
i1.print()
i2.print()
}
Would appreciate if the community can share some use-cases where delegation can help.
1: You can reuse the delegate without having to override get and/or set each time. Example: the lazy delegate
2: let's say you want to create a MutableList that prints the list every time you mutate the list. You don't want to reimplement MutableList, you just want to override the functions mutating the list. So instead of manually delegating every call, you just say class PrintList<T>(original: MutableList<T>) by original and you just override the functions you care about

Generic constraint as type list? Or compile-time duck typing?

The following minimal example shows my problem quite well, I hope:
data class OneThing(val x: Int, val y: Int, val foo: String)
data class ASimilarThing(val x: Int, val y: Int, val bar: String)
fun <THING> process(thing: THING) {
println(thing.x)
println(thing.y)
}
fun main() {
val oneThing = OneThing(1, 2, "hi")
val aSimilarThing = ASimilarThing(3, 4, "ho")
process(oneThing)
process(aSimilarThing)
}
The two classes OneThing and ASimilarThing come from a library, so I can not change them. But semantically they are very similar, and I'd like to avoid implementing my logic two times, so I'd like to have it in a generic process function.
However the above does not compile. (In C++ it is possible.)
Is there a way to make process work? Ideally I'm looking for something like
fun <THING: [OneThing, ASimilarThing]> process(thing: THING) { ... }
But such a syntax does not exist.
Any other ideas? :)
Kotlin like Java is a statically typed, object oriented language. Therefore you'd have to provide an common supertype declaring the properties x and y.
As this is not the case you cannot achieve duck typing as you like. THING has a supertype of Any? and therefore x and y are not accessible.
You could work around your issue by using a wrapper or delegate:
sealed class Thing {
abstract val x: Int
abstract val y: Int
}
class ASimilar(
delegate: orig.OneThing
): Thing() {
override val x get() = delegate.x
override val y get() = delegate.y
}
class ASimilarThing(
delegate: orig.ASimilarThing
): Thing() {
override val x get() = delegate.x
override val y get() = delegate.y
}
fun process(thing: Thing) {
println(thing.x)
println(thing.y)
}