Skip to content

Commit 83b835c

Browse files
committed
More technical updates
1 parent d6bdc74 commit 83b835c

File tree

1 file changed

+37
-13
lines changed

1 file changed

+37
-13
lines changed

proposals/context-parameters.md

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ This document is not (yet) formally a KEEP, since it lacks some of the technical
4040
* [Technical design](#technical-design)
4141
* [Syntax](#syntax)
4242
* [Extended resolution algorithm](#extended-resolution-algorithm)
43+
* [Extended type inference algorithm](#extended-type-inference-algorithm)
4344
* [JVM ABI and Java compatibility](#jvm-abi-and-java-compatibility)
4445
* [Q\&A about design decisions](#qa-about-design-decisions)
4546
* [Acknowledgments](#acknowledgments)
@@ -52,6 +53,14 @@ This document is not (yet) formally a KEEP, since it lacks some of the technical
5253
* Within the body of the declared member, the value of the context parameter is accessible using its name, similar to value parameters.
5354
* It is allowed to use `_` as a name; in that case, the value is not accessible through any name (but still participates in context resolution).
5455

56+
```kotlin
57+
context(analysisScope: AnalysisScope)
58+
fun Type.equalTo(other: Type): Boolean = ...
59+
60+
context(analysisScope: AnalysisScope)
61+
val Type.isNullable: Boolean get() = ...
62+
```
63+
5564
**§2** *(restrictions)*:
5665

5766
* It is an *error* to declare an **empty** list of context parameters.
@@ -106,7 +115,7 @@ withConsoleLogger {
106115

107116
## Standard library support
108117

109-
**§7** *(`context` function)*: To extend the implicit scope in a contextual manner we provide additional functions in the standard library.
118+
**§8** *(`context` function)*: To extend the implicit scope in a contextual manner we provide additional functions in the standard library.
110119

111120
* The implementation may be built into the compiler, instead of having a plethora of functions defined in the standard library.
112121

@@ -116,7 +125,7 @@ fun <A, B, R> context(a: A, b: B, block: context(A, B) () -> R): R = block(a, b)
116125
fun <A, B, C, R> context(a: A, b: B, c: C, block: context(A, B, C) () -> R): R = block(a, b, c)
117126
```
118127

119-
**§8** *(`implicit` function)*: We also provide a generic way to obtain a value by type from the context. It allows access to context parameters even when declared using `_`, or within the body of a lambda.
128+
**§9** *(`implicit` function)*: We also provide a generic way to obtain a value by type from the context. It allows access to context parameters even when declared using `_`, or within the body of a lambda.
120129

121130
```kotlin
122131
context(ctx: A) fun <A> implicit(): A = ctx
@@ -173,7 +182,7 @@ We do this by introducing a **bridge function** that simply wraps the access to
173182
context(r: Raise<E>) inline fun raise(error: Error): Nothing = r.raise(error)
174183
```
175184

176-
**§12** *(receiver migration, members)*: If your library exposes a "scope" or "context" type, we suggest moving to context parameters:
185+
**§13** *(receiver migration, members)*: If your library exposes a "scope" or "context" type, we suggest moving to context parameters:
177186

178187
1. functions with the scope type as extension receiver should be refactored to use context parameters,
179188
2. operations defined as members and extending other types should be taken out of the interface definition, if possible,
@@ -376,19 +385,17 @@ functionContext: 'context' [ '(' [ receiverType { ',' receiverType } ] ') ]
376385

377386
**§23** *(declaration with context parameters)*: The context parameters declared for a callable are available in the same way as "regular" value parameters in the body of the function. Both value and context parameters are introduced in the same scope, there is no shadowing between them.
378387

379-
**§24** *(function applicability)*: Building the constraint system is modified for lambda arguments. Compared with the [Kotlin specification](https://kotlinlang.org/spec/overload-resolution.html#description), the type of the parameter _U<sub>m</sub>_ is replaced with _nocontext(U<sub>m</sub>)_, where _nocontext_ removes the initial `context` block from the function type.
388+
**§24** *(applicability, lambdas)*: Building the constraint system is modified for lambda arguments. Compared with the [Kotlin specification](https://kotlinlang.org/spec/overload-resolution.html#description), the type of the parameter _U<sub>m</sub>_ is replaced with _nocontext(U<sub>m</sub>)_, where _nocontext_ removes the initial `context` block from the function type.
380389

381-
**§25** *(most specific candidate)*: When choosing the **most specific candidate** we follow the Kotlin specification, with one addition:
390+
**§25** *(applicability, context resolution)*: After the first phase of function applicability -- checking the type constraint problem -- an additional **context resolution** phase is inserted. For each potentially applicable callable, for each context parameter, we traverse the tower of scopes looking for **exactly one** default receiver or context parameter with a compatible type.
382391

383-
* Candidates with context parameters are considered more specific than those without them.
384-
* But there is no other prioritization coming from the length of the context parameter list or their types.
392+
There are three possible outcomes of this process:
385393

386-
**§26** *(context resolution)*: Once the overload candidate is chosen, we **resolve** context parameters (if any). For each context parameter:
394+
1. If _no_ compatible context value is found for at least one context parameter, then the call is _not_ applicable, and it is removed from the candidate set as a result.
395+
2. If for at least one context parameter there is more than one compatible value at the same level (and case 1 does not apply), a _context ambiguity_ error is issued.
396+
3. If none of (1) or (2) apply, then the candidate is applicable.
387397

388-
* We traverse the tower of scopes looking for **exactly one** default receiver or context parameter with a compatible type.
389-
* Anonymous context parameters (declared with `_`) also participate in this process.
390-
* It is an ambiguity error if more than one value is found at the same level.
391-
* It is an overload resolution error if no applicable value is found.
398+
The following piece of code exemplifies how scoping interacts with context resolution:
392399

393400
```kotlin
394401
interface Logger {
@@ -410,9 +417,26 @@ context(console: ConsoleLogger, file: FileLogger) fun example3() =
410417
with(console) { logWithTime("hello") } // no ambiguity, uses 'console'
411418
```
412419

420+
**§26** *(applicability, `DslMarker`)*: During context resolution, if at a certain scope there is a potential contextual value in scope (either coming from a context parameter or for implicit scope) marked with an annotation `@X` which is itself annotated with [`@DslMarker`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-dsl-marker/) then:
421+
422+
- It is an _error_ for two such values to be available in the same scope.
423+
- If context resolution chooses a contextual value with the same annotation, but in an outer scope, it is a compilation _error_.
424+
- If a call binds to a receiver with the same annotation, it is a compilation _error_.
425+
426+
These rules extend the usual behavior of `@DslMarker` to cover both receivers and context parameters uniformly.
427+
428+
**§27** *(most specific candidate)*: When choosing the **most specific candidate** we follow the [Kotlin specification](https://kotlinlang.org/spec/overload-resolution.html#choosing-the-most-specific-candidate-from-the-overload-candidate-set), with one addition:
429+
430+
* Candidates with context parameters are considered more specific than those without them.
431+
* But there is no other prioritization coming from the length of the context parameter list or their types.
432+
433+
### Extended type inference algorithm
434+
435+
**§28** *(lambda literal inference)*: the type inference process in the [Kotlin specification](https://kotlinlang.org/spec/type-inference.html#statements-with-lambda-literals) should take context parameters into account. Note that unless a function type with context is "pushed" as a type for the lambda, context parameters are never inferred.
436+
413437
### JVM ABI and Java compatibility
414438

415-
**§27** *(JVM and Java compatibility)*: In the JVM a function with context parameters is represented as a regular function with the context parameters situated at the *beginning* of the parameter list. The name of the context parameter, if present, is used as the name of the parameter.
439+
**§29** *(JVM and Java compatibility)*: In the JVM a function with context parameters is represented as a regular function with the context parameters situated at the *beginning* of the parameter list. The name of the context parameter, if present, is used as the name of the parameter.
416440

417441
* Note that parameter names do not impact JVM ABI compatibility.
418442

0 commit comments

Comments
 (0)