Given they're already supported it seems a shame not to have them be denotable, but you'd have to be careful with the syntax to steer people away from confusing it with multiple inheritance. I also wonder if there are good arguments against passing around A & B & C rather than RealType: A,B,C. I also want discriminated union types, but it seems unlikely.
8 หลายเดือนก่อน +4
That's exactly what I'm looking for ❤. The other part of my problem was to pass an anonymous object as parameter to the function call. That was what worked for me: // calling process(item = object : Named, Addressable { override val url: String get() = TODO() override val name: String get() = TODO() })
Ah, thanks for pointing that out, Vinícius! Yes, anonymous classes can indeed be created for multiple interfaces at the same time! That could be helpful for others watching the video.👍
At first I thought, “you need something like Rust’s trait bounds or Haskell’s type constraints.” Then you showed exactly that. (In fact Rust’s `where` syntax is remarkably similar.) I don’t work on the JVM much anymore, but if I did, I’d choose Kotlin for its more complete data type algebra.
Oh, cool! I'll enjoy getting a closer look at how those language features work compared to this - especially if / how they apply to function results instead of just function arguments. 👍
Nice video, thanks Dave! I have used the "where" constraints before, but never realized they could be used for type intersections. Based on the definitely non-nullable generics syntax, I think native intersection support is desired by the language maintainers, but I imagine it's low in the priority list.
Thanks, Stephen! Yeah, a lot of new features are in a holding pattern until Kotlin 2.0 comes out, which I suspect will land sometime between now and KotlinConf in May. In last year's keynote, they mentioned some features they were planning to deliver after K2 arrives, and Denotable Intersection Types wasn't on that list... but it does have more votes than some of the things that _were_ on the list. So, we'll see...
I absolutely use intersection types in Typescript, often just as a way of extending a type because I prefer to use *type* over *interface* (so your combination interface was the first solution that came to my mind for Kotkin). But what I'd love to see even more in Kotlin is union types!
That's kind of you to say! Breslav and Elizarov are some incredibly knowledgeable developers, and I'm just thankful they created a language that I enjoy so much!
fyi kotlin's typesystem was made by Ross Tate, who also made a typesystem for ceylon with union and intersection types. those two kinda deprived us from having them in kotlin
I go back and forth on whether I want intersections (and unions) to be easily denotable in Kotlin specifically or not. On the one hand, they can be useful like you showed. On the other, I think Kotlin has kind of suffered from having too many features thrown in that are sliiightly different from one another and could use a bit of trimming + condensing into deeper features first, _then_ take a look at which new features are still needed. (Of course that's not going to happen in practice, removing any non-experimental feature would be a huge breaking change.) I'm also curious how this is represented on the JVM, I'd guess it probably just chooses the nearest superclass of both types or something?
Yeah, that makes sense. I've definitely seen how some programming languages (that have been around longer than Kotlin) have ended up with many features that are similar to each other, or that allow so many varying approaches to the same thing, that the language starts to lose its identity, and there's no clear consensus from the developer community about how to solve the same problems. FWIW, Kotlin _has_ successfully deprecated quite a few behaviors and stdlib functions... but it's a pretty long lead time, and like you said - breaking changes are a big consideration. The implementation on JVM is pretty straightforward - the object is just cast to the appropriate type before calling a function on that type. For the example in this video, it looks like this: "Refreshing data for " + ((Named)item).getName() + " at " + ((Addressable)item).getUrl();
I liked this approach!!! In cases that need more than 2 interfaces, and to create a new interface that envolve all of them it's a little strange. Great content!!
Great video, keep it up! We're always looking forward to your content and eagerly await a tutorial video for your amazing book 'Kotlin: An Illustrated Guide'
Another way to do this is to use the `context` feature (needs to be enabled in the compiler options). The syntax isn't as nice for this use-case, but makes sense for other ones. context(Named, Addressable) fun process() { println("Processing... $name at $address") } fun main() { User("John Doe", "123 Main St.").run { process() } }
Great video explainer 😊. I'm trying to do something similar but I think different. Is this the same as Union types? I want to create a function that accepts / returns either String Boolean Integer or my own type. Any seems to be my only option at the moment plus runtime checks.
Thanks so much! 🙂 Yes, union types are related but different, as you noted. Kotlin's type system doesn't currently support union types at all, but there are a few things you can consider using in order to avoid the Any type. In certain cases, some good old fashioned function overloads will do the trick. It definitely results in additional boilerplate, though, since you have to define a function for each type. Another option to consider is an Either type, like the Arrow library provides. This is helpful in cases where you've only got _two_ types... but since your use case has four, it wouldn't be as helpful there. Anyway, not sure if those are helpful for your particular use case. Kotlin has an open ticket for considering denotable intersection and union types here: youtrack.jetbrains.com/issue/KT-13108. You can vote on it, add your use case, and watch it for updates!
To add, custom union types (such as Arrow’s Either type, or Michael Bull’s Result type) can be created with a combination of sealed classes, generics, and extension functions
My goal in each video is to lay a solid foundation of understanding for the concept, and that doesn't happen when you simply jump to the "best" solution. It requires wading through worse solutions, talking through the problems with them, and gradually refining them toward better solutions, so that by the end we understand why each solution is more optimal or less optimal compared to the others. If you can't spare the three minutes that the Any section took up in this video, that's fine, but future videos here will have a similar pace.
Its not a perfect or even good solution but its a solution. You cant solve everything on compile time, so solving things on runtime is still a little part of state of the art.
What do you think - should Kotlin support denotable intersection and union types?
If it doesn't cost me anything in compilation or runtime performance then yes absolutely up for denotable intersection types.
Given they're already supported it seems a shame not to have them be denotable, but you'd have to be careful with the syntax to steer people away from confusing it with multiple inheritance. I also wonder if there are good arguments against passing around A & B & C rather than RealType: A,B,C.
I also want discriminated union types, but it seems unlikely.
That's exactly what I'm looking for ❤.
The other part of my problem was to pass an anonymous object as parameter to the function call.
That was what worked for me:
// calling
process(item = object : Named, Addressable {
override val url: String
get() = TODO()
override val name: String
get() = TODO()
})
Ah, thanks for pointing that out, Vinícius! Yes, anonymous classes can indeed be created for multiple interfaces at the same time! That could be helpful for others watching the video.👍
At first I thought, “you need something like Rust’s trait bounds or Haskell’s type constraints.” Then you showed exactly that. (In fact Rust’s `where` syntax is remarkably similar.) I don’t work on the JVM much anymore, but if I did, I’d choose Kotlin for its more complete data type algebra.
Oh, cool! I'll enjoy getting a closer look at how those language features work compared to this - especially if / how they apply to function results instead of just function arguments. 👍
Nice video, thanks Dave! I have used the "where" constraints before, but never realized they could be used for type intersections.
Based on the definitely non-nullable generics syntax, I think native intersection support is desired by the language maintainers, but I imagine it's low in the priority list.
Thanks, Stephen! Yeah, a lot of new features are in a holding pattern until Kotlin 2.0 comes out, which I suspect will land sometime between now and KotlinConf in May. In last year's keynote, they mentioned some features they were planning to deliver after K2 arrives, and Denotable Intersection Types wasn't on that list... but it does have more votes than some of the things that _were_ on the list. So, we'll see...
Great video! And I love the Mr. Grumpy Recalcitrant Man 😆
Thank you! I'm glad you like Mr. Grumpy Recalcitrant Man! I think we can all relate to him. 😁
I absolutely use intersection types in Typescript, often just as a way of extending a type because I prefer to use *type* over *interface* (so your combination interface was the first solution that came to my mind for Kotkin). But what I'd love to see even more in Kotlin is union types!
I think you are know Kotlin better than Kotlin inventor himself
That's kind of you to say! Breslav and Elizarov are some incredibly knowledgeable developers, and I'm just thankful they created a language that I enjoy so much!
fyi kotlin's typesystem was made by Ross Tate, who also made a typesystem for ceylon with union and intersection types. those two kinda deprived us from having them in kotlin
I go back and forth on whether I want intersections (and unions) to be easily denotable in Kotlin specifically or not. On the one hand, they can be useful like you showed. On the other, I think Kotlin has kind of suffered from having too many features thrown in that are sliiightly different from one another and could use a bit of trimming + condensing into deeper features first, _then_ take a look at which new features are still needed. (Of course that's not going to happen in practice, removing any non-experimental feature would be a huge breaking change.)
I'm also curious how this is represented on the JVM, I'd guess it probably just chooses the nearest superclass of both types or something?
Yeah, that makes sense. I've definitely seen how some programming languages (that have been around longer than Kotlin) have ended up with many features that are similar to each other, or that allow so many varying approaches to the same thing, that the language starts to lose its identity, and there's no clear consensus from the developer community about how to solve the same problems. FWIW, Kotlin _has_ successfully deprecated quite a few behaviors and stdlib functions... but it's a pretty long lead time, and like you said - breaking changes are a big consideration.
The implementation on JVM is pretty straightforward - the object is just cast to the appropriate type before calling a function on that type. For the example in this video, it looks like this:
"Refreshing data for " + ((Named)item).getName() + " at " + ((Addressable)item).getUrl();
I liked this approach!!! In cases that need more than 2 interfaces, and to create a new interface that envolve all of them it's a little strange. Great content!!
Great, thanks so much! I'm glad it was helpful for you! 😁
Great video, keep it up! We're always looking forward to your content and eagerly await a tutorial video for your amazing book 'Kotlin: An Illustrated Guide'
Thanks so much! New video coming very soon!
I was reading your blog from day 1 and right these videos, it is amazing man really, with your draws or these animations. you are the best
Ah, thanks so much! I appreciate it! 😁
Another way to do this is to use the `context` feature (needs to be enabled in the compiler options). The syntax isn't as nice for this use-case, but makes sense for other ones.
context(Named, Addressable)
fun process() {
println("Processing... $name at $address")
}
fun main() {
User("John Doe", "123 Main St.").run { process() }
}
Context receivers! Yeah, it's been a while since I checked in on them. Definitely similar! 👍
Amazing content man!
Thanks - I appreciate it!
Great video explainer 😊. I'm trying to do something similar but I think different. Is this the same as Union types? I want to create a function that accepts / returns either String Boolean Integer or my own type. Any seems to be my only option at the moment plus runtime checks.
Thanks so much! 🙂 Yes, union types are related but different, as you noted. Kotlin's type system doesn't currently support union types at all, but there are a few things you can consider using in order to avoid the Any type.
In certain cases, some good old fashioned function overloads will do the trick. It definitely results in additional boilerplate, though, since you have to define a function for each type.
Another option to consider is an Either type, like the Arrow library provides. This is helpful in cases where you've only got _two_ types... but since your use case has four, it wouldn't be as helpful there.
Anyway, not sure if those are helpful for your particular use case. Kotlin has an open ticket for considering denotable intersection and union types here: youtrack.jetbrains.com/issue/KT-13108. You can vote on it, add your use case, and watch it for updates!
To add, custom union types (such as Arrow’s Either type, or Michael Bull’s Result type) can be created with a combination of sealed classes, generics, and extension functions
I like your work thank you!
Thanks so much! 🙂
The fact that you even considered the idea of just using the "Any" type is really bad, especially that it took up a major part of the video.
My goal in each video is to lay a solid foundation of understanding for the concept, and that doesn't happen when you simply jump to the "best" solution. It requires wading through worse solutions, talking through the problems with them, and gradually refining them toward better solutions, so that by the end we understand why each solution is more optimal or less optimal compared to the others. If you can't spare the three minutes that the Any section took up in this video, that's fine, but future videos here will have a similar pace.
Its not a perfect or even good solution but its a solution.
You cant solve everything on compile time, so solving things on runtime is still a little part of state of the art.