What is wrong about implementation by delegation?

From the Wrap-up of the article Effective Class Delegation:

implementation by delegation - has been named as the “worst” feature in Kotlin by the lead language designer, Andrey Breslav on several occasions (e.g. during the KotlinConf 2018 closing panel discussion). There are some cases where this kind of delegation can get complicated and produce some… Interesting behaviour.

What problematic cases exist? They are not mentioned in the article or the closing panel discussion.

2 Likes

One of the points is the fact that delegation happens through a hidden field, even if there is an actual field as well. Partial specialisation doesn’t always work well either.

1 Like

Thank you for your response. What is the issue of two fields (hidden vs. actual)? Can you give an example of partial specialization that is not working?

@pdvrieze I’m not sure if you got notified about my reply. May I draw attention to my questions again?

I don’t know the reason the Andrey Breslav has but I can speculate. Based on some older discussions here it is a feature that even though it looks powerful is not flexible enough to be useful in most cases. From what I know of open source projects and my own it is rarely if ever used.
I don’t know how complex it’s implementation is in the compiler but it’s something that at least as to be supported for the foreseeable future even though it does nothing to improve the language. It might even be a problem if it prohibits adding a better alternative or other features.

1 Like

From what I know of open source projects and my own it is rarely if ever used.

@Wasabi375 Is composition in general (delegation methods, etc.) rarely used or just the implementation by delegation syntax?

I don’t have any real data, but I can’t think of any place where I have seen the delegation syntax used at all. Composition on the other hand is a useful design method that while maybe not as popular as inheritance is still used regularly.

What goes wrong is for example if you have the following:

class X(var a:String): CharSequence by a

fun main() {
  val x = X("a")
  println("before (expected a): ${x[0]}")
  x.a="b"
  println("after (expected b): ${x[0]}")
}

This is an unexpected result that also differs from delegating each function explicitly. Note that this is a point against the implementation of delegation implemented through this language support. It is not against the concept of delegation in principle (which has plenty of valid uses). Delegation properties are different and are not part of this discussion.

2 Likes

Thanks for this example. Could you please explain why it behaves like this? As you said, it is an unexpected result :sweat_smile: What’s going on under the hood? :thinking:

Under the hood it’s something like this

class X(var a:String): CharSequence {
    private val charSequenceDelegate = a
    override fun iDontKnowTheCharSequnceFunctions() = charSequenceDelegate.iDontKnowTheCharSequnceFunctions()
}

This means that reassigning a will have no effect on the delegate but modifying a value within a directly would.
Ok maybe String is not the best example here since it’s an immutable class but lets just imagine it is something that we can modify.

Also whenever you want to know how a kotlin feature works internally I you can look at the decompiled bytecode. With intellij you can do that under tools>kotlin>show kotlin bytecode.
If you don’t want to or know how to read jvm bytecode the bytecode window has a button to convert the bytecode to java. That way you get an easy to understand representation of the kotlin features. Just a small warning the conversion to java is not perfect. It’s good enough to figure out what happens but you would not be able to compile it with a java compiler.

2 Likes

The key to realise is that if you were using a StringBuilder (which is mutable), modifying the content of the StringBuilder would work as expected (it is a reference), but if you actually set the value to a different StringBuilder it doesn’t work as expected. I chose String as example as the issue is with value types (reference reassignment).

I wonder, why

class X(var a:String): CharSequence by a

is not translated to

class X(var a:String): CharSequence {
    override val length: Int
        get() = a.length

    override fun get(index: Int): Char = a.get(index)

    override fun subSequence(startIndex: Int, endIndex: Int): CharSequence =
        a.subSequence(startIndex, endIndex)
}

fun main() {
    val x = X("a")
    println("before (expected a): ${x[0]}")
    x.a="b"
    println("after (expected b): ${x[0]}")
}

Why use a (hidden) delegate property (like charSequenceDelegate in the example of @Wasabi375) in the generated code in the first place?

1 Like

Interface by delegation is one of my most favorite things about Kotlin.
Therefor I never understood why it’s one of the regrettable things about Kotlin.
Sure, I understand that the current implementation is not the best one and can confuse people.
But why can’t it be extended:

I don’t know if Breslav talked about this, but during this Kotlin-conf he said that there were features where people want more of, but which aren’t possible.
Therefor, I think adding a keyword (like vararg) which would make the field a var isn’t easy to do?
As I think that is the extension that would increase the value a lot…

But I don’t know enough to know if and why this cannot be changed…

To end somewhat positive: I’m not experienced with it, so i don’t know if it will solve all issues (like interface-constraints) the “correct” (global vars for state?) and most simple (is it extendable without needing to change a lot of code) way, I do think decorators can help a lot of use-cases that currently are solved with interface delegation or manual.

4 Likes