Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KT-58965 (Consider moving content of subsection 4.1.1.1 to chapter 5): Possible resolution #132

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 0 additions & 80 deletions docs/src/md/kotlin.core/declarations.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,86 +306,6 @@ Inner classes cannot be declared in [object declarations][Object declaration], a
> }
> ```
##### Inheritance delegation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You made a great point in the issue, that this section is somewhere in between Inheritance and Declarations. One of the ways we use in the specification to reconcile such cases is to keep a forwards or backwards reference, saying "Well, actually we also have inheritance delegation, it is described in more detail [here][there]". So that the reader who comes to Declarations looking for inheritance delegation still can find the full description.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main question to answer here is then: where should we have the actual section and where should we have the reference? In Declarations or in Inheritance? I would like you to think a bit more, make a decision and explain your reasoning.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the one hand, it seems that the chapter Inheritance talks mainly about inheritance and redefinition rules, rather than specific cases, while Inheritance Delegation is a specific thing and syntax related to classifier declaration.
However, on the other hand, the previous location of Inheritance Delegation is hidden deep inside the class declaration and includes both the syntax for it (which is suitable for this chapter) and the inheritance logic with rules (which is more suitable for the chapter Inheritance). In addition, it uses some of the concepts presented later in the chapter Inheritance.

One simple solution in this case would be to somehow split the entire Inheritance Delegation section into two parts-one with syntax, an example (perhaps some technical details) in the Declarations chapter, where it is present right now, and another referring to the exact chapters and concepts from the section Inheritance could go to the Inheritance section itself. These two "parts" could be further connected to each other.

However, the most significant problem with this approach is that the section itself is quite small, and splitting it will result in two even smaller sections with incomplete information, and in order to understand one of them, the reader will need to read the other anyway.

If the "Split" approach is acceptable in this situation, then I would prefer it (even though how to do the splitting is a good question), as the section does not fully belong to either of the chapters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I also agree that the split would be quite difficult. I think that we should keep the body of the section in "Declarations" and simply create the same-named section with reference to it in "Inheritance". WDYT?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me, will do so.
How much should be put to the section in inheritance? Just a reference will be too little for a section, and, I think, even for a subsection. If duplicating information is fine, then I guess I can put two first paragraphs of the original subsection into the new one, with third being somewhere around "Technical details and examples can be found in ". Or even put only the first one along with a small syntax example and just a few sentences from the second one, along with the reference...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be very minimal, for example, we have this very short section: https://kotlinlang.org/spec/built-in-types-and-their-semantics.html#built-in-annotation-types =)

But feel free to propose a more suitable split or reference.

In a classifier (an object or a class) declaration $C$, any supertype $I$ inheritance may be *delegated to* an arbitrary value $v$ if:
- The supertype $I$ is an interface type;
- $v$ has type $T$ such that $T <: I$.
The inheritance delegation uses a syntax similar to [property delegation][Delegated property declaration] using the `by` keyword, but is specified in the classifier declaration header and is a very different concept.
If inherited using delegation, each method $M$ of $I$ (whether they have a default implementation or not) is delegated to the corresponding method of $v$ as if it was overridden in $C$ with all the parameter values directly passed to the corresponding method in $v$, unless the body of $C$ itself has a suitable override of $M$ (see the [method overriding][Overriding] section).
The particular means on how $v$ is stored inside the classifier object is platform-defined.
Due to the [initialization order of a classifier object][Classifier initialization], the expression used to construct $v$ can not access any of the classifier object properties or methods excluding the parameters of the primary constructor.
> Example:
>
> ```kotlin
> interface I {
> fun foo(value: Int): Double
> val bar: Long
> }
> interface J : I {
> fun fee(): Int
> }
>
> class C(delegatee: I): I by delegatee
> ```
>
> is expanded to
>
> ```kotlin
> interface I {
> fun foo(value: Int): Double
> val bar: Long
> }
> interface J : I {
> fun fee(): Int
> }
>
> class C(delegatee: I): I {
> val I$delegate = delegate
>
> override fun foo(value: Int): Double = I$delegate.foo(value)
> override val bar: Long
> get() = I$delegate.bar
> }
> ```
>
> Please note that the expression used as delegate is accessed exactly once when creating the object, e.g. if the delegate expression contains a mutable property access, this mutable property is accessed once during object construction and its subsequent changes do not affect the delegated interface functions.
> See [classifier initialization section][Classifier initialization] for details on the evaluation order of classifier initialization entities.
>
> For example (assuming interface `I` from the previous example is defined):
>
> ```kotlin
> var mut = object: J {...}
>
> class D: I by mut // D delegates I to mutable property
> ```
>
> is expanded to
>
> ```kotlin
> var mut = object: J {...}
> class D: I {
> val I$delegate = mut // mut is accessed only once
>
> override fun foo(value: Int): Double = I$delegate.foo(value)
> override val bar: Long
> get() = I$delegate.bar
> }
> ```
>
> ```kotlin
> mut = x1
> val d1 = D() // d1 methods are delegated to x1
> mut = x2
> val d2 = D() // d2 methods are delegated to x2
> // but d1 methods are still delegated to x1
> ```
##### Abstract classes {#abstract-classes-declarations}
A [class declaration][Class declaration] can be marked `abstract`.
Expand Down
80 changes: 80 additions & 0 deletions docs/src/md/kotlin.core/inheritance.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,85 @@ As Kotlin is a language with single inheritance (only one supertype can be a cla

TODO(Examples)

### Inheritance delegation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think the path Inheritance > Inheriting > Inheritance delegation is the best to place the section about inheritance delegation within the Inheritance section?

I have a feeling Inheriting talks about smth else, and there is another better-suited section in Inheritance to talk about inheritance delegation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remark: here the "Inheritance delegation" section is not located in the "Inheriting" section, but rather as an independent section directly inside the "Inheritance" chapter.

Apparently, if we are still considering places within the "Inheritance" chapter, it may seem meaningful to move the section to the "Classifier type inheritance", as, well, it does speak about the classifiers, but as I see it, "Classifier type inheritance" is more general, talking about what can actually be affected by inheritance, rather than about specifics of how.
The next section, "Matching and subsumption of declaration" describes which declarations can be considered as "matching", and thus a section about one specific case of inheritance ("Inheritance delegation") cannot be placed here or before it.
Next comes the "Inheriting" section, which, to be honest, just expands on the previous section, again, talking about general rules for inheritance.
The only section left is "Overriding", which is the only section finally introducing overriding and showing concrete examples.

Perhaps it may be meaningful to put it after the "Overriding" section, especially since "Inheritance delegation" references it, and, as it seems, is even more specific.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer to place it right after the "Classifier type inheritance", tbh.


In a classifier (an object or a class) declaration $C$, any supertype $I$ inheritance may be *delegated to* an arbitrary value $v$ if:

- The supertype $I$ is an interface type;
- $v$ has type $T$ such that $T <: I$.

The inheritance delegation uses a syntax similar to [property delegation][Delegated property declaration] using the `by` keyword, but is specified in the classifier declaration header and is a very different concept.
If inherited using delegation, each method $M$ of $I$ (whether they have a default implementation or not) is delegated to the corresponding method of $v$ as if it was overridden in $C$ with all the parameter values directly passed to the corresponding method in $v$, unless the body of $C$ itself has a suitable override of $M$ (see the [method overriding][Overriding] section).

The particular means on how $v$ is stored inside the classifier object is platform-defined.

Due to the [initialization order of a classifier object][Classifier initialization], the expression used to construct $v$ can not access any of the classifier object properties or methods excluding the parameters of the primary constructor.

> Example:
>
> ```kotlin
> interface I {
> fun foo(value: Int): Double
> val bar: Long
> }
> interface J : I {
> fun fee(): Int
> }
>
> class C(delegatee: I): I by delegatee
> ```
>
> is expanded to
>
> ```kotlin
> interface I {
> fun foo(value: Int): Double
> val bar: Long
> }
> interface J : I {
> fun fee(): Int
> }
>
> class C(delegatee: I): I {
> val I$delegate = delegate
>
> override fun foo(value: Int): Double = I$delegate.foo(value)
> override val bar: Long
> get() = I$delegate.bar
> }
> ```
>
> Please note that the expression used as delegate is accessed exactly once when creating the object, e.g. if the delegate expression contains a mutable property access, this mutable property is accessed once during object construction and its subsequent changes do not affect the delegated interface functions.
> See [classifier initialization section][Classifier initialization] for details on the evaluation order of classifier initialization entities.
>
> For example (assuming interface `I` from the previous example is defined):
>
> ```kotlin
> var mut = object: J {...}
>
> class D: I by mut // D delegates I to mutable property
> ```
>
> is expanded to
>
> ```kotlin
> var mut = object: J {...}
> class D: I {
> val I$delegate = mut // mut is accessed only once
>
> override fun foo(value: Int): Double = I$delegate.foo(value)
> override val bar: Long
> get() = I$delegate.bar
> }
> ```
>
> ```kotlin
> mut = x1
> val d1 = D() // d1 methods are delegated to x1
> mut = x2
> val d2 = D() // d2 methods are delegated to x2
> // but d1 methods are still delegated to x1
> ```
ice-phoenix marked this conversation as resolved.
Show resolved Hide resolved
### Overriding
A callable declaration (that is, a [property][Property declaration] or [member function][Function declaration] declaration) inside a classifier declaration is said to be *overridable* if:
Expand Down Expand Up @@ -145,3 +224,4 @@ If the overriding declaration *does* have its visibility specified, it must not
> Note: if a declaration binds a new function to the same name as was introduced in the base class, but which does not subsume it, it is neither a compile-time error nor an overriding declaration.
> In this case these two declarations follow the normal rules of [overloading][Overload resolution].
> However, these declarations may still result in a compile-time error as a result of [conflicting overload][Conflicting overloads] detection.