Skip to content

Commit 8cae704

Browse files
committed
[KEEP-0449] Clarifications to "companion blocks and extensions"
1 parent 9954586 commit 8cae704

File tree

1 file changed

+54
-34
lines changed

1 file changed

+54
-34
lines changed

proposals/KEEP-0449-companions-block-extension.md

Lines changed: 54 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ assigns relative importance to them. Since Kotlin has a big user base, any
104104
problem that affects a seemingly minor percentage of users may still affect
105105
a large absolute number of people.
106106

107-
**(§1) Extending the companion scope**.
107+
**(#1) Extending the companion scope**.
108108
Companion objects are in many ways no different than any other object in Kotlin.
109109
In particular, you can write an _extension_ to a companion object to create
110110
a new callable that can be accessed through the classifier's name.
@@ -120,7 +120,7 @@ It's an [old issue](https://youtrack.jetbrains.com/issue/KT-11968) to remove
120120
this gate-keeping, and make extending the companion scope available regardless
121121
of the presence of an actual companion object class.
122122

123-
**(§2) More direct mapping to platform statics.**
123+
**(#2) More direct mapping to platform statics.**
124124
Kotlin prides itself in good interoperability with the underlying platforms
125125
it runs. And most of them (JVM, JS, Swift) feature a notion of _static member_.
126126
In some scenarios, it becomes very important to generate such a static member
@@ -145,13 +145,13 @@ platform well. In many cases, failing to add the corresponding annotation
145145
either breaks at runtime, or may succeed to run with unintended behavior
146146
(like tests failing or passing when they should not).
147147

148-
**(§3) Multiplatform `@Static` annotation.**
148+
**(#3) Multiplatform `@Static` annotation.**
149149
The current design also require an annotation for every platform
150150
(`@JvmStatic`, `@JsStatic`, and so forth). This issue impact authors of
151151
multiplatform libraries mostly, that need to remember all those annotations
152152
to expose their API in the way expected by every target platform.
153153

154-
**(§4) Additional object allocation.**
154+
**(#4) Additional object allocation.**
155155
Since companion objects are indeed objects, they require an allocation the
156156
first time they are used. This is a problem in the JVM and Native targets.
157157

@@ -195,7 +195,7 @@ and [dropping the companion if empty](https://youtrack.jetbrains.com/issue/KT-15
195195
However, doing so in a naive way impact binary compatibility, since methods
196196
or class that were created before would no longer be available.
197197

198-
**(§5) No expect/actual matching**.
198+
**(#5) No expect/actual matching**.
199199
Since static members do not exist in the Kotlin language, matching classes
200200
in a multiplatform environment becomes more complicated than expected.
201201
For example, it may be desirable to declare a expected `Vector` type,
@@ -234,7 +234,7 @@ existing features. In the new design we try to follow a few principles:
234234
### Companion extensions
235235

236236
This is a direct way to declare functions accessible through the type name
237-
(that is, class members). This solves problem §1.
237+
(that is, class members). This solves problem #1.
238238

239239
```kotlin
240240
companion val Vector.UnitX get() = Vector(1.0, 0.0)
@@ -258,7 +258,7 @@ companion extensions define alongside the type itself.
258258

259259
### Companion blocks
260260

261-
Solving problem §4 requires changes to the current code generation strategy.
261+
Solving problem #4 requires changes to the current code generation strategy.
262262
In particular, in order to generate code that doesn't allocate a companion
263263
object we need to ensure that its instance is not used. We have considered
264264
several approaches to this mechanism, ultimately choosing for companion blocks.
@@ -286,7 +286,7 @@ but do not include any of the properties associated with an _object_:
286286

287287
The compilation strategy for companion blocks should not be fixed.
288288
However, in those platforms that support statics, we propose to use those
289-
features, solving §2 and §4. This also alleviates the need for §3,
289+
features, solving #2 and #4. This also alleviates the need for #3,
290290
that remains out-of-scope for this KEEP document
291291

292292
### Discarded ideas
@@ -408,7 +408,7 @@ reasons for that choice, mostly for the sake of simplicity.
408408
- It gives a clear answer to what is in scope and with what priority when
409409
we are in the body of a companion block, companion extension, and companion
410410
object member, by reusing the same machinery (receivers) that we already
411-
have in Kotlin (see examples 2.1 to 2.3).
411+
have in Kotlin (see examples Example 2.1 to Example 2.3).
412412
- Companion object members and extensions can always be disambiguated by
413413
_explicitly_ writing `T.Companion`, something that is not possible with
414414
the other kinds of companions.
@@ -440,16 +440,31 @@ confusion especially among experienced Kotlin developers.
440440
### Declaration
441441

442442
**§1.1** (_grammar_): the [Kotlin grammar](https://kotlinlang.org/spec/syntax-and-grammar.html#syntax-grammar)
443-
is modified as follows.
443+
is modified as follows. Note that only function and property declarations are
444+
allowed in a companion block.
444445

445446
```diff
446447
classMemberDeclaration:
447448
...
448449
| companionObject
449-
+ | companionDeclaration
450-
451-
+ companionDeclaration:
452-
+ 'companion' {NL} classBody
450+
+ | companionBlock
451+
+
452+
+ companionBlock:
453+
+ 'companion' {NL} companionBlockBody
454+
+
455+
+ companionBlockBody:
456+
+ '{'
457+
+ {NL}
458+
+ companionBlockDeclarations
459+
+ {NL}
460+
+ '}'
461+
+
462+
+ companionBlockDeclarations:
463+
+ {companionBlockDeclaration [semis]}
464+
+
465+
+ companionBlockDeclaration:
466+
+ functionDeclaration
467+
+ | propertyDeclaration
453468

454469
memberModifier
455470
...
@@ -556,7 +571,7 @@ if desired.
556571
are updated to include properties defined in companion blocks or as companion
557572
extensions.
558573

559-
**1.1** (_example_): the following code should be accepted. Note that it
574+
**Example 1.1** (_example_): the following code should be accepted. Note that it
560575
uses two separate companion blocks, declares properties with an initializer,
561576
and the last one is marked as constant.
562577

@@ -577,7 +592,7 @@ data class Vector(val x: Double, val y: Double) {
577592
}
578593
```
579594

580-
**1.2** (_operators_): the following code uses `invoke` to define
595+
**Example 1.2** (_operators_): the following code uses `invoke` to define
581596
a fake constructor that `suspend`s.
582597

583598
```kotlin
@@ -642,7 +657,7 @@ object by using its classifier name.
642657
**§2.1.2** (_`this` expressions_): phantom static implicit `this` is not
643658
available through [explicit `this` expressions](https://kotlinlang.org/spec/overload-resolution.html#receivers).
644659
Access to companion block members must always be done either implicitly or
645-
through an explicit type receiver, as defined in §2.3.
660+
through an explicit type receiver, as defined in §2.3.1.
646661

647662
**§2.2.1** (_companion block members, receiver_): the implicit `this` receiver
648663
for the class is **not** available for companion block members. As a result,
@@ -672,7 +687,7 @@ The rules are updated as follows (updates in boldface):
672687
This approach is consistent with the definition of phantom static `this` in the
673688
specification as a receiver with more priority than the companion.
674689

675-
**2.1** (_priority_): the code below exemplifies how companion extensions win
690+
**Example 2.1** (_priority_): the code below exemplifies how companion extensions win
676691
even over companion object members, and how to refer to companion object
677692
members in such a situation.
678693

@@ -691,7 +706,7 @@ fun example() {
691706
}
692707
```
693708

694-
**2.2** (_absence of companion object members in extensions_): the code below
709+
**Example 2.2** (_absence of companion object members in extensions_): the code below
695710
exemplifies the fact that companion object members are not available in
696711
companion extensions, since they only have the phantom static implicit `this`
697712
as an implicit receiver.
@@ -709,7 +724,7 @@ companion fun Example.example() {
709724
}
710725
```
711726

712-
**2.4** (_resolution in companion blocks_): the code below exemplifies that
727+
**Example 2.4** (_resolution in companion blocks_): the code below exemplifies that
713728
companion block members and companion block extensions may be called without
714729
qualification from any of those places.
715730

@@ -734,7 +749,7 @@ companion fun Example.testInCompanionExtension() {
734749
```
735750

736751

737-
**2.4** (_receiver in companion objects_): in the case of members or extensions
752+
**Example 2.4** (_receiver in companion objects_): in the case of members or extensions
738753
to the companion object, the corresponding receiver is introduced with the
739754
highest priority. In the case of companion object members, the phantom static
740755
implicit `this` of the enclosing class is still available.
@@ -782,7 +797,7 @@ The rules are updated as follows (updates in boldface):
782797
In the case of a companion extension, the receiver type is not visible in
783798
the type of the obtained callable reference.
784799

785-
**⌘2.4** (_callable references_): the code below should be accepted.
800+
**Example 2.5** (_callable references_): the code below should be accepted.
786801

787802
```kotlin
788803
data class Vector(val x: Double, val y: Double) {
@@ -936,7 +951,7 @@ properties are compiled into static private fields.
936951
937952
**§4.1.2** (_JVM, companion initialization_): companion initialization is
938953
compiled into the static initializer `<clinit>`, following the order described
939-
in §3.4.
954+
in §3.2.2.
940955
941956
**§4.1.3** (_JVM, companion extensions_): companion extensions are compiled
942957
as static members of the corresponding `FileKt` class, without any value
@@ -946,7 +961,7 @@ are compiled into static private fields.
946961
The name of this static member is the same given in the source code, unless
947962
it has been overridden using a `@JvmName` annotation.
948963
949-
**4.1.1** (_JVM example_): the code below,
964+
**Example 4.1.1** (_JVM example_): the code below,
950965
951966
```kotlin
952967
// in file 'Vector.kt'
@@ -967,7 +982,7 @@ is compiled into bytecode equivalent to the following Java code,
967982
class Vector {
968983
// instance fields and constructor omitted
969984
970-
private static Vector Zero;
985+
private static final Vector Zero;
971986
public static Vector getZero() { return Zero; }
972987
973988
public static final int Dimensions = 2;
@@ -982,7 +997,7 @@ class VectorKt {
982997
}
983998
```
984999
985-
**4.1.2** (_avoiding platform clashes_): the rules above may lead to platform
1000+
**Example 4.1.2** (_avoiding platform clashes_): the rules above may lead to platform
9861001
clashes in both class bodies and companion extensions. Note that the JVM
9871002
forbids defining a static and instance method with the same signature. The
9881003
solution is to use `@JvmName` to rename one of them.
@@ -1053,6 +1068,11 @@ public class Vector {
10531068
}
10541069
```
10551070
1071+
**§4.1.5** (_JVM, expect/actual matching_): the described compilation strategy
1072+
should be taken into account for `actual`izing an `expect` declaration with
1073+
a Java implementation. In particular, static members in a Java class may
1074+
`actual`ize companion block members.
1075+
10561076
**§4.2.1** (_JS, companion block members_): companion block members are
10571077
compiled into [static members](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static)
10581078
of the enclosing class.
@@ -1074,7 +1094,7 @@ into the initializer of that property.
10741094
as any other top-level declaration. The name in JS is taken verbatim from
10751095
the declaration, although it can be overridden using `@JsName`.
10761096
1077-
**4.2.1** (_JS example_): the code below,
1097+
**Example 4.2.1** (_JS example_): the code below,
10781098
10791099
```kotlin
10801100
data class Vector(val x: Double, val y: Double) {
@@ -1145,7 +1165,7 @@ as an [extension](https://docs.swift.org/swift-book/documentation/the-swift-prog
11451165
holding the members. Other than that, the compilation scheme is similar
11461166
to that of companion block members.
11471167
1148-
**4.3.1** (_Swift example_): the code below,
1168+
**Example 4.3.1** (_Swift example_): the code below,
11491169
11501170
```kotlin
11511171
data class Vector(val x: Double, val y: Double) {
@@ -1187,7 +1207,7 @@ the package is updated as follows.
11871207
11881208
2. `KClass` implements `KCompanionDeclarationContainer`.
11891209
1190-
**§6.2** (_companion "receiver"_):
1210+
**§5.2** (_companion "receiver"_):
11911211
the following property is added to `KCallable`. This property should be `null`
11921212
whenever the callable is neither a companion extension nor defined in a
11931213
companion block.
@@ -1215,7 +1235,7 @@ interface KCompanionParameter {
12151235
Note that the `type` is a `KClass` and not a `KType` because those relate to
12161236
the _class itself_, not a particular instantiation thereof.
12171237
1218-
**§6.3** (_no additional arguments to `call`_):
1238+
**§5.3** (_no additional arguments to `call`_):
12191239
no value should be passed in the position of receiver for neither
12201240
companion block members nor companion extensions
12211241
when using `call`, `callBy` from `KCallable`, and the corresponding
@@ -1239,7 +1259,7 @@ We should acknowledge that removing the companion object is
12391259
a _binary breaking change_, and library authors should act accordingly.
12401260
If their goal is to re-expose the same functionality from companion objects
12411261
as companion blocks, the best solution is to use `@JvmStatic` (and similar
1242-
annotations in other platforms). Another solution that improves on problem §4
1262+
annotations in other platforms). Another solution that improves on problem #4
12431263
is to manually keep both versions, but that has additional boilerplate.
12441264
12451265
```kotlin
@@ -1268,11 +1288,11 @@ platform. In this section we discuss how the entire proposal "translates" to
12681288
the JVM side of things.
12691289
12701290
Companion blocks translate to **static members**. Since this mapping works well
1271-
in both directions, this solves problems §2 (mapping to platform statics) and
1272-
§5 (expect/actual matching).
1291+
in both directions, this solves problems #2 (mapping to platform statics) and
1292+
#5 (expect/actual matching).
12731293
12741294
Using static members alleviate the need for an object allocation, solving
1275-
problem §4. However, in order to have good performance, we need a way to
1295+
problem #4. However, in order to have good performance, we need a way to
12761296
prevent re-computation of values; for that reason the proposal allows for
12771297
properties in companion blocks and companion extensions to have **backing
12781298
fields and initializers**.

0 commit comments

Comments
 (0)