So, here's my take on the current experimental sta...
# compiler
o
So, here's my take on the current experimental state of Context Parameters! I love them. But I got some annoyances to talk about. When converting all my Context Receivers to Context Parameters, 90% of all the time, I had to use
_:
because my "top level" functions do not directly care about the parameter name, they do not call anything from those context values. All they do is call lower-level functions, usually bridge functions I'd say, that specifically need the parameter and give it an explicit name. Now, seeing that my most used character in modern Kotlin will be the
_
, I wonder whether from a language design perspective it would be more beneficial to allow all context parameters where the name is irrelevant to be simply stated just like context receivers where before: without any name spec in front of the type. Just like even now a type alias works. Type aliases, interestingly did not change at all coming from context receivers! I think it might be done like this because right now coming from context receivers it is not a good idea to allow the same syntax to mean different things, but on the long run: would it not be possible to omit this unnecessary, cumbersome piece of
_:
everywhere?
a
the short answer is "yes", we may be able to omit the
_
once the transition from context receivers is done on the other hand, having a name in front of the context parameter may allow us to provide better syntax for function with context parameters, for example, allowing to provide the context as additional (named) parameters
something we're investigating is whether the usage of
_:
is linked to those who used context receivers in the past, or also happens with first-time users of context parameters
o
Hm, I am pretty sure that the use case that you want to only define the necessary components for a call site but not wanting to name your context parameters is completely valid and will happen a lot. Take Arrow. Whenever I define a function with Raise context, I will want to put a
context(_: Raise<E>)
and then call bridge functions. This is currently the most used part in my code base after the transition. And it looks awful. Just the way Kotlin never wanted to look like. 😉 If you have small functions that compose bigger functionality, there will always be a level of higher level functions that simply call other functions, and all they need is a context parameter without a name. But hey... I am just a user, not a programming language design expert... 😉
This "superpower" a function gets by adding a context as in Arrow's Raise DSL is that by adding the context, you are able to call certain functions. Just like when we used Context Receivers. People do not want to name the Raise context and call things like
raise.ensure(...)
- at least that's what I think...
What would a first time user do when facing this problem? Without unnamed context parameters, the beauty of such top level functions with use restricted to certain contexts are not available.
a
Raise is a tricky case because you really want to make it feel as part of the language, so you want to use
raise(...)
without qualification. However, many other cases of context parameters may involve some sort of service, in which you often want to calls functions from that service:
Copy code
context(users: UserService)
fun UserId.getFriendNames() {
  val friends = users.getById(this)
}
o
I know, I know. But in order to allow libraries like Arrow to make users "feel" it's part of the language, it would be really cool to just change the syntax for unnamed context parameters to just be (like before in context receivers)
context(Raise<E>)
reading "to be used only in the presence of a Raise context". It's tiny, but I really felt annoyed by putting so many useless
_:
into my code. This was the very first time in my Kotlin experience for years where I felt I needed to add boilerplate stuff.
in combination that's also fine like in
Copy code
context(Raise<Error>, users: UserService)
fun UserId.getFriendsNames() {
  val friends = users.getById(this)
}

...

interface UserService {
  context(Raise<Error>)
  fun getById(userId: UserId): User
}
👍 1
Is this something the language team "knows" and discusses or should I get active, Alejandro? 😉
a
it is something known, and it's actually in our plans to investigate this shorter syntax once the receiver->parameter transition period is over 🙂
❤️ 3
o
I think it's perfectly okay for now given that everything is experimental - but it would be the final cherry on top if this would be a syntax option for the stable feature!