Ben Woodworth
10/01/2020, 4:59 AMinterface NumberExtensions<T> {
fun T.squared(): T
}
@Given
object IntExtensions : NumberExtensions<Int> {
override fun Int.squared(): Int = this * this
}
class MyClass(
/*with*/ val intExtensions: NumberExtensions<Int> = given, // Something like KEEP-87's 'with'
) /*: NumberExtensions<Int> by intExtensions*/ { // Without needing to delegate like this
fun printTwoSquared(): Unit =
println(2.squared()) // Unresolved reference: squared
}
raulraja
10/01/2020, 10:12 AM.run
as Kotlin may bring multiple receivers and it’s a more idiomatic way to deal with that problem in your particular use case.
What meta brings for actual syntax projections is @Coercion
and @Extension
which automatically project syntax between types. Your example with @Extension
may look like this.raulraja
10/01/2020, 10:13 AMraulraja
10/01/2020, 10:14 AMraulraja
10/01/2020, 10:14 AMCoercion
instead of Extension
it would be the same but you can also do:raulraja
10/01/2020, 10:15 AMval x: NumberExtensions<Int> = 1 // desugars to 1.extensions()
Ben Woodworth
10/01/2020, 5:55 PM@Extension
and @Coercion
a bunch too, and they're definitely very powerful features. For my specific use case though, I'm a bit wary of using wrappers to add functionality.
I originally had classes that wrapped types to add functionality like your NumberExtensions
class (which would've worked perfectly with @Extension
), but I ran into performance issues with that, since at times there were tens of thousands of instances to be instantiated each second.
I switched to using a typeclass interface with extension members (like my NumberExtensions
interface), so only one instance needs to be created, and that improved performance significantly for me. I think something like a @With
annotation to bring the extension members into scope would work really well in my current code. The interface delegation to bring members into scope is working well for me, though it does add/expose unnecessary class members.
Here's an example of my old code, and here's the refactored current code. I'm not sure that my current code is the best approach, but I am still wary of creating unnecessary wrappers because of the performance problems I had, especially when one instance to add functionality could suffice.