Skip to content

Commit 29982ec

Browse files
authored
Refinements to "context parameters" KEEP (Kotlin#400)
1 parent 7604809 commit 29982ec

File tree

1 file changed

+45
-11
lines changed

1 file changed

+45
-11
lines changed

proposals/context-parameters.md

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ This is an updated proposal for [KEEP-259](https://github.com/Kotlin/KEEP/issues
1313

1414
1. Introduction of named context parameters,
1515
2. Context receivers are dropped,
16-
3. Removal of `this@Type` syntax, introduction of `implicit<A>()`,
16+
3. Removal of `this@Type` syntax, introduction of `contextOf<A>()`,
1717
4. Contexts are not allowed in constructors,
1818
5. Callable references resolve their context arguments eagerly,
1919
6. Context-in-classes are dropped.
@@ -107,7 +107,7 @@ context(logger: Logger) fun User.doAction() {
107107
* The type and order of context parameters must coincide.
108108
* It is allowed (yet discouraged) to change the name of a context parameter.
109109

110-
It is a conflict to declare overloads which only differ in the order of the context parameters.
110+
It is a conflict to declare overloads which only differ in the number of context parameters. It is allowed to declare one overload with _no_ context parameters and one with _some_ of them (this is aligned with §7.8).
111111

112112
**§1.6** *(naming ambiguity)*: We use the term **context** with two meanings:
113113

@@ -134,6 +134,34 @@ Logger.(User) -> Int
134134
(Logger, User) -> Int
135135
```
136136

137+
As a result of these equivalences, it is possible invoke a value with a function type with context parameters by giving them as regular value parameters.
138+
139+
```kotlin
140+
fun foo(x: context(String, Double) Int.(z: Long) -> Unit, y: Int) {
141+
context("", 1.0) {
142+
y.x(1L) // OK, regular way to call it
143+
}
144+
145+
y.x("", 1.0, 1L) // NO
146+
x("", 1.0, y, 1L) // OK, all parameters as values
147+
}
148+
```
149+
150+
It is not allowed to pass context arguments explicitly inside a value argument list when callee is a regular function (not a value of a function type).
151+
152+
```kotlin
153+
context(_: String, _: Double) fun Int.x(z: Long): Unit { }
154+
155+
fun foo(y: Int) {
156+
context("", 1.0) {
157+
y.x(1L) // regular way to call it
158+
}
159+
160+
y.x("", 1.0, 1L) // NO
161+
x("", 1.0, y, 1L) // NO
162+
}
163+
```
164+
137165
**§1.8** *(lambdas)*: If a lambda is assigned a function type with context parameters, those behave as if declared with `_` as its name.
138166

139167
* They participate in context resolution but are only accessible through the `implicit` function (defined below).
@@ -162,13 +190,13 @@ fun <A, B, R> context(a: A, b: B, block: context(A, B) () -> R): R = block(a, b)
162190
fun <A, B, C, R> context(a: A, b: B, c: C, block: context(A, B, C) () -> R): R = block(a, b, c)
163191
```
164192

165-
**§2.2** *(`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.
193+
**§2.2** *(`contextOf` 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.
166194

167195
* Implementations are encouraged, but not required, to mark this function as `inline`.
168196
* If possible, type inference for type variable `A` should be restricted (for example, using `@NoInfer`). Developers are expected to use the function with explicit type arguments.
169197

170198
```kotlin
171-
context(ctx: A) fun <A> implicit(): A = ctx
199+
context(ctx: A) fun <A> contextOf(): A = ctx
172200
```
173201

174202
_Note:_ This function replaces the uses of `this@Type` in the previous iteration of the design.
@@ -474,6 +502,12 @@ functionType: [ functionContext ] [ receiverType '.' ] ...
474502
functionContext: 'context' '(' receiverType { ',' receiverType } [ ',' ] ')'
475503
```
476504

505+
Context parameters may receive annotations as any other regular parameter.
506+
507+
```kotlin
508+
context(@Annotated users: UserRepository) fun foo() = ...
509+
```
510+
477511
**Recommended style:** annotations, context parameters, then other modifiers as per the [usual style guide](https://kotlinlang.org/docs/coding-conventions.html#modifiers-order).
478512

479513
**§7.2** *(disambiguating `context`)*: We do not enforce `context` as a hard keyword, leading to potential ambiguities in the body of declarations. In particular, `context(` may be both the beginning of:
@@ -538,13 +572,13 @@ context(ctx: Any) fun foo() {
538572
context(s: String) fun bar() { }
539573
```
540574

541-
**§7.7** *(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 from an implicit receiver) marked with an annotation `@X` which is itself annotated with [`@DslMarker`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-dsl-marker/) then:
575+
**§7.7** *(applicability, `DslMarker`)*: we say that a value is `X`-DSL-marked if the type of that value is annotated with `@X`, and `X` is itself annotated with [`@DslMarker`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-dsl-marker/).
576+
577+
If at a certain scope there is a potential contextual `X`-DSL-marked value in scope (either coming from a context parameter or from an implicit receiver) and context resolution chooses a contextual `X`-DSL-marked value in an outer scope, it is a compilation _error_.
542578

543-
- It is an _error_ for two such values to be available in the same scope.
544-
- If context resolution chooses a contextual value with the same annotation, but in an outer scope, it is a compilation _error_.
545-
- If a call binds to a receiver with the same annotation, it is a compilation _error_.
579+
This is in addition to the _old rule_, stating: if at a certain scope there is an implicit `X`-DSL-marked receiver in scope and resolution chooses a `X`-DSL-marked implicit receiver in an outer scope, it is a compilation error. Note that context parameters do _not_ conflict with implicit receivers when the latter are used as receivers.
546580

547-
These rules extend the usual behavior of `@DslMarker` to cover both receivers and context parameters uniformly.
581+
These rules extend the [usual behavior of `@DslMarker`](https://kotlinlang.org/docs/type-safe-builders.html#scope-control-dslmarker) to cover both receivers and context parameters uniformly.
548582

549583
```kotlin
550584
@DslMarker annotation class ExampleMarker
@@ -575,8 +609,8 @@ fun dslMarkerExample() =
575609
// the ExampleScope introduced (3)
576610
// to resolve context parameters
577611

578-
this.exemplify() // rejected: DSL scope violation
579-
// since it binds the receiver from (2)
612+
this.exemplify() // correct, uses (2)
613+
// (1) and (3) may not be receivers
580614
similarExampleTo("bye") // rejected: DSL scope violation
581615
}
582616
}

0 commit comments

Comments
 (0)