This type


#1

I may have posted something about this in the past, so sorry if this is a duplicate.

I’d love to have a This type in Kotlin. For example:

class Foo {
  private var _x: Float = 0f
  fun setX(x: Float): This {
     _x = x
     return this
  }
}

class Bar : Foo {
   private var _z: Float = 0f
   fun setZ(z: Float): This {
       _z = z
       return this
   }
}

val b = Bar()
b.setX(3f).setZ(4f)  // Valid because the return of setX is treated as the same type as the Bar instance.

My use case isn’t exactly for chaining, but it was an easy example I could think of. For chaining I’d prefer to see something like Dart’s … operator. b.setX(3f)…setY(4f)
I would use it more for things like callbacks where the instance is passed.


Why not provide self-type for mixin interface?
Type inference when map with generic function
#2

One more example:

`
open class StyleBase {
   var onChanged: ((This)->Unit)? = null

   fun notifyChanged() {
       onChanged?.invoke()
   }
}

class ButtonStyle : StyleBase {
  public val padding: Float = 0f
}

val b = ButtonStyle()
b.onChanged = { println(it.padding) }  // No need to cast `it` from `Style` to `ButtonStyle`

b.padding = 5f
b.notifyChanged()

`

#3

Use extension methods:

open class Foo {
    internal var x: Float = 0f
}

fun <T: Foo> T.setX(x: Float): T {
    this.x = x
    return this
}

open class Bar: Foo() {
    internal var z: Float = 0f
}

fun <T: Bar> T.setZ(z: Float): T {
    this.z = z
    return this
}

class Baz: Bar()

fun test() {
    val baz: Baz = Baz().setX(42f).setZ(1337f)
}

I think it’s a pretty cool application of extension methods.

You do have to make your var internal though. If you only use setX and setZ from within the class you can move them there and keep the var private.

The second example is trickier, but you can achieve it like in Java:

abstract class Changeable<Self>
    where Self: Changeable<Self>
{
    var onChange: ((Self) -> Unit)? = null
}

open class StyleBase<Self>: Changeable<Self>()
    where Self: StyleBase<Self>
{
    fun notifyChanged() {
        onChange?.invoke(this)
    }
}

class ButtonStyle : StyleBase<ButtonStyle>() {
    public var padding: Float = 0f
}

fun test2()
{
    val b = ButtonStyle()
    b.onChange = { println(it.padding) }
    b.padding = 5f
    b.notifyChanged()
}

Note you don’t need Changeable per se, you can roll it into StyleBase. You need to have these where clause on the whole hierarchy except at the leaf. In this case it’s okay, but if you want to actually use StyleBase elsewhere than as the supertype in a class definition, it’s really burdensome. In those case I’d recommend sticking to the casts.

Or wait until there is a real This type :slight_smile: Myself, I’m more in favour of some form of abstract type (which is really just like a type parameter, except that each class must fix it once and for all and that it hence doesn’t need to be carried around with the type).


#4

Extension methods aren’t polymorphic, and as you said, also requires you to expose state properties.

That’s interesting about abstract types in Scala. Certainly there are more powerful solutions than the one I suggested.


#5

Unfortunately, self-types are very hard to implement in the language without crippling the rest of the type system significantly


#6

To be pedant, here they are polymorphic, it’s just parametric polymorphism (generics) rather than inclusion polymorphism (inheritance).

But I get your point: there’s no dynamic dispatch involved. In this case it doesn’t really matter because the extension method just gives you the correct type, and you can always override the setter for custom treatment.

And yes, file-private and package-private modifiers would be swell.

@abreslav Any thoughts on abstract type members? At first look, they seem strictly less powerful than type parameters.


#7

I think that it would be worth to have comfortable support even just for method chaining. It is definitely priceless for implementing especially builders and I believe that such support does not cause such problems that you mention. One of the notable case where the lack of method chaining support resulted in usability failure is the case of NIO buffers. For those not familiar with them, following example shows the point (rather in Java – feeling more confident not to make a mistake):

class SimpleBuilder {
    SimpleBuilder add(String value) { /* some implementation returning this */ }
    Product build() { /* make the product */
}

class ExtendedBuilder extends SimpleBuilder {
    ExtendedBuilder add(int value) { /* some implementation returning this */ }
}

// Alas, this is not possible:
Product result = new ExtendedBuilder().add("string").add(1).build();
// It must be written using a temporary variable and more steps:
ExtendedBuilder builder = new ExtendedBuilder();
builder.add("string");
Product result = builder.add(1).build();

But if the compiler knew that SimpleBuilder::add returns this, it could allow the impossible construction, just by emitting the code equivalent to the manual workaround. And this is the case when This pseudotype could be used – or perhaps a cleaner solution would be using an annotation, let’s say @This, on the method which would just tell the compiler that the method does return this (and the compiler could check it when compiling the method):

class SimpleBuilder {
    @This // Requires the method to return this and allows the compiler at the invocation site use this assumption
    SimpleBuilder add(String value) { /* some implementation returning this */ }
    Product build() { /* make the product */
}

I admit that this is just a syntactic sugar for a particular case. But it is still better than nothing. If anybody has a better proposal, I’ll be the first to vote for it :wink:


#8

@pdolezal You can do this with extension methods, as outlined in my post above. The only downside is that you have to write one extension method per class method if you want to allow overriding.


#9

Abstract type members = [path-]dependent types = too much


#10

Aha, indeed.

I was thinking more of something like this:

open class C1 { type T }
open class C2: C1() { type T = Foo }

which would be semantically equivalent to:

open class _C1<T>
open class _C2<T: Foo>: _C1<T>()

given that each occurrence of C1 is replaced by _C1<out Any> and each occurrence of C2 by _C2<out Foo> (or _C2<Foo> if C2 were final).

(In fact it would be even better to introduce two notations: type T < Foo and type T = Foo were the former would behave as in the example, and the latter would allow removing the covariance while keeping the class open, fixing T to Foo for all derived classes.)