suggestion: full support for traits. Syntactically...
# language-proposals
x
suggestion: full support for traits. Syntactically I feel like kotlin already does with interfaces, but interfaces cannot have state currently, a trait on the other hand can store state but can't be instantiated (outside of anonymous class... ) itself. I actually thought kotlin had this, because you can put a val in the interface, however I didn't realize you'd then have to override that in the composing class. Basically I want support for Perl 6 Style role's except statically typed.
šŸ‘ 1
k
Maybe I'm misunderstanding, but how would this be different from abstract classes?
āž• 1
r
traits unlike interfaces are not restricted in terms of inheritance. The issue is that in the Jvm AFAIK you still need to resort to something like trait linearization https://stackoverflow.com/questions/34242536/linearization-order-in-scala
x
interfaces are restricted in terms of inheritance?
difference is you can have multiple "traits" on a concrete implementation, but they are flatly composed with no ambiguous MRO (need for c3 or other MRO), and conflicts are compile time errors that must be resolved by the developer. IMHO, they effectively are interfaces (so much so that the traits page on wikipedia lists kotlin as having them). IMHO Roles/Traits though can have state, meaning if I have a trait
Nameable { var name }
and I compose it with
Identifiable { var id }
when composed into a concrete implementation I shouldn't have to override id/name.
k
So abstract classes with multiple inheritance? I don't really understand the scala stuff šŸ˜•
x
@karelpeeters that's a gross simplification? and a little wrong since it doesn't use inheritance at all... (traits are flat composition, essentially an ordered set, not a hierarchy (same problem different solution) ) not in the way you'd normally mean... maybe this ancient post is useful https://perl6advent.wordpress.com/2009/12/18/day-18-roles/
hmm... analogy, is it an abstract class with multiple inheritance, yes if you consider that a nail and a screw both hold boards together, it's just another type of fastener.
k
Hmm, I see.
m
And it’s not just Scala. Groovy introduced Traits too.
šŸ‘ 2
p
Not sure what's pragmatic about that. Why not just use Scala instead of polluting Kotlin with tons of unnecessary concepts? Sure, nobody will force me to use them, but it will force me to read them in other people's code. Thumbs down from me šŸ‘Ž
x
@poohbar... ... are you just a hater? I've worked in languages that have this, it makes working on certain problems vastly easier and the code shorter, and dry-er usually in ways that are easier to comprehend. I guess to you though kotlin is perfect and won't be in need of more features.
p
I am not just a hater. But I do think that Kotlin has a lot of features already and does not need that many more. It's almost perfect the way it is right now. Only things I would change is couple clean ups here and there, some consistency fixes etc. I would even remove some features, to be honest. I am just very wary of feature bloat. I think we all know what happened to Scala. It's now pretty much garbage language full of features that contradict each other and every project you didn't write yourself is a freaking enigma. All thanks to people like you who just couldn't rest until every new flashy concept was pushed into the language for very little benefit. Please don't take it personally I understand you mean well and this is just my opinion. On the other hand, you have Java which is one of the most successful and widely used languages and one of the reasons is that it is very simple and evolves extremely slowly. I think Kotlin should evolve a bit faster than that but slower than it does now. I would love to see Kotlin being adopted in enterprise environments where it could replace Java but it will never happen if Kotlin becomes too complicated and bloated. My 2 cents.
l
I think a feature like traits could be useful without harming the ecosystem if it's kept simple. I've been thinking about something I'd call `inline interface`s with restrictions of the same kind as for inline classes, which you could implement with an adapter that you would import similarly to how you import an
operator fun
. I've a use case which is diving the number of a kind of functions in a project by 3 where I currently define them for multiple types that have a property of the same type. I've been thinking about submitting a KEEP, but I'm missing some pieces to have a good design proposal, it's about how to write an inline interface adapter.
šŸ‘ 2
r
@poohbar that is a very narrow view of Scala, specially since Kotlin borrows so much from it and makes no excuses for it either. Sounds to me like lang flame wars that most people here don't care about. Scala in the same way as Kotlin has good and bad things and simpler and other more complicated things. That is because of the audience they target and their historical background. Languages that are closed for improvements become outdated and I don't think Kotlin is closed for improvement given the channel we are reading each other in.
@louiscad KEEP-87 solves the interface composition problems by providing implementations known to the compiler automatically and their interface definitions. This allows for horizontal composition without inheritance or need for linearization since you can provide families of extensions for any type and activate them scoped wherever needed.
šŸ‘ 2
What you describe sounds a lot like Keep 87
x
I don't know the story of scala, I skipped that chapter. I'll be honest though I see a trend towards 3GL language features converging. One benefit of kotlin evolving rapidly, and already supporting a ton of features is that you aren't as likely to run into people using "new feature solves everything let's use it everywhere". So in gaps like traits I'm rather in favor of getting it fast so people aren't spending time working around it not being there and using other features where traits would be the better solution. Note, I don't care if they just end up being baked into kotlin's advanced interfaces, as long as they can quack like the duck.
my biggest use case for traits is data objects, I'm constantly writing the exact same kinds of data objects... but ultimately should be different kinds of classes, and usually they are 1 or 2 property different. If I could avoid the repetitiveness of typing out the properties again and again and just compose the traits... I think this could be done without changes to syntax, just allow use of existing interface val/var, but don't require them to be overridden in the concrete class
l
@xenoterracide For this use case, why don't just use a common class with the common properties that you include as a property in the classes that have other different properties?
@raulraja Will look into it, thank you šŸ™‚
x
@louiscad because, I swear the PMs make things just different enough, just to be different. In our system we have User, Organization, Role, ProviderTest, etc, and all of them have a name attribute and extend the same base class, except? ProviderTest, what's provider test have? a title... we also have a tone of other entities lacking a name resemblence but that have an id. On top of that we have Local DTO's or 2 step views... so common I made an IdentifiableNameableDTO, but it has a problem, because our Id's are Long's if you were to mix entities they could in theory compare as equal, so I documented don't do that, but the better idea would simply be to compose an Identifiable and Nameable trait into a 1 line class definition where I needed a new one. Essentially due to subtle differences such a feature would be a massive code saver. (and these aren't the only properties commonly duplicated, hey this thing has id, name, dateCreated that one is id, dateModified, description, make a cartesian product of class properties)
so for something massive like traits, how is that approached? doesn’t seem as simple as ā€œrequest featureā€, or is it?
r
I think that sounds like the cake pattern
which most people hate because it gets unwild in terms of initialization order and boilerplate to define the modules. That is just so that the compiler can verify your abstract deps in traits have concrete implementations that satisfy the uber class.
Keep 87 solves that problem via compiler injection but with horizontal composition and agreggation instead of interface/trait inheritance
Copy code
interface Nameable<A> {
  fun A.name(): String
}

data class Person(val firstName: String)

extension object PersonNameable: Nameable<Person> {
  override fun Person.name(): String = firstName
}

fun List<A>.names(with nameable: Nameable<A>): List<String> =
  map { it. name() }

listOf(Person("John"), Person("Jane")).names()
@xenoterracide if Keep 87 makes it all those interface compositional issues related to inheritance and sub typing are gone because composition is horizontal and transparent. The compiler allows
names()
to be invoked because if there was no
Nameable<Person>
declared as extension it would not compile with an appropriate error. This technique allows you to inject any functionality over any type in a type safe way at compile time wether you are the author or it's a third party type.
It also removes the need for runtime DI frameworks all-together
x
link to keep 87?
r
x
heh, I’ve heard that about traits before, but I’m not sure I believe it… or rather I think it removes some cases for DI frameworks… but not architectural ones… I could be wrong though
r
traits don't type classes do.
Traits with the cake patterns is to achieve compile time DI. But you can use traits for good use without the cake pattern
Type classes break the sub-typing inheritance relationship and permit any level of dependencies and any scope of creation both, singleton, prototype and scoped
which map to
extension object|val
,
extension class
,
extension fun
x
yeah I’m just thinking that it sounds like something people could use to make god objects… which would be back to another problem (people can write spaghetti in everything šŸ˜‰ )
doesn’t make it a bad pattern/feature
r
and since they are coherent the compiler can find them without imports
yeah but the problem with traits agreggated and the cake pattern is that you need to provide multiple overrides all over the place because of the collisions and initialization order
x
right
traits really shouldn’t require overrides unless there’s a conflict if they’re done right, and use composition. anyways, I’m going to read this keep over to make sure I understand it, I’ll probably come back to it after work.
r
sounds good, if you have any questions feel free to ping me