Context parameters in Kotlin are implicit function parameters that are automatically threaded by the compiler, reducing boilerplate while maintaining code readability. The 'spotlight principle' guides API design decisions: receivers should be main characters (central to the code's purpose, like builders), while context parameters should be secondary characters (dependencies that are needed but should remain unnoticed). For dependency injection, use context parameters; for builders and DSLs, use receivers; for effects like test scopes, use invisible context parameters with underscore names. This design philosophy ensures that code remains focused on its primary purpose while dependencies are handled implicitly.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Context Parameters and API Design | Alejandro Serrano Mena
Added:I'm Alejandro.
I work on the Kotlin language evolution team, which means I tell my family that I am a coder, but that's just wrong, but that's I guess that that's the the closest thing to be. And today I would like to talk more about about one of the features that are coming in the new release of of Kotlin as a stable context parameters. This has been If you follow kind of the the the Kotlin news, this has been something that has been been talked about for years.
Last year in 2.2, it finally released as experimental and now it's a stable, which means that all of you can use it and hopefully more and more libraries will make use for it. And it's one of those features which tries to cover a variety of use cases with one single feature. And in particular, when you have what we call an implicit value. And I will go in a moment about what that means.
So, let me give a very very brief introduction of what context parameters are.
The first thing is that context parameters are in a sense just parameters, right? You put them on your signature, you declare them. This function now has an extra parameter, which is called users and it's of type user service.
And you use it as any other parameter, right? You just write the name and that's it. You just access the parameter.
So, what makes it different from regular parameters?
Well, it's all about how you call functions with context parameters. And here I have an example in which the summarize function is calling the get friends function. And if you look closely at the get friends call, the one on the bottom, you see that we have we are just passing one argument. We are just passing the user.
The the the user service is actually going to be resolved by the compiler by their type and it's going to be automatically threaded to the other thing.
Um and this is what we call an implicit value. It's a value that it's existing.
We know it's part of the function, right? It's a parameter, but in most cases we don't actually see it. It's just there. It's part of the context.
That's where the name of context parameters is coming from.
Now, you can actually if you really want from this version you can also make this explicit. That's cover a few corner cases where you actually need to be explicit about context arguments, but in most cases if you're using context parameters that that means you declare them at the at your signature. You just put them at the top and then you completely forget about it. The compiler is the one making the job of passing the arguments for you.
Now, at some point we need to provide the first thing, right? We are passing the argument if there is another in the function, but who who gives me the first user service?
Well, for this we have a function called context and this context is very similar if you know about it to with. It put something as an implicit value in the context. That means that here inside the block of the context we have an extra context argument, an extra implicit value of type in this case DBUserImplementation and the compiler then if you call summarize will be uh clever enough to say, "Well, this is a a subtype of the thing I need. I will give it and I'll start the domino effect of threading this around your functions."
Uh by the way, if you use IntelliJ, I guess from 2026.2, uh you will also get an inlay hint if you if you like those uh in the same way that if you have a new receiver you can get this and blah blah blah. We also have the same thing for context.
Now, the fact that you can use this context to inject anything that you want gives you actually one additional power, which is that you can refine context. And give me Let me give you an example. Here we have again a function which uses this service, but also uses a logger.
And then we do the typical thing, right?
We want to save the user somewhere using our search user service, but then we have some non-critical section, right?
We are sending maybe some analytic to some other analytic service, and this is not as important as it is, right? So, maybe we want to log with a bit less uh importance. We want to say that we only want to log if really, really something goes wrong in this analytic section. And for this, what we want to do is we want to replace the logger, but only for this section. Well, this context function allows us to do this. We can just take the logger that we have with just by using its name. We can change whatever we need. Imagine this is a data class.
We just copy it and say that the now the minimum log level is critical, so please only log critical events. And then on this section, we're using an another logger, which means that well, something different will happen in this case.
Good. So, the whole idea of this feature is actually to reduce boilerplate. So, try for you to write uh as less code as possible, but without affecting readability. Uh in a sense, it's all about reducing the noise on your code. So, you just have to focus on whatever you want in this piece of code.
But actually, there are other things in the language which look very similar to context parameters. And the main thing is extensions receivers.
Because many people may have thought, well, I have my user service. I know a way in which I could have written the code at the very beginning of the slides exactly the same thing ex- exactly with the same idea of something is being threaded by the compiler. Uh and I don't need anything new. I just need these beautiful extension receivers, right? Uh so, what's the difference? Why uh do we want to have these two features in the language? What's the difference between context parameters and receivers?
And even if I convince you there is a difference, how do you choose between them, right? And that's a very important uh question for everybody writing code in but especially for library authors because the way you choose shapes a lot how people use this library you use, right?
How it has kind of a compound effect on on your users.
So, to do this, I like to think it That's how code looks in my head. I call it the spotlight principle, but the the idea is that in my mind, code is like a movie, right? So, everything that happens is just a scene in the movie. And as in any movie, we have main characters and we have secondary characters.
Uh so, I I like to use this movie I I don't know I guess that point I should ask whether people know the the movie Grease.
Uh raise your hand if you know the movie I if you watch the movie.
Okay. If you have song, that's it what I want I don't know uh uh uh uh uh uh uh uh uh uh uh uh uh Yes, you see, these are always different people, the people who sound the sing the the things in karaoke and the people who watch the movie. Anyway, uh you know, you have here uh the the idea like John Travolta and Olivia Newton-John, these are the main characters. These will be the receivers.
And then you have all the other people, the context parameters.
And the thing that, you know, in this movie, every time that any of them feels like a feeling, suddenly these people appear from nowhere, right? They are there. Oh, they are there, too. Woah.
They are there. Now, they we have to sing a different song. They are there, right? And And that's the idea like context parameters are a bit like the secondary characters, right? They move from a scene to a scene and the whole idea is you don't notice them, right?
You need them because if it was just John Travolta singing, that would be very, very sad, I guess.
Uh so, you need them, but you want them to be unnoticed. They appear when you need them and that's the idea we have with context parameters.
Right? You want to have this code, but the fact that you have a user service is completely oblivious to the fact that you are summarizing. You need it, of course, because you need to have the get friends, but the important part here is you are getting the get the friends from the user. That's what your code is doing and that's what really is of importance.
Now, one thing that happens is if imagine you have to talk to a secondary character, right? We need to like say, "Hey, Terry." Or yeah, you know, or if you are in a in your typical sitcom, they have to remind you that oh, "Hello, Terry, brother of of this." So, so, right? And that's that's kind of something which also works for context parameters because as I said, context parameters are parameters and they must be called into action. You have to explicitly talk about them, right? If you at at some point are using the user service, then you need to write users.find friends by ID. You need to call this parameter specifically into action.
Now, the thing is that we want to do this again so that our code in our code really the main characters get the spotlight.
And with that, I mean that imagine I am writing this code. This is the code I've been showing you. Uh we have the we want to get the friends of the user, summarize the user.
But if we really want to write code with which is very idiomatic for Kotlin, what we would like to do is to make this user the receiver, right? We we want to put it at the front and that's actually one of the things that context parameters allow us to do. It kind of the couples the idea that you have some contextual information from the fact that this receiver has this very special role in your function, right? It's the thing you are talking about. It's the It's the main character of this a scene.
Uh because again, that I think you you have followed at this point that the idea is that the receivers are the main characters. Those are the ones which get the spotlight. Uh we will talk about actually about one slightly related thing, which is a scope pollution, because in a movie also, if there are too many characters, the scene is confusing. Like I love Ocean's 11, but I at some point, yes, they are like robbing something, but who knows who is anybody, right?
There are too like too many people. We will return to this problem later.
Um so, bear with me for a while.
Good. So, at this point, a reasonable question can now have turned into Well, hopefully, you already know enough difference between context parameters and receivers. And the way you want to choose between them is, well, at every at every point in your in your uh program, how do you choose who is the main character and who is secondary character? And for this, I want to go through different uh scenarios.
I think there are at least three different patterns that can happen in your code, and each of them we are going to try to reason with this idea of the spotlight principles, whether our thing should be a secondary character, and that means it's going to be a context parameter, or it's rather a main character and should rather be a receiver.
Okay. Now, so, let's begin with injection.
I don't want to say dependency injection, whatever. Just You want to get some some value from point A to to point B, right? You you want to do this threading.
Uh or in other words, you somehow want to isolate the class from its dependency, which will be provided at some other point, maybe at some entry point from your program.
Well, this is the perfect example actually of a secondary character, right? Because they just you just want to move them around. They should be completely unnoticed and only when you need it, you want to bring it there. So, this is one of the actually main use cases why we brought context parameters.
This kind of brings you safety to this idea of having a dependency injection around your program.
So, just just to show this in this in this example, again, here user and user services are kind of the dependencies of this function, right? These are things that again don't tell me exactly about what my code is doing. That's That's uh the code inside, but are required for my code to run. These are the dependencies and the context function is the one that actually injects the thing.
It's It's the one which is really appearing at the entry point and will inject the values there.
Now, one question that usually arises when you start doing this thing is that sometimes, I mean, in most cases in which you use dependency injection in every program, you often want more than one value, right? You don't only want the user service, you may need an order service, you might need a logger, you might need an HTTP client, and all of these you want them to have as dependencies. So, one question is maybe how we can we thread all of them in a convenient way. Well, nice thing is because these are parameters, well, you can just have more than one. Nobody's stopping you from doing this. You can just put them as different parameters and all of them will be threaded around for you.
So, that's that's the first thing. And this this actually works quite well in practice, at least for the for the uh projects in which we have applied context parameters. This seems to do the job reasonably well and that's what I would suggest if you want to use context parameters to start with. It is very explicit in the dependencies. You always say in each function only what you need because in this function maybe you need a different thing from another one and you want to be explicit about this because then the compiler can help you more. It's relatively easy to take one of them and then change it as slightly as I showed at the very beginning.
But there is a problem. If you add a new context you have to change a lot of signatures.
And with it I mean that well, you have this thing where you save the friends and this is called by save which is called by repair, who knows? And then suddenly you start requiring a logger for any reason. Imagine any other dependency. That means that this code is now red, right? Because the compiler says, "Well, there is no context argument. There is no value I can find in my context for this logger." Okay.
I will put it there. And then we go up one layer, right? Oh, there is no context argument for logger. So you need to go up and up and that's kind of a domino effect that can be annoying.
So I I see what some people are thinking. Okay, what if we have just one single thing which has everything, right? We just put a a context which it has all the dependencies and we just put everything there, right? That that would solve our problem because we will just change the dependencies and we'll change the entry point.
Well, I mean that's that's kind of just packaging unrelated values together.
It's it's it's more or less like a god object which just has a bunch of information about the bunch of things, but it like there is no single responsibility of what this dependency does other like threading stuff. And you are also losing a lot of information, right? Because uh you know, these dependencies at the bottom is actually not telling you what the dependencies are. It's just telling you that okay, you depend on this big thing, but at this point it's no better than well, just having another argument or whatever. But the goal is here we want to be explicit so that everything can be better checked.
So, to be clear, like Cody is perfectly fine with having a class which puts everything together to pass it around.
That maybe would be even more efficient in some platforms than just passing single different arguments.
The problem is in the bottom, right? We still want to make the dependencies very explicit because of this single responsibility principle.
So, there is a pattern and I want to introduce it to you, but be reminded that this is actually quite heavy. We tried to apply this actually inside the Kotlin compiler and it's like it's heavy and and and you may think twice before doing this, but it is we can decouple the precise description of the dependencies from the way in which they are provided. With that, I mean that we can create several interfaces. So, our dependency is going to be something which implements several interfaces and each of these interfaces will have a single responsibility. In this case, the user service holder will be an interface which is responsible of providing an user service. And then when we need a user service, instead of just going directly to the user service, we will introduce another indirection because remember everything can be solved by another layer of indirection.
We will just have another indirection to get it inside our context. The good thing is that now we can just introduce one for each of them and make our dependencies thing implement each of these things, right? So, we are decoupling the notion of kind of providing this particular dependency from the way we're actually implementing this, which is just putting all of them together. Maybe that's better from the entry point and again getting all of those.
I found that this works especially well if you have some kind of tree of context because then what you can do is kind of have a very leaf services and then say well this this DB dependencies aggregate some of those and then if we put it with the logger we have bigger dependencies. And this also works very well if you try to do this with interface delegation because what you can say is well if my dependencies use the DB dependencies, I don't actually need to go and implement everything from DB dependencies myself. I can just go and delegate and say the user service holder and the group service, you will get it from the DB and that's another place where uh the Kotlin compiler is clever enough to do all this writing for you.
Good. But maybe being more practical, right? I've spent a lot of time actually telling you about the holder pattern and which as I said you should think twice before use her before using. Uh the practical advice is that the best thing you can do is actually to start just with a regular context parameters if you want any of this. If you feel that you have multiple dependencies, just uh start by having multiple context parameters. This usually works very well in practice.
And if you want to be able to detect when you need context parameters, for me the easiest way is to look like kind of the end of the name. If something is called service or is a some kind of configuration, something like that, that's usually a good chance that you want to have this as a context parameter.
Good. So, let's go to uh the second case which is invisible context or what we will call also effects.
And let me show you an example here.
Like let's dream of an API. This is an API for testing and the thing is that we want these functions should be null and should be to be simply available. We want to be them magically available. I can just call them every time that I am in one of these test scopes. Kind of the test scope is the one telling me that I'm doing test but inside the function I really want it to be kind of uh as the air right? Or the water for a fish. Just something which is there as if it was in the language in this small piece of code.
Right? So, here what the context parameter is doing is mostly describing an a scope, something where something is is now available, or maybe what some people can call a capability, something that you can now do that you were not allowed to or able to do before.
So, in this case, we have uh something in the context parameter design, which is a name context parameter. Is when you don't really want to ever use the context parameters by name. Uh like here, you just want to use them to signify that something is available in your scope. And here you have, for example, the the signatures of functions like should be null or should be. You can see that we have the context with the test scope, and we just have an underscore because, as I said, we don't really want to refer them by name.
Uh the thing here is that, as you can say, they are never in the spotlight in the sense that we never refer to the test scope itself, right? We never have uh the idea that it is there. It's very auxiliary to what the code is doing, and usually provide kind of very, very generic facility. So, most examples of invisible context are like a a test scope, right? We are all writing tests, right?
Um or something like a race effect. I don't know. Uh things which also call a scope, which also again means that something is available now.
Uh the good thing here is that uh whereas using invisible context could be quite common, and we expect that as time passes, people will use this pattern, and that means that users will use invisible context more and more, defining invisible context is pretty rare, right? It's pretty rare that you are the person defining one of these very generic facilities. I mean, maybe here people writing uh test libraries are overrepresented, but you know, most of the people use write tests. They don't write test libraries. And why I say that's a good thing is because actually doing all of this is kind of a lot of work. You need to follow a particular pattern. Uh we call it the bridge pattern, which conflicts with like the usual bridge pattern thing, but we didn't find a better name. Uh because what you want to do is in this case is to define an interface, right?
At the test library author needs to define an implementation of all of these methods, and the way we are going to give the implementation of this method it's a class that implements an interface, right? And this interface is going to be like in this case our test a scope. So, this interface is usually very small. It has a very core small API, and what we're going to do is to re-expose this library using this interface using context parameters, and then build a larger API on top of that.
And I know this sounds completely abstract, so let me show you the example for the test a scope, right? So, here is going to be our core functionality of this test a scope, right? This test a scope is going to uh have a function which says this should be true because it on top of this should be true we can build everything, right? It It should be equal means should be true that these two things are equal.
Uh should be not null means that should be true that this thing is not null, and so on. So, this is going to be like the very very very very core of our API. So, as I said, we're going to define it as uh as an interface in the usual way that we often define interfaces, and then we are going to write the bridge method. And the bridge method is just re-exposing the interfaces as if we would like uh as if we would like it to be, right? As if they were context parameters. So, in this case we see that the scope the test a scope is now a context parameter of the function.
It's no longer a receiver of the function, and then we have the sole should be true.
But, this should be true is only going to be available whenever it we are in a tester scope. And these bridge methods always look in the same way. You just take the take the the context parameter you have, and then you call the function from the interface. I'm I'm trying to put here actually some colors to make it a bit more clear. Right? So, the the API we want is the purple should be true.
And but this purple should be true, in principle, only lives on the tester scope, which is the the red thing. So, what we do is we go through the red thing and call the should be true version from this interface, which is the green thing. So, it's often the case that we we will use the same name, but the good thing is after we do all of that, we just completely forget the interface from the public API. That's now going to become an implementation concern. That's the way we're going to eventually uh pass the the tester scope when we are actually running the test.
So, for the users of our testing library, all that we need to know is that this should be true exist. And on top of that, we could do whatever we want. We could write should be null, and then as asset should be null is essentially well, it should be true that this is not null, and otherwise pass this message, and so on and so forth.
Uh so, this invisible context, that's how I prefer to think of it, but they're very related to this notion that exist in in many programming communities of effects. The idea there being that you want to somehow separate every you want to somehow mark everything which is not pure computation. You want to say that maybe this function is doing some IO, for example, and then they define effects for IO, or is doing some networking.
This is actually something you can do with this kind of of context parameters.
You can do some kind of effect tracking.
It's not going to be It's maybe it's not going to be as easy as in Haskell or Scala, but you can really use them. And And if you're interested on this whole idea, I actually uh talked some time ago for a couple of hours about how you can do this in this in this uh in this other talk, Pure Functions, Powerful Effects, where I kind of describe how to do this in Kotlin. Uh but in general, if you if you know what effects are, you can bring some of these ideas, but just remember that that we want to use them pretty rarely. Like context parameters are not optimized for the effects use case. This is something you can do, but you need to put some work, especially if you are a library author.
Good. So, we have two out of three. Oh, yeah. I'm sorry. My practical advice is do not do this.
If you think that you need an underscore in your in your context parameter, really think twice. This is only for very generic effects. Actually, having a name in your context parameter is good.
First of all, documentation, but second, that allows you to talk about it, refine it, and use explicit context arguments. And uh what is often the case is that people have been following this context parameter kind of uh uh thing for years. And before we have something called context receiver, which worked more similarly to effects.
And what you often see that is that what in context receiver was the main use case, using this kind of effect-like uh things, becomes kind of an antipattern here, right? So, we inherit this kind of pattern, but it has become more now of an antipattern right now.
Good. So, let's move to the third the third case, which is builders and nesting.
And again, let me start with an example.
So, this is a function that our standard library will uh like and love, at least I do. Uh build list and build list allows you to have a block, right? So, again, we are thinking of this scoping effects thing.
And inside inside this building's block, what we can do is call add, and at the end So, and every everything we add at the end of this block will be given back to us as a list, right?
So, the question is how does all of this work? And uh and to understand this, we need to look at the at the signature of the function. What you can see is is that build list takes a block, which is itself a function. That's why when we call it, we give a lambda, kind of a an anonymous uh function block. And inside inside If you look at the function of this, we have mutable list of T as a receiver. That means that when we are calling this function inside this block, this mutable list of T becomes the receiver, right? Now, this refers to the mutable list of in, which means that when we call add one, the compiler will try to resolve this and say, "Oh, what is my closest receiver? The mutable list." So, that means that this add is from the mutable list, and well, the build list at the end will just give you back this this list, right?
So, one question is, you know, context parameters are here. Should we just change our whole uh standard library and use a context parameter here instead, right? It's Yeah, I don't know. We are hyped about it, right? So, we should use it everywhere. As is As in anytime that you think, "I should use it everywhere." the question is the answer is no.
Because let's think about it again from this idea of of who gets the spotlight.
If you are inside the build list block, what is the main goal of this thing?
What is happening in this uh scene? I am building a list, right? Because otherwise, I would be choosing some other kind of pattern, right? I will maybe If if my list was some ancillary thing you do, you often put this on a on a on a val on a mutable list and then you refer this directly. If I'm going to build this build list block, this a scene is about building a list, right?
That's the goal of the scene. So, building the list should take the spotlight and that means that receiver is the best choice for this case.
Uh but the thing is builders are everywhere once you squint your eyes, right? You have build list and build set uh but also functions like sequence or flow from the standard library are builders from sequential structures. But in Kotlin we also often use builders for tree-like structures, right? We have Kotlin X HTML to build HTML or you can have some kind of JSON document which can also be built with the JSON DSL from Kotlin X serialization. And even if you think of something like transaction, transaction have this kind of tree-like structure.
So, that in these cases receivers are the best.
Uh let me also point something which we can do with receiver which is quite important in all this discussion. If we try to write Well, this is an example from the Kotlin X HTML library from the from the readme and you can see that here we are kind of nesting things, right? We have the div and inside the div we have an H1 and the idea is that this all mimics the a structure of a HTML document where we tag uh when we nest tags inside each other.
But if you look at uh how all of this work, what you see is that you actually have a bunch of receivers. Div introduces a receiver of type div and that's going to be the our our red receiver and then H1 and P are called on this receiver. That's actually how at runtime we know where we are coming from, what is our parent tag. And then H1 introduces another receiver, right?
So, the plus inside H1 is now going inside the H1 receiver. And the same from for the for the blue one, right? P introduces yet another receiver and A introduces yet another one, so the plus Kotlin code actually refers to a receiver introduced by A. What you see here is the receivers changed in a nested fashion. And this if any is saying that this is really important, right? The fact that we are tracking this thing means that all this structure is really important. Each receiver shadows the previous one. We We even take this so far that there is some functionality in Kotlin code DSL marker that builds on top of this idea of shadowing the previous receiver. And from a point of view that is a strength as its position as as a main character.
I I I always want to like I just had too many movie kind of relations, so I I I always think of this as the typical villain which just becomes stronger and stronger as the movie kind of progresses, right? So, once once the villain is is a crazy dragon, it cannot go back to be just a poor guy which had a trauma from their childhood, right? It It now is a dragon.
You cannot go back. And that kind of a strength and is is is position as a main character, right? The villain is kind of a main character of of of of movies.
Uh And one thing important here is also if you are not careful with all these receivers, uh one problem that you may have is a scope pollution, right? And and DSL marker is a way we're solving this and contest parameter is another, but think about all the receivers that are introduced in this piece of code. If you follow what you what I've said, diff introduces one, H1 introduces one, P introduces one, A.
So, at the point in which I have the the the space, right? If I would have to now do control a space and try to get the auto completion list, well, the auto completion list will be huge, right? Because we have all the top level functions, but all the functions from diff, all the functions from P and from A. And this is actually one of the main things that led us to changing context receiver to context parameters. There are just too many functions. If everything is a receiver, it's like nobody's a receiver, right? If everybody's important, nobody's important. Each receiver contributes to things and makes everything more difficult to understand without an ID.
If you have an ID, it could be fine because you can always navigate and know what is you're doing.
But if you're reviewing code, I don't know, in a GitHub code review, you may have a a hard time tracking all of this in your head. And this is a a thing that people were telling us, this is kind of polluting the scope. So, that's why it's important that context parameter, in the general case, have a name because then you can always say, "No, no, no, no, no.
This is the get from the users. It's not the get from the group service." Which otherwise will be a problem, right? You will have two gets. Who do you call?
What who you're calling at each point and so forth.
Good. So, practical advice here, of course, is and I think you have guessed, if you have something which looks like a builder, uh start with a receiver. The good news is that going from receiver to context parameter is actually very well supported because we really want that people that were using user service, as I showed at the very beginning, in the wrong way, like using them as receivers when they could be rather be context parameter. We want to support this migration. So, going from a receiver to a context parameter is well supported.
So, that means that if you even if you, you know, if you thought wrong and it's actually not such a builder, this is a something which we take care that you can easily migrate. Uh you can call a function with a context parameter if you have the receiver, but not vice versa, for And and and actually it this happens sometime that you actually need too many main characters and then you have to decide which of them is the receiver and who will will going to go in the context parameter section for this time.
Good.
That's great.
We're almost there.
Just let me tell you if you are a library author anyway, these are guidelines, but anyway, API design is hard. I don't want to say that if you follow these three key everything follows in these three categories and then you can choose. That's not the reality, right? Imagine something like core routines. I have lengthy discussion with my colleagues.
Something like a core routine is an effect in the sense that you want a sync and launch to just be there in this beautiful. On the other hand, they are hierarchical, right? They have a parent-child relationship. So in in some sense, we are building a tree, right? So here is the the magic word builder. So maybe it's good that they are a receiver, right?
And and so that's something which is already hard. The second thing which is hard is that well, you have just one position for the receiver, right? It's not that you can choose that three of them are now receivers and you know, no, you have one position for the receiver and that means that sometimes you make you have to make the hard decision and put some of them into the context parameters. And that's something that happens in real in real libraries.
Libraries that require several things working together will have this problem.
But in general, if you are writing code with context parameters, then remember that the whole idea is to reduce noise, right? To make people reading your code have an easier time doing so. It might be the case that actually receivers are not your solution. Maybe context parameters are not your solution. Your solution is just a regular parameter because on that in that position, it is very important to know what you're passing. And that's important thing.
This is not then reducing noise. It's making more noise because now I have to go to my colleague to ask what's going on here.
So yes, this is kind of the guiding principle for the whole thing.
But just to summarize the whole idea of of the talk is that you can decide whether to use a context parameter or a a by thinking of who has the spotlight, and we have seen three different scenarios, right? We have the injection scenario, where you only want to use the thing in a very, very few places, usually like in the leaves of your computation, and your main goal with this thing is just to thread them around. In that case, context parameters work wonderfully.
In some other cases, the the that parameter is actually central to the code. As As I was mentioning, you are building a list, so the receiver of build list is central to the code. In that case, it's better to use a builder as scenario. And something which I take as a as an important thing which tells me this is really important is when I actually have shadowing of receivers. If which particular receiver am I calling is important to this piece of code, in most cases you want to go with a receiver, because with context parameters this becomes hairier. And then we have the last scenario, the effects. That's very rare, most cases you should just consume it, and then you don't care what you are doing.
Uh but if you are defining uh an API and you want to use this pattern, we've seen that the best thing is to use a context parameter and a name context parameter, and then use uh bridge functions to do this.
So, that's it. Uh thank you, and don't forget to vote, and also don't forget that the whole like almost the whole language evolution team is on a booth downstairs, and we are happy to talk to you about either what we are doing, or you can tell us about we are missing, so we have more work to do. I guess. Uh so, thank you. I'm happy to take questions from the audience.
>> [applause] >> Wait, if somebody has a question, they have to go to the uh microphones, which I cannot see, but they told me they are there. Huh. Or you can just go.
>> Yeah, it's working.
Uh Yeah, uh hello.
I'm over here.
>> Where are you?
>> In the middle. In the middle. Hello.
>> Hello.
>> [laughter] >> Um so, uh one question that I had or one thought that I had was about optional context parameters. So, to use one of your examples, you had this logger as a context parameter. Now, I was thinking, okay, what if I'm in a part of the code where I don't actually want to log? Um if maybe I can define a context parameter that is of type logger {question mark} and then the compiler checks, is there uh a logger in the context where I'm calling or isn't there? And if there isn't one, then, you know, the function can still be called, but the logger will be null. I- is that something that you considered, discussed, discarded?
Uh yeah, so uh in that case, there is So, one trick you can always play is to put null. Like, you can make uh the logger just an optional like a a nullable type. That is completely supported that a- any type you can do.
And then, one trick you can play is to say, I I want to put in the context null as logger {question mark}. So, you are explicitly saying this that you want this thing. Uh the case in which in which you have uh uh an a really optional uh context parameter is actually not supported and we made a decision not to support it because it made resolution very difficult to track. Because all of this is implicit, it's very hard to know where you're calling. In particular, it's very hard uh to understand the cases in which you really wanted the logger to be threaded around and it is not. And we didn't want people to go into that direction. We didn't want people to have to think very very very hard about uh who the the thing is being threaded. So, we decided that that at least at this point, optional context parameters are not supported. You are allowed to define uh two uh Yeah, And you're not even allowed to define two functions which have one which only has one thing in the context and the other one has the context and the logger. That this will be a resolution ambiguity. So so we went as far as saying at this point we really want all the freedom to be very very very uh simple. And uh but uh as you can see we are bringing some ex- experimental things on top of context parameters. So it might be the case that in the future we do something like this if if if it turns out that this is a pattern that people uh find themselves into very often.
Okay. Thank you.
So hello hello. Uh thank you for the talk. Uh I actually initially wanted to ask the same question but luckily I have a second one. Um So there's no relation between these new context parameters and the already existing coroutine context, right? Uh because when first looking into this um I kind of thought that they were and was very confused when they're obviously doing different things. Okay. I I So the the the basic answer is no.
Like no, these are for different things.
Like the They are even They They work in a different level in the sense that that coroutine like context parameters reflect kind of the lexical environment of your code. They reflect how you organize functions. Whereas coroutine context reflect how you organize coroutines, right? They depend on the tree type relationship not all the functions but on of the coroutines which of course in many cases are the same, right? Because we use coroutine scope but they don't need to be. Now there is something we are working on which is that one thing you can do is that every suspend function has a coroutine context. So we are working on something which will automatically inject coroutine context if you are in a suspend function because this has some uh utilities for people who are uh who are using the coroutine context to put some information inside. So, that's something like an extra we are still working on. We hope to have it uh ready soon. Like the keep is is already published. So, yeah.
Okay, thank you.
Hello. Hello. Um I must say, well, thanks for the talk.
And I must say as a Scala developer, you are giving me a little bit of PTSD with uh implicit.
I wonder how much did you see this as a controversial thing to add?
>> Uh how did you decide to do it?
>> let me preface this by saying that I was a Haskell developer for a long time. So, I have my enough trouble of type class resolution. And I actually like like uh so, I I I see you. That's what I mean.
That that I'm I didn't do Scala, but I think that's close enough, right?
Uh so, the main thing here is that what what context parameter is not doing, and I was kind of emphasizing this on the previous answer, is we are not doing complex resolution. We want that you know very easily where things are coming from. And if we don't know what things are coming from, it's an ambiguity, or we ask you to be explicit about it. And for me, that's the key difference here, right? That means of course that we cannot use context parameters for EQ. We cannot use it for type real type class resolution. But that also means that there is no type class resolution in Kotlin, which is the main problem here, right? You see that if you go to the IntelliJ Scala plugin, there is a special thing just to show you how things were resolved because this thing is hard. And uh we think that this is the main thing that makes things hard. If we have the implicit passing, that's great, but let's stop at very simple boundaries, and let let's not go into resolution which has to look at all the instances and maybe needs to be guided. So, hopefully, that gives you the good thing without all the uh trauma associated with it. That's that's the idea of the design at least.
Okay, thank you.
>> [applause]
Related Videos

TOP 15 Data compression Interview Questions and Answers 2019 Part-2 | Data compression | Wisdom jobs
wisdomjobs
281 views•2019-06-28

CTS 158: 802.11w Management Frame Protection
ClearToSend
4K views•2019-02-04

NDSS 2019 Send Hardest Problems My Way: Probabilistic Path Prioritization for Hybrid Fuzzing
NDSSSymposium
496 views•2019-04-02

How realistic is Cities: Skylines?
CityBeautiful
159K views•2019-02-14

GUIs & TUIs: Choosing a User Interface for Your Python Project | Real Python Podcast
realpython
2K views•2025-04-04

The OSI Model - Explained by Example
hnasr
225K views•2019-05-12

Cloud Computing - Introduction
elithecomputerguy
98K views•2019-10-07

From Traveler's Dilemma to Dynamic Routing | Demystifying Networking
IITBombayJuly
5K views•2019-08-04
Trending

Playstation NO DISC/NO BUY Fight Is Over...
DavidJaffeGames
4K views•2026-07-23

Americans Confused in Australia for 17 Minutes Straight
IWrocker
17K views•2026-07-23

Bitcoin Social Interest: Dozens of us Left
benjaminjcowen
12K views•2026-07-23

Tesla Profits Plunge & SpaceX Stock Continues Fall
TheJohnJohnstonLounge
6K views•2026-07-23