Published earlier today: <https://github.com/Kotli...
# language-evolution
e
👏 3
🎉 6
K 8
👏🏼 1
@elizarov although this feature would open lots of possibilities, there are also the risks mentioned on the proposal. I'd like to know how the release/evaluation process would work. The team is working on a prototype and then this prototype will be released as an experimental feature following the standard release process? Or will it be released separately? Also, how certain the Kotlin team is about this feature becoming part of the language? Can it be "rejected" after gathering feedback from the community? I just want to understand what follows...
e
We’ll gather feedback from the community first. There might be more risks or opportunities we do not see yet. At some point we plan to release a prototype under a flag, so that we and community can evaluate it in practice to see how all it plays out in real code and what is the net result.
👍 2
r
I’m really uneasy about this one. I find extension methods easy to reason about because I can map it easily to methods on a class or interface, which implicitly have access to all the members of
this
, but I’m not looking forward to trying to understand other people’s code written with multiple receivers / context.
e
I feel the same way. I constantly have to remember my colleagues to avoid using extension functions for everything, so there’s already potential for abuse. Now, with this feature, this potential increases. I know it’s more of a programmer discipline problem than a language issue, but unfortunately less experienced programmers will always exist and this is the reason some people prefer simpler languages, like Go and Java, as they force “dumb” code that can be understood by everyone. Considering Kotlin is targeted towards the enterprise, I think this diversity of experience must be taken into account. Anyway, the only way to know is to see how it plays on real projects, and that’s why I think it’s very important for this feature to be released not as an experimental feature in the same way of other experimental features. Glad the Kotlin team is releasing a prototype first.
e
I think we know a general approach to addressing this kind of issues. We can present "context receivers" in our learning materials as an "advanced feature for DSL and API authors", as a something a general developer should not be worried about or using much. I mean, we could put up an even higher barrier somehow, but just a proper positioning might be enough to do the trick.
👍 3
🔝 1
r
Apologies for the negativity, but one of the things I like least about Scala is the volume of “advanced” libraries I find it very, very hard to read. And back in the day Groovy DSLs were also a nightmare to explore & learn by using IDE code completion & reading the code. One of the things I really like about Kotlin is I’m generally pretty confident that if I follow a hyperlink into the code of any open source lib written in Kotlin then I’ll be able to follow what’s going on. Reading the code is my preferred way to learn what a library does & how it does it.
👍 2
👎🏼 1
e
I think this distinction of application vs. library development could be a good way to go. It’s kind of a premise of library development to abstract the complexity so clients don’t have to deal with it. This implies accepting more complexity and needing more advanced language features. Also, making these “library-developer-friendly” features something separate could help keep simple what is known as standard Kotlin. For example, project Lombok added powerful capabilities to Java, but it used some very complex bytecode manipulation and so many developers chose to stay away from it. Nevertheless, its existence didn’t affect Java’s reputation (I think) because it was an external library/tool. That’s one reason I really like compiler plug-ins: they add powerful capabilities to Kotlin while not being part of the language. However, I don’t have any ideas how this feature could be restricted.
e
This is gonna be very well welcome for 3d api bindings, where I need to have receiver for identifiers (objects) and native memory scopes
âž• 2
u
As described in the proposal, this has many similarities to Scala's
using
/
given
mechanism. But unlike Scala's similar mechanism, doesn't this potentially run into problems when there are multiple instances of the same generic type in the context? E.g.
context(Monoid<A>, Monoid<B>)
. I don't think I can use the
typealias
workaround mentioned in the proposal to resolve the label ambiguity here.
e
Monoid<A>
and
Monoid<B>
are different types, and there should not be any problems for a type-system to distinguish between them. However, assuming that
Monoid
declared
id()
function, you will not be able just to all
id()
there, since there will be an ambiguity of which Monoid’s
id()
function to call. The only workaround we propose at the moment is to declare a typealias and use it with a qualified-this expression to disambiguate the call.
u
Yes, but I guess I could declare something like
typealias Monoid1<X> = Monoid<X>
and
typealias Monoid2<X> = Monoid<X>
and then declare
context(Monoid1<A>, Monoid2<B>)
. It's a bit tedious, but it works, and it is still nice to be able to have a context with monoid instances for different generic types. That is not possible with receivers because the type system does not allow me to say
object MyInstance : Monoid<A>, Monoid<B>
because of type erasure.
What's the advantage of the form
context(Monoid<A>, Monoid<B>)
over
fun foo(using monoidA: Monoid<A>, using monoidB: Monoid<B>)
?
f
The reasoning is that the declaration syntax will be different from the usage syntax: if they are not passed as parameters they should not be declared as parameters.
u
But they are parameters, except that they are implicitly resolved from the context. It just seems unfortunate that one has to essentially apply hacks via
typealias
because the type name also serves as the binder of the context receiver object.
And if you write
fun foo(using monoid: Monoid<A>)
then an instance of
foo
in a context where there is an instance
myInstance: Monoid<A>
could be seen as a specialization of
foo
with the signature
fun foo(monoid: Monoid<A> = myInstance)
. This could be shown as a hint in intelliJ, which I as a programmer would welcome, as it would tell me directly how implicit parameters are resolved.
f
But they are parameters, except that they are implicitly resolved from the context.
They are parameters, but they are not passed as parameters. This is why declaring them as if they are passed normally is syntactically inconsistent.
It just seems unfortunate that one has to essentially apply hacks via
typealias
because the type name also serves as the binder of the context receiver object.
There are ideas in the proposal to add a
context(rec: Receiver)
named context syntax, but currently they don't find enough use case for it. After all you're only supposed to use it as a context, not as a normal parameter.
This could be shown as a hint in IntelliJ
Yes, that could be an upside of your idea, however I feel that a hint (looking different, but) serving the same purpose can be implemented with the current proposal.
u
I am not sure I agree on the premise that it should not be possible to pass them as parameters. After all, they do not bind
this
, so they are much closer related to parameters than regular receivers are. I understand that this style may be preferred for consistency with the rest of the Kotlin language, but otherwise I don't think I see the advantage of this style over something like Scala3's
using/given
approach.