Super-type list in traits


#1

The blog post about multiple inheritance describes a peculiar feature of the Kotlin language, which is requiring non-trait types. I do see the problem in having state in your traits and the problem with constructors; I understand why it is a good idea to avoid it, but I do not see the real value in requiring class types in traits.

Suppose that a trait T requires type C and C is a class.

open class C trait T: C

Now suppose that you need to inherit from X and you want to implement T; this will give you a compiler error.

open class X class D : X(), T // error, must initialize C

My point is, if implementing T requires me to extend C, then why having T in the first place? I would rather provide a (possibly abstract) subclass of C, because I'll be forced to extend C() anyway.

abstract class T : X() class D : T() // cannot extend C anyway

Am I missing something?

thanks!


#2

My point is, if implementing T requires me to extend C, then why having T in the first place? I would rather provide a (possibly abstract) subclass of C, because I'll be forced to extend C() anyway.

abstract class T : X()
class D : T() // cannot extend C anyway

Am I missing something?

I think so: When you declare your class D, you can add functionality to it by subclassing only. You can't combine it with my class E extends C.

For this combining to work, you need that at least one of D and E is a trait. I’ve tried to create a practical example, but failed on my lacking syntax.

By the way, your first example seems to compile:
http://kotlin-demo.jetbrains.com/?publicLink=104379794518123041179-1677427850


#3

I think so: When you declare your class D, you can add functionality to it by subclassing only. You can't combine it with my class E extends C.

For this combining to work, you need that at least one of D and E is a trait. I’ve tried to create a practical example, but failed on my lacking syntax.


I know what the benefits of traits are. What I’m discussing here is a particular feature of Kotlin’s traits, which is to allow class types in their super-type list.

Your trait T may require class C by using the syntax:

open class C trait T : C

then you are effectively constraining any class that implements T to also extend C. Now, I understand that the class that implements T may also extend D that extend C in turn, but still you are imposing a hard constraint on your class hierarchy, which, in my opinion, goes a bit against the use of traits in the first place...

 
open class C
open class D() : C()
trait T: C

class X1 : C(), T
class X2 : D(), T

but what if, again, I want X1 to also extend class X ? (why would I want that? a framework may impose the choice on me)

By the way, your first example seems to compile:

That's because the online version is buggy. See http://kotlin-demo.jetbrains.com/?publicLink=117344351312029847525545262373

Compare it to the nightly version of the plugin (0.8.740)

if you want to implement T, you are actually required to instantiate C()

class D : C(), X(), T

> error: Only one class may appear in a supertype list


#4
I know what the benefits of traits are. What I'm discussing here is a particular feature of Kotlin's traits, which is to allow class types in their super-type list.

This is how I understood the question, however, my answer missed it, sorry for that. I thought I could find some real use examples, but I can't. I can't even find a good abstract example. It should go like this:

Imagine a third party open class C failing to expose a useful method foo() via an interface. You want to add some functionality Bar to it, which depends on foo(), so you write

trait Bar : C {
  fun bar = foo() + 42
}

and it can be nicely combined with a similar trait Baz : C. However, the better way would be to extract a

trait Foo {
  abstract fun foo();
}

and add

trait Bar : Foo

trait Baz : Foo

class CBarBaz : C(), Bar, Baz

What remains is a trait accessing fields of C, but this is rather absurd.

TL;DR; I though I had an idea, but it was an illusion.


#5

It was conceived as means of decomposing biggish classes and having traits as mixable-in behaviors in a big hierarchy: see this paper http://www.ptidej.net/courses/ift6251/fall06/presentations/061122/061122.doc.pdf

This feature is indeed questionable: the benefits are smallish, and the concept may be hard to grasp and implement. We are considering removing it before 1.0


#6

abreslav ha scritto:

It was conceived as means of decomposing biggish classes and having traits as mixable-in behaviors in a big hierarchy: see this paper http://www.ptidej.net/courses/ift6251/fall06/presentations/061122/061122.doc.pdf

This feature is indeed questionable: the benefits are smallish, and the concept may be hard to grasp and implement. We are considering removing it before 1.0


Oh, I’ve read (and sometimes cited) that paper! I also see that problems arise from the fact that Smalltalk is dynamically-typed programming language, while Kotlin is statically-typed, thus, assumptions that they make there cannot be made here.

e.g., a trait may require arbitrary members to be available, while, in Kotlin, the required members must be declared somewhere.

As you probably already know, Scala(*) overcomes this by also supporting structural typing (that is, for people who may not know, you can basically require an unnamed type in terms of the members you expect it to have e.g. “some type with a member fun foo(): Unit”), but I’m quite certain that you do not want that kind of complexity in Kotlin.

I’m now seeing where the idea of a class requirement may come from, but I think that would really tightly couple the trait hierarchy with a class hierarchy, which I feel like it would hinder the benefits of traits altogether. What I would rather do is what @maaartinus is suggesting in his own post; that is, define an ad-hoc trait and code against that, rather than actually requiring the class. This would still fit the original trait model(**).

e.g. Class = Superclass + State + t1 + t2 + Glue

I see no problem with State being implemented by the last class in the chain (together with the Glue), e.g.

open class SuperClass(val someInt: Int) trait Requirements {   val someInt: Int } trait T1 {   val otherState: Int } trait T2: Requirements

class LastClass: SuperClass(100), T1, T2 {
  val otherState = 20
}

(*) whose particular flavor of traits is kind of different from that described in the original paper; but then again, Scala is not Smalltalk
(**) for people who may be interested in this topic, Chapter 8 of Schärli’s PhD thesis, which came after the paper, discusses other trait implementations, even in statically-typed contexts http://scg.unibe.ch/archive/phd/schaerli-phd.pdf


#7

Oh, I've read (and sometimes cited) that paper! I also see that problems arise from the fact that Smalltalk is dynamically-typed programming language, while Kotlin is statically-typed, thus, assumptions that they make there cannot be made here.

Yes, I think so, too. Stéphane Ducasse is a big fan of Smalltalk and also quite involved in Pharo (pharo.org), which is a variant of Squeak Smalltalk. I guess they only tried out their approach towards traits in their own trait-aware modification of Squeak Smalltalk (see http://wiki.squeak.org/squeak/538).


#8

Oliver_Plohmann ha scritto:

Yes, I think so, too. Stéphane Ducasse is a big fan of Smalltalk and also quite involved in Pharo (pharo.org), which is a variant of Squeak Smalltalk. I guess they only tried out their approach towards traits in their own trait-aware modification of Squeak Smalltalk (see http://wiki.squeak.org/squeak/538).


yep, however Schärli did compare their trait definition to other trait implementations, check his PhD thesis out