From 6cf9203ccac58d95df70073fe59f130a4a3655de Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 25 Aug 2016 19:16:18 +0200 Subject: [PATCH 01/20] Create dynamic-dispatch-extension-receiver.md --- .../dynamic-dispatch-extension-receiver.md | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 proposals/dynamic-dispatch-extension-receiver.md diff --git a/proposals/dynamic-dispatch-extension-receiver.md b/proposals/dynamic-dispatch-extension-receiver.md new file mode 100644 index 000000000..c363a435c --- /dev/null +++ b/proposals/dynamic-dispatch-extension-receiver.md @@ -0,0 +1,83 @@ +# Dynamic Dispatch Extension Receiver + +* **Type**: Design proposal +* **Author**: Johannes Neubauer +* **Status**: Shepherded +* **Shepherd**: [@dnpetrov](https://github.com/dnpetrov) +* **Prototype**: *not yet* + +## Feedback + +Discussion has been started in [this pull request](https://github.com/Kotlin/KEEP/pull/35) regarding type-checking of reified type parameters so far. Further discussion should be done in the pull request for this proposal. An issue in the KEEP project has to be opened. + +## Summary + +Support dynamic dispatch for the extension receiver of extension methods via a new keyword `dispatch`. + +## Motivation + +Extension methods are statically dispatched. Currently, dispatching has to be done manually (if necessary), which is boiler-plate and error-prone. Often this is the intended behavior and it is much faster than dynamic dispatch. Hence, adding dynamic dispatch as standard behavior is not an option, especially as the implicit extension receiver is used for dynamic dispatch of extension functions, if the extension method is defined on a class. Additionally, this would break a lot of existing code. + +Instead a new keyword `dispatch` is proposed to be introduced, which can be added to the receiver of extension functions/methods. This proposal is intended to be the first in a row (iff successful) for adding dynamic dispatch (where explicitly defined) to function parameters, too. + +## Description + +The following example shows a possible syntax for dynamic dispatch for (explicit) extension receiver in action (for method overload): + +```kotlin +interface A +interface B: A + +// the parantheses are necessary because a future proposal could introduce the dispatch keyword for the complete function (including the parameter `a`) and this should not introduce source-breaking changes... +fun (dispatch A).copyTo(a: A) { + // do the copy stuff +} + +fun (dispatch B).copyTo(b: B) { + // should call `A.copyTo(A)` + super.copyTo(b) + // do the copy stuff for `B` +} + +// more concrete implementations... + +inline fun T.copy(): T { + val copy = T::class.java.getConstructor().newInstance() + // this call is dispatched, because of the `dispatch` keyword in the implementations. + copyTo(copy) + return copy +} + +val b: A = B() +// Variable `bCopy` will be of type `A` as this is the type for which the inline function has been called. +// But the instance assigned to `bCopy` at runtime will be of type `B` because of dynamic dispatch. +val bCopy = b.copy() +``` + +If the repitition of the dispatch keyword is not preferable this could be done with a separate declaration: + +```kotlin +// declaration of extension receiver dispatching for all extension methods named copyTo for type A and its subtypes +fun (dispatch A).copyTo +// such a declaration could also specify for which overloads it adds dynamic dispatch (although the dispatching is done for the receiver only). +fun (dispatch T).copyTo(a: T) +// and of course you can add it to all extension methods of a type in a given scope (using modifiers private, ...). +fun (dispatch A).* + +fun A.copyTo(a: A) { + // do the copy stuff +} + +fun B.copyTo(b: B) { + // should call `A.copyTo(A)` + super.copyTo(b) + // do the copy stuff for `B` +} + +// more concrete implementations... +// ... +``` + +## Open Questions + +*Not yet.* From f02ecd45b06771d477251d93464dc6ff93bf6765 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Mon, 29 Aug 2016 11:57:17 +0200 Subject: [PATCH 02/20] Update dynamic-dispatch-extension-receiver.md --- proposals/dynamic-dispatch-extension-receiver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/dynamic-dispatch-extension-receiver.md b/proposals/dynamic-dispatch-extension-receiver.md index c363a435c..39fe38af9 100644 --- a/proposals/dynamic-dispatch-extension-receiver.md +++ b/proposals/dynamic-dispatch-extension-receiver.md @@ -8,7 +8,7 @@ ## Feedback -Discussion has been started in [this pull request](https://github.com/Kotlin/KEEP/pull/35) regarding type-checking of reified type parameters so far. Further discussion should be done in the pull request for this proposal. An issue in the KEEP project has to be opened. +Discussion has been started in [this pull request](https://github.com/Kotlin/KEEP/pull/35) regarding type-checking of reified type parameters so far. Further discussion should be done in [the pull request for this proposal](https://github.com/Kotlin/KEEP/pull/46). An issue in the KEEP project has to be opened. ## Summary From aed321e226e34f36065ad65143328e6da847b043 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Tue, 13 Sep 2016 15:59:36 +0200 Subject: [PATCH 03/20] Update and rename dynamic-dispatch-extension-receiver.md to overriding-extension-methods.md Adapted the proposal to the hints from @dnpetrov --- .../dynamic-dispatch-extension-receiver.md | 83 ------ proposals/overriding-extension-methods.md | 275 ++++++++++++++++++ 2 files changed, 275 insertions(+), 83 deletions(-) delete mode 100644 proposals/dynamic-dispatch-extension-receiver.md create mode 100644 proposals/overriding-extension-methods.md diff --git a/proposals/dynamic-dispatch-extension-receiver.md b/proposals/dynamic-dispatch-extension-receiver.md deleted file mode 100644 index 39fe38af9..000000000 --- a/proposals/dynamic-dispatch-extension-receiver.md +++ /dev/null @@ -1,83 +0,0 @@ -# Dynamic Dispatch Extension Receiver - -* **Type**: Design proposal -* **Author**: Johannes Neubauer -* **Status**: Shepherded -* **Shepherd**: [@dnpetrov](https://github.com/dnpetrov) -* **Prototype**: *not yet* - -## Feedback - -Discussion has been started in [this pull request](https://github.com/Kotlin/KEEP/pull/35) regarding type-checking of reified type parameters so far. Further discussion should be done in [the pull request for this proposal](https://github.com/Kotlin/KEEP/pull/46). An issue in the KEEP project has to be opened. - -## Summary - -Support dynamic dispatch for the extension receiver of extension methods via a new keyword `dispatch`. - -## Motivation - -Extension methods are statically dispatched. Currently, dispatching has to be done manually (if necessary), which is boiler-plate and error-prone. Often this is the intended behavior and it is much faster than dynamic dispatch. Hence, adding dynamic dispatch as standard behavior is not an option, especially as the implicit extension receiver is used for dynamic dispatch of extension functions, if the extension method is defined on a class. Additionally, this would break a lot of existing code. - -Instead a new keyword `dispatch` is proposed to be introduced, which can be added to the receiver of extension functions/methods. This proposal is intended to be the first in a row (iff successful) for adding dynamic dispatch (where explicitly defined) to function parameters, too. - -## Description - -The following example shows a possible syntax for dynamic dispatch for (explicit) extension receiver in action (for method overload): - -```kotlin -interface A -interface B: A - -// the parantheses are necessary because a future proposal could introduce the dispatch keyword for the complete function (including the parameter `a`) and this should not introduce source-breaking changes... -fun (dispatch A).copyTo(a: A) { - // do the copy stuff -} - -fun (dispatch B).copyTo(b: B) { - // should call `A.copyTo(A)` - super.copyTo(b) - // do the copy stuff for `B` -} - -// more concrete implementations... - -inline fun T.copy(): T { - val copy = T::class.java.getConstructor().newInstance() - // this call is dispatched, because of the `dispatch` keyword in the implementations. - copyTo(copy) - return copy -} - -val b: A = B() -// Variable `bCopy` will be of type `A` as this is the type for which the inline function has been called. -// But the instance assigned to `bCopy` at runtime will be of type `B` because of dynamic dispatch. -val bCopy = b.copy() -``` - -If the repitition of the dispatch keyword is not preferable this could be done with a separate declaration: - -```kotlin -// declaration of extension receiver dispatching for all extension methods named copyTo for type A and its subtypes -fun (dispatch A).copyTo -// such a declaration could also specify for which overloads it adds dynamic dispatch (although the dispatching is done for the receiver only). -fun (dispatch T).copyTo(a: T) -// and of course you can add it to all extension methods of a type in a given scope (using modifiers private, ...). -fun (dispatch A).* - -fun A.copyTo(a: A) { - // do the copy stuff -} - -fun B.copyTo(b: B) { - // should call `A.copyTo(A)` - super.copyTo(b) - // do the copy stuff for `B` -} - -// more concrete implementations... -// ... -``` - -## Open Questions - -*Not yet.* diff --git a/proposals/overriding-extension-methods.md b/proposals/overriding-extension-methods.md new file mode 100644 index 000000000..020cd28f1 --- /dev/null +++ b/proposals/overriding-extension-methods.md @@ -0,0 +1,275 @@ +# Overriding Extension Methods + +* **Type**: Design proposal +* **Author**: Johannes Neubauer +* **Status**: Shepherded +* **Shepherd**: [@dnpetrov](https://github.com/dnpetrov) +* **Prototype**: *not yet* + +## Feedback + +Discussion has been started in [this pull request](https://github.com/Kotlin/KEEP/pull/35) regarding type-checking of reified type parameters so far. Further discussion should be done in [the pull request for this proposal](https://github.com/Kotlin/KEEP/pull/46). An issue in the KEEP project has to be opened. + +## Summary + +Support dynamic dispatch for the extension receiver of extension methods via allowing to override extension functions. + +## Motivation + +Extension methods are statically dispatched. Currently, dispatching has to be done manually (if necessary), which is boiler-plate and error-prone. Often this is the intended behavior and it is much faster than dynamic dispatch. Hence, adding dynamic dispatch as standard behavior is not an option, especially as the *implicit extension receiver* is used for dynamic dispatch of extension functions, if the extension method is defined on a class. Additionally, this would break a lot of existing code. + +Instead introducing the concept of overriding for extension functions (analogous to overriding member functions) is proposed. This proposal is intended to be the first in a row (iff successful) for adding sophisticated semantics for function/method lookup that allows to create behavior for objects that is more object-oriented, and at the same time offers type-safety, performance (where necessary), and backward compatibility (no source-breaking). + +## Description + +The following example shows a possible syntax as well as semantics for function overriding for an *(explicit) extension receiver* in action. First, let us see how it works for member functions and then we will take a look at a solution for extension functions. + +The task is to create a (more or less) complex inheritance structure of classes with one (root) base class which offers the possibility to copy instances of any object of these classes and call polymorphically a method `foo()` on the copies. All this should be able using a list with the root class as type argument: + +```kotlin +fun main(args: Array) { + val l = arrayOf(A(), B(), C(), D()) + + // prints "A\nB\nC\nD\n" + l.map { it.copy() }.forEach { it.foo() } +} + +open class A { + + open fun foo() { + println("A") + } + + open fun copy(): A { + val copy = this.javaClass.newInstance() + // do copy stuff + return copy + } +} + +open class B: A() { + + override fun foo() { + println("B") + } + + override fun copy(): B { + val copy = super.copy() as B + // do copy stuff + return copy + } +} + +open class C: B() { + + override fun foo() { + println("C") + } + + override fun copy(): C { + val copy = super.copy() as C + // do copy stuff + return copy + } +} + +open class D: A() { + + override fun foo() { + println("D") + } + + override fun copy(): D { + val copy = super.copy() as D + // do copy stuff + return copy + } +} +``` + +The cast in each `copy()`-method can be avoided by using generics like this (only implementation of `A` and `B` for brevity): + +```kotlin +open class A { + open protected fun copy(clazz: Class): T { + val a = clazz.newInstance() + // do copy stuff + return a + } + + open fun foo() { + println("A") + } + + open fun copy(): A { + return copy(this.javaClass) + } +} + +open class B: A() { + override fun copy(clazz: Class): T { + val b = super.copy(clazz) + // do copy stuff + return b + } + + override fun foo() { + println("B") + } + + override fun copy(): B { + return copy(this.javaClass) + } +} +``` + +Now consider a scenario, where we have an existing compilation unit (module) *M1* that is not under our control, e.g., a third-party library. In M1 a set of interfaces exists, that all inherit from a base interface `A` and have a complex inheritance structure among each other (so very similar to the former issue): + +```kotlin +// compilation unit M1 +interface A +interface B: A +interface C: A +interface D: C +interface E: B, C +// ... +``` + +Our client code of the third-party module M1 is called *M2*. Assume that we need to copy a list of objects conforming to `A` and again call method `foo` on it (dynamically dispatched): + +```kotlin +// compilation unit M2 + +val l = // retrieve some objects conforming to `A`, `B`, ... + +// should output "A\nB\nC\nD" +l.map { it.copy() }.forEach { it.foo() } + +``` + +Unfortunately, the interface currently does not offers neither a copy nor a foo method. So the task is to implement a method `foo` in M2 which takes an object of any subtype of `A` and prints out the name of the class of the extension receiver (if an override is available for this class): + +```kotlin +// (still) compilation unit M2 + +// mark as overridable +open fun A.foo() { + println("A") +} + +//overrides A.foo +override fun B.foo() { + println("B") +} + +//overrides B.foo +override fun C.foo() { + println("C") +} + +//overrides A.foo +override fun D.foo() { + println("D") +} +``` + +Next let us define a corresponding copy method taking an object of any subtype of `A` and returning a copy of that object (using for brevity the variant with cast from above). Of course, if no extension method exists for a subclass/interface of `A` the copy method on `A` will be used: + +```kotlin +// mark as overridable +open fun A.copy(): A { + val copy = this.javaClass.newInstance() + // do copy stuff + return copy +} + +//overrides A.copy +override fun B.copy(): B { + val copy = super.copy() as B + // do copy stuff + return copy +} + +//overrides B.copy +override fun C.copy(): C { + val copy = super.copy() as C + // do copy stuff + return copy +} + +//overrides A.copy +override fun D.copy(): D { + val copy = super.copy() as D + // do copy stuff + return copy +} +``` + +This should **not** be limited to methods with no parameters. It should just behave like overriding normal member methods (parameters are statically dispatched). The same holds for overloading methods in a type (but this works already as expected in extension functions). + +## Outlook + +A possible addition to this proposal would be to allow to override `open` member functions via extension functions for (sub)classes that do not overide the given method of the superclass themselves like this (introducing a new keyword `overload`): + +```kotlin +// M1 +open class A { + open fun foo() { + println("A") + } +} + +open class B: A() + +// M2 +override fun B.foo() { + println("B") +} +``` + +Another addition would be to allow to explicitly overload methods in subtypes if they are defined open like this: + +```kotlin +open class A { + open fun eat(a: A) { + println("delicious") + } +} + +open class B { + overload fun eat(b: B) { + println("tasty") + } +} + +val b: A = B() + +// prints "tasty" +b.eat(b) + +val a = A() + +//prints "delicious" +a.eat(b) +``` + +As overloading currently works on the same class (and not on a type hierarchy), only. This would be something that could be added to extension functions analogously: + +```kotlin +interface A +interface B: A + +open fun A.eat(a: A) { + println("delicious") +} + +overload fun eat(b: B) { + println("tasty") +} +``` + +This is **not** about co- and contravariance, but just about overloading of methods in a type hierarchy. + +## Open Questions + +*Not yet.* From 5171f2fbb4b3f6bd1e19d30e3ccdded3bf0f52a2 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Tue, 13 Sep 2016 16:00:53 +0200 Subject: [PATCH 04/20] Rename overriding-extension-methods.md to override-extension-methods.md --- ...rriding-extension-methods.md => override-extension-methods.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{overriding-extension-methods.md => override-extension-methods.md} (100%) diff --git a/proposals/overriding-extension-methods.md b/proposals/override-extension-methods.md similarity index 100% rename from proposals/overriding-extension-methods.md rename to proposals/override-extension-methods.md From b773eeba43b06925f792d03157ab3a3c7e0854c0 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Tue, 13 Sep 2016 16:16:45 +0200 Subject: [PATCH 05/20] Update override-extension-methods.md --- proposals/override-extension-methods.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 020cd28f1..c29510d18 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -35,7 +35,6 @@ fun main(args: Array) { } open class A { - open fun foo() { println("A") } @@ -48,7 +47,6 @@ open class A { } open class B: A() { - override fun foo() { println("B") } @@ -61,7 +59,6 @@ open class B: A() { } open class C: B() { - override fun foo() { println("C") } @@ -74,7 +71,6 @@ open class C: B() { } open class D: A() { - override fun foo() { println("D") } From d2fe9d1ae0fbb01bda328b1b136d5aea93c560ac Mon Sep 17 00:00:00 2001 From: juangamnik Date: Wed, 14 Sep 2016 18:11:42 +0200 Subject: [PATCH 06/20] Update override-extension-methods.md Added description for scopes and realization. The description of a realiziation of calls to `super.foo` comes soon. --- proposals/override-extension-methods.md | 118 +++++++++++++++++++++++- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index c29510d18..3f2584bf5 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -119,10 +119,12 @@ open class B: A() { } ``` -Now consider a scenario, where we have an existing compilation unit (module) *M1* that is not under our control, e.g., a third-party library. In M1 a set of interfaces exists, that all inherit from a base interface `A` and have a complex inheritance structure among each other (so very similar to the former issue): +Now consider a scenario, where we have an existing (compilation) unit/module *M1* that is not under our control, e.g., a third-party library. In M1 a set of interfaces exists, that all inherit from a base interface `A` and have a complex inheritance structure among each other (so very similar to the former issue): ```kotlin -// compilation unit M1 +// module M1 +package m1 + interface A interface B: A interface C: A @@ -134,7 +136,8 @@ interface E: B, C Our client code of the third-party module M1 is called *M2*. Assume that we need to copy a list of objects conforming to `A` and again call method `foo` on it (dynamically dispatched): ```kotlin -// compilation unit M2 +// module M2 +package m2 val l = // retrieve some objects conforming to `A`, `B`, ... @@ -146,7 +149,7 @@ l.map { it.copy() }.forEach { it.foo() } Unfortunately, the interface currently does not offers neither a copy nor a foo method. So the task is to implement a method `foo` in M2 which takes an object of any subtype of `A` and prints out the name of the class of the extension receiver (if an override is available for this class): ```kotlin -// (still) compilation unit M2 +// (still) module M2 // mark as overridable open fun A.foo() { @@ -172,6 +175,8 @@ override fun D.foo() { Next let us define a corresponding copy method taking an object of any subtype of `A` and returning a copy of that object (using for brevity the variant with cast from above). Of course, if no extension method exists for a subclass/interface of `A` the copy method on `A` will be used: ```kotlin +// (still) module M2 + // mark as overridable open fun A.copy(): A { val copy = this.javaClass.newInstance() @@ -203,12 +208,115 @@ override fun D.copy(): D { This should **not** be limited to methods with no parameters. It should just behave like overriding normal member methods (parameters are statically dispatched). The same holds for overloading methods in a type (but this works already as expected in extension functions). +The scope of such an extension method (**both** overriding or not) is the same as before. So, if we have a third compilation unit *M3* (or another package `m3`) the extension methods do not interfere with our local ones: + +```kotlin +// module M3 +package m3 + +open fun A.foo() { + println("extension for A in M3.") +} + +val b = B() + +// prints "extension for A in M3." and **not** "B" +b.foo() +``` + +If we import the other extension it is used and name clashes occur as usual: + +```kotlin +// (still) module M3 + +// import all extension methods from module M2 (consider that the package `m2` is in M2) +import m2.* + +// results in an compilation error "This function has the same signature as m2.foo(A)" +open fun.A.foo() { + println("extension for A in M3.") +} +``` + +## Realization + +Technically this can be realized via a single dispatch method with the most general extension receiver type, which uses a `when` expression that takes into account all overriding extension functions in scope (including imported ones) and makes a static call to the most specialized matching function. The respective overriding functions get a leading `_` to distinguish them. The following example shows a realization (what the compiler would output) in *pseudo kotlin*: + +```kotlin +// module M1 +package m1 + +open class A +open class B: A() +open class C: A() +open class D: B() + +// module M2 +package m2 + +fun A.foo() { + when(this) { + is C -> _foo(c = this) + is B -> _foo(b = this) + is A -> _foo(a = this) + } +} + +fun _foo(a: A) { + println("A") +} + +fun _foo(b: B) { + println("B") +} + +fun _foo(c: C) { + println("C") +} + +val l = arrayOf(A(), B(), C(), D()) + +// prints "A\nB\nC\nB\n" since we have no override for `D` +l.forEach { it.foo() } + +// module M3 (in this example we add here the function `D.foo`) +package m3 + +import m2._foo +import m2.A +import m2.B +import m2.C +import m2.D + +fun A.foo() { + when(this) { + is D -> _foo(d = this) + is B -> _foo(b = this) + is C -> _foo(c = this) + is A -> _foo(a = this) + } +} + +fun _foo(d: D) { + println("D") +} + +val l = arrayOf(A(), B(), C(), D()) + +// prints "A\nB\nC\nD\n" +l.forEach { it.foo() } +``` + +If we would not add any override in M3 the dispatch method `A.foo` would be imported instead of redefining it, using the `_foo` as in the example above. + ## Outlook A possible addition to this proposal would be to allow to override `open` member functions via extension functions for (sub)classes that do not overide the given method of the superclass themselves like this (introducing a new keyword `overload`): ```kotlin // M1 +package m1 + open class A { open fun foo() { println("A") @@ -218,6 +326,8 @@ open class A { open class B: A() // M2 +package m2 + override fun B.foo() { println("B") } From 3ea4e3d51a7b7c32337da6f473c7f01f1c62a84e Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 00:05:47 +0200 Subject: [PATCH 07/20] Update override-extension-methods.md Added section "Interplay with type parameters". Adapted examples so that inheritance structure is in all examples the same. --- proposals/override-extension-methods.md | 46 ++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 3f2584bf5..ed7c2d2ce 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -238,6 +238,42 @@ open fun.A.foo() { } ``` +## Interplay with Type Parameters + +A nice sideeffect of this proposal for extension functions with a type parameter as extension receiver could be, that you can do something, that is not possible with member functions, easily: + +```kotlin +// (alternative variant of) module M2 + +// mark as overridable +open fun T.copy(): T { + val copy = this.javaClass.newInstance() + // do copy stuff + return copy +} + +//overrides `fun T.copy` +override fun T.copy(): T { + val copy = super.copy() + // do copy stuff + return copy +} + +//overrides `fun T.copy` +override fun T.copy(): T { + val copy = super.copy() + // do copy stuff + return copy +} + +//overrides `fun T.copy` +override fun T.copy(): T { + val copy = super.copy() + // do copy stuff + return copy +} +``` + ## Realization Technically this can be realized via a single dispatch method with the most general extension receiver type, which uses a `when` expression that takes into account all overriding extension functions in scope (including imported ones) and makes a static call to the most specialized matching function. The respective overriding functions get a leading `_` to distinguish them. The following example shows a realization (what the compiler would output) in *pseudo kotlin*: @@ -248,8 +284,8 @@ package m1 open class A open class B: A() -open class C: A() -open class D: B() +open class C: B() +open class D: A() // module M2 package m2 @@ -276,7 +312,7 @@ fun _foo(c: C) { val l = arrayOf(A(), B(), C(), D()) -// prints "A\nB\nC\nB\n" since we have no override for `D` +// prints "A\nB\nC\nA\n" since we have no override for `D` l.forEach { it.foo() } // module M3 (in this example we add here the function `D.foo`) @@ -291,8 +327,8 @@ import m2.D fun A.foo() { when(this) { is D -> _foo(d = this) - is B -> _foo(b = this) is C -> _foo(c = this) + is B -> _foo(b = this) is A -> _foo(a = this) } } @@ -378,4 +414,4 @@ This is **not** about co- and contravariance, but just about overloading of meth ## Open Questions -*Not yet.* +* Is overriding as shown in section "Interplay with Type Parameters" ok? From b1d880cf8450ba397d84bc533cda612125c8d172 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 10:01:53 +0200 Subject: [PATCH 08/20] Update override-extension-methods.md Added kotlin-code to the realization example --- proposals/override-extension-methods.md | 55 ++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index ed7c2d2ce..6bc48fde5 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -276,7 +276,7 @@ override fun T.copy(): T { ## Realization -Technically this can be realized via a single dispatch method with the most general extension receiver type, which uses a `when` expression that takes into account all overriding extension functions in scope (including imported ones) and makes a static call to the most specialized matching function. The respective overriding functions get a leading `_` to distinguish them. The following example shows a realization (what the compiler would output) in *pseudo kotlin*: +Technically this can be realized via a single dispatch method with the most general extension receiver type, which uses a `when` expression that takes into account all overriding extension functions in scope (including imported ones) and makes a static call to the most specialized matching function. The respective overriding functions get a leading `_` to distinguish them. The following example shows a realization (what the compiler would output) in *pseudo kotlin*. First, let us see how the code we give the compiler would look like: ```kotlin // module M1 @@ -289,6 +289,54 @@ open class D: A() // module M2 package m2 +import m1.* + +fun A.foo() { + println("A") +} + +fun B.foo() { + println("B") +} + +fun C.foo() { + println("C") +} + +val l = arrayOf(A(), B(), C(), D()) + +// prints "A\nB\nC\nA\n" since we have no override for `D` +l.forEach { it.foo() } + +// module M3 (in this example we add here the function `D.foo`) +package m3 +import m1.* +import m2.* + +fun D.foo() { + println("D") +} + +val l = arrayOf(A(), B(), C(), D()) + +// prints "A\nB\nC\nD\n" +l.forEach { it.foo() } +``` + +Second, we take a look at the compilers pseudo kotlin output would look like (it is not really "pseudo" as it compiles and works in kotlin 1.0.3 😇): + +```kotlin +// module M1 +package m1 + +open class A +open class B: A() +open class C: B() +open class D: A() + +// module M2 +package m2 +import m1.* fun A.foo() { when(this) { @@ -319,10 +367,7 @@ l.forEach { it.foo() } package m3 import m2._foo -import m2.A -import m2.B -import m2.C -import m2.D +import m1.* fun A.foo() { when(this) { From 0c200d60d82a810627425775e0ccec2d7f9c41d8 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 10:04:21 +0200 Subject: [PATCH 09/20] Update override-extension-methods.md Added missing `open` and `override` keywords --- proposals/override-extension-methods.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 6bc48fde5..ac59e28df 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -291,15 +291,15 @@ open class D: A() package m2 import m1.* -fun A.foo() { +open fun A.foo() { println("A") } -fun B.foo() { +override fun B.foo() { println("B") } -fun C.foo() { +override fun C.foo() { println("C") } @@ -313,7 +313,7 @@ package m3 import m1.* import m2.* -fun D.foo() { +override fun D.foo() { println("D") } From 3bcb634a1f6ffd9bb25d418c338952a3d0740aa6 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 10:37:46 +0200 Subject: [PATCH 10/20] Update override-extension-methods.md Description of possible realization of `super`-function --- proposals/override-extension-methods.md | 118 ++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index ac59e28df..15037bd56 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -390,6 +390,124 @@ l.forEach { it.foo() } If we would not add any override in M3 the dispatch method `A.foo` would be imported instead of redefining it, using the `_foo` as in the example above. +Next, let us take a look at the realization of `super`-calls in overriden extension methods. Take this enhanced example in (future-) kotlin: + +```kotlin +// module M1 +package m1 + +open class A +open class B: A() +open class C: B() +open class D: A() + +// module M2 +package m2 +import m1.* + +open fun A.foo() { + print("A") +} + +override fun B.foo() { + super.foo() + print("B") +} + +override fun C.foo() { + super.foo() + print("C") +} + +val l = arrayOf(A(), B(), C(), D()) + +// prints "A\nAB\nABC\nA" since we have no override for `D` +l.forEach { it.foo() } + +// module M3 (in this example we add here the function `D.foo`) +package m3 +import m1.* +import m2.* + +override fun D.foo() { + super.foo() + print("D") +} + +val l = arrayOf(A(), B(), C(), D()) + +// prints "A\nAB\nABC\nAD" +l.forEach { it.foo(); println() } +``` + +The realization in pseudo kotlin compiler output would add an additional implicit parameter to all overriden `_*` methods which retrieves the jump address (here it is a lambda) to the super function (this again compiles well in kotlin 1.0.3 😇: + +```kotlin +// module M1 +package m1 + +open class A +open class B: A() +open class C: B() +open class D: A() + +// module M2 +package m2 +import m1.* + +fun A.foo() { + when(this) { + is C -> _foo(c = this, superFunction = { c: C -> _foo(b = c, superFunction = { b: B -> _foo(a = b) }) }) + is B -> _foo(b = this, superFunction = { b: B -> _foo(a = b) }) + is A -> _foo(a = this) + } +} + +fun _foo(a: A) { + print("A") +} + +fun _foo(b: B, superFunction: (B) -> Unit) { + superFunction(b) + print("B") +} + +fun _foo(c: C, superFunction: (C) -> Unit) { + superFunction(c) + print("C") +} + +val l = arrayOf(A(), B(), C(), D()) + +// prints "A\nAB\nABC\nA" since we have no override for `D` +l.forEach { it.foo() } + +// module M3 (in this example we add here the function `D.foo`) +package m3 + +import m2._foo +import m1.* + +fun A.foo() { + when(this) { + is D -> _foo(d = this, superFunction = { d: D -> _foo(a = d) }) + is C -> _foo(c = this, superFunction = { c: C -> _foo(b = c, superFunction = { b: B -> _foo(a = b) }) }) + is B -> _foo(b = this, superFunction = { b: B -> _foo(a = b) }) + is A -> _foo(a = this) + } +} + +fun _foo(d: D, superFunction: (D) -> Unit) { + superFunction(d) + print("D") +} + +val l = arrayOf(A(), B(), C(), D()) + +// prints "A\nAB\nABC\nAD" +l.forEach { it.foo() } +``` + ## Outlook A possible addition to this proposal would be to allow to override `open` member functions via extension functions for (sub)classes that do not overide the given method of the superclass themselves like this (introducing a new keyword `overload`): From a9f9e9f9dd7a617f6d2b097ec37b5c0b42a388ad Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 10:47:38 +0200 Subject: [PATCH 11/20] Update override-extension-methods.md Added section Further discussion. --- proposals/override-extension-methods.md | 47 ++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 15037bd56..21cc23d66 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -575,6 +575,51 @@ overload fun eat(b: B) { This is **not** about co- and contravariance, but just about overloading of methods in a type hierarchy. +## Further Discussion + +This feature is completely additive as the current behavior stays the same although I think that the current overload behavior of extension functions in kotlin is inconsistent with member functions: It is allowed to overload an extension function with a function of the same signature (and statically dispatched) but not on member functions (when omitting the open keyword). It would be more consistent to allow this on member functions, too (which is completly additive): + +```kotlin +open class A { + fun foo() { + println("A") + } +} + +class B: A { + // should be allowed and overload A.foo since A.foo is not `open`. + // If A.foo would have been `open`, this would be denied and the + // keyword `override` is required (and then it behaves like a normal + // overriden method). + fun foo() { + println("B") + } +} + +val b: A = B() +b.foo() // prints "A" +val b2 = B() +b2.foo() // prints "B" + +// rationale, this is perfectly ok in current kotlin: + +open class A +class B: A + +fun A.foo() { + println("A") +} + +fun B.foo() { + println("B") +} + +val b: A = B() +b.foo() // prints "A" +val b2 = B() +b2.foo() // prints "B" +``` + ## Open Questions -* Is overriding as shown in section "Interplay with Type Parameters" ok? +Is overriding as shown in section "Interplay with Type Parameters" ok? From 1f8ffc505def2480bb99a891228da3f1c6784de5 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 16:04:49 +0200 Subject: [PATCH 12/20] Update override-extension-methods.md Added a paragraph on "local method override" --- proposals/override-extension-methods.md | 53 ++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 21cc23d66..98ca9d0c3 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -238,6 +238,55 @@ open fun.A.foo() { } ``` +Local overrides should be possible, too, but will be visible only in the scope of the method: + +```kotlin +interface A +interface B : A + +open fun A.bar() { + print("A") +} + +fun B.foo() { + override fun B.bar() { + print("B") + } + + this.bar() +} + +// prints "B" +B().foo() +``` + +In order to be consistent with the current behavior in Kotlin, for inline methods the scope of the declaration-site should be used: + +```kotlin +interface A +interface B : A + +open fun A.bar() { + print("A") +} + +inline fun T.doBar() { + bar() +} + +fun B.foo() { + override fun B.bar() { + print("B") + } + doBar() +} + +// prints "A" +B().foo() +``` + +If [proposal 35](https://github.com/Kotlin/KEEP/pull/35) becomes accepted, the above mentioned behavior should change, of course. Then "inlining" would mean to really copy the body of the method and run the typechecker on the resulting code afterwards. So, the above code should print "B" then, too. + ## Interplay with Type Parameters A nice sideeffect of this proposal for extension functions with a type parameter as extension receiver could be, that you can do something, that is not possible with member functions, easily: @@ -274,7 +323,7 @@ override fun T.copy(): T { } ``` -## Realization +## Possible Realization Technically this can be realized via a single dispatch method with the most general extension receiver type, which uses a `when` expression that takes into account all overriding extension functions in scope (including imported ones) and makes a static call to the most specialized matching function. The respective overriding functions get a leading `_` to distinguish them. The following example shows a realization (what the compiler would output) in *pseudo kotlin*. First, let us see how the code we give the compiler would look like: @@ -508,6 +557,8 @@ val l = arrayOf(A(), B(), C(), D()) l.forEach { it.foo() } ``` +For "local method overrides" (see above) a `A.foo` dispatch function can be generated into the sourrounding function, which automatically has the correct scope. + ## Outlook A possible addition to this proposal would be to allow to override `open` member functions via extension functions for (sub)classes that do not overide the given method of the superclass themselves like this (introducing a new keyword `overload`): From 667c72de274d7e37f6d2acc64f26acdc46d277a7 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 16:32:55 +0200 Subject: [PATCH 13/20] Update override-extension-methods.md Added section "Alternative Realization" as well as some minor refinements --- proposals/override-extension-methods.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 98ca9d0c3..7c693e0df 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -260,7 +260,7 @@ fun B.foo() { B().foo() ``` -In order to be consistent with the current behavior in Kotlin, for inline methods the scope of the declaration-site should be used: +In order to be consistent with the current behavior in Kotlin, for inline methods the scope of the declaration-site could be used (although I dislike this... see open questions): ```kotlin interface A @@ -285,7 +285,7 @@ fun B.foo() { B().foo() ``` -If [proposal 35](https://github.com/Kotlin/KEEP/pull/35) becomes accepted, the above mentioned behavior should change, of course. Then "inlining" would mean to really copy the body of the method and run the typechecker on the resulting code afterwards. So, the above code should print "B" then, too. +If [proposal 35](https://github.com/Kotlin/KEEP/pull/35) becomes accepted (or a V-table is used... see "Alternative Realization"), the above mentioned behavior should change, of course. Then "inlining" would mean to really copy the body of the method and run the typechecker on the resulting code afterwards. So, the above code should print "B" then, too. ## Interplay with Type Parameters @@ -557,7 +557,16 @@ val l = arrayOf(A(), B(), C(), D()) l.forEach { it.foo() } ``` -For "local method overrides" (see above) a `A.foo` dispatch function can be generated into the sourrounding function, which automatically has the correct scope. +For "local method overrides" (see above) a `A.foo` dispatch function can be generated into the sourrounding function, which automatically has the correct scope (if inline methods should resolve a call to an overriden extension function at declaration site... see open questions). + +## Alternative Realization + +The possible realization in the example above shows the semantics in a translational fashion very well, but has a few major drawbacks: + +1. Imagine you add in the example of the section "Possible Realization" M2 with another class `E` and a corresponding override of the `foo`-function. Now you have to recompile M3 in order to update the local `A.foo` dispatch function implementation, accordingly. This means, existing generated code will break just because a dependency has been changed. This is bad. +2. Consider you have more than one compilation unit (e.g. *M2'*, *M2''*, ...) that have overrides of `foo` in package `m2`, but during compilation of M3 only M2 is on the compilation path, and at runtime M2', M2'' are loaded, too. Then the generated dispatch function `A.foo` in M3 will behave oddly. + +Hence, this realization should **not** be used. Instead a V-table (V stands for virtual) approach should be used, which contains a mapping "Scope -> Type -> Method" and instead of calling a dispatch function `A.foo` a call to `A.foo` will generate to a lookup in the V-table and call the respective `_foo`-function. Done right this implementation can circumvent the "bad" behavior of local overides in combination with inline functions as shown above without the need to realize [proposal35](https://github.com/Kotlin/KEEP/pull/35). ## Outlook @@ -659,7 +668,6 @@ class B: A fun A.foo() { println("A") -} fun B.foo() { println("B") @@ -673,4 +681,5 @@ b2.foo() // prints "B" ## Open Questions -Is overriding as shown in section "Interplay with Type Parameters" ok? +* Is overriding as shown in section "Interplay with Type Parameters" ok? +* Should calls to extension functions in inline functions be resolved at call- or declaration-site? I vote for call-site From 2c145cc9b8da583f07bb3c5418505883f98e7049 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 16:39:11 +0200 Subject: [PATCH 14/20] Update override-extension-methods.md Refined usage of notion "compilation unit", "module", and "package" --- proposals/override-extension-methods.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 7c693e0df..52fbd080a 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -119,7 +119,7 @@ open class B: A() { } ``` -Now consider a scenario, where we have an existing (compilation) unit/module *M1* that is not under our control, e.g., a third-party library. In M1 a set of interfaces exists, that all inherit from a base interface `A` and have a complex inheritance structure among each other (so very similar to the former issue): +Now consider a scenario, where we have an existing compilation unit/module *M1* that is not under our control, e.g., a third-party library. For now, we have exactly one package per compilation unit equally named (with first character in lower case). For ease of presentation code snippets may contain code of different packages and even compilation units, but the areas are separated by comments. Section "Alternative Realization" elaborates on scenarios, where different compilation units use the same package. In M1 a set of interfaces exists, that all inherit from a base interface `A` and have a complex inheritance structure among each other (so very similar to the former issue): ```kotlin // module M1 @@ -208,7 +208,7 @@ override fun D.copy(): D { This should **not** be limited to methods with no parameters. It should just behave like overriding normal member methods (parameters are statically dispatched). The same holds for overloading methods in a type (but this works already as expected in extension functions). -The scope of such an extension method (**both** overriding or not) is the same as before. So, if we have a third compilation unit *M3* (or another package `m3`) the extension methods do not interfere with our local ones: +The scope of such an extension method (**both** overriding or not) is the same as before. So, if we have a third compilation unit *M3* (with another package `m3`) the extension methods do not interfere with our local ones: ```kotlin // module M3 From 44927eaab5a7a60c62093ae350bac70482945c81 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 18:06:56 +0200 Subject: [PATCH 15/20] Update override-extension-methods.md Fixed some mistakes in section "Outlook" --- proposals/override-extension-methods.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 52fbd080a..172f63548 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -566,11 +566,11 @@ The possible realization in the example above shows the semantics in a translati 1. Imagine you add in the example of the section "Possible Realization" M2 with another class `E` and a corresponding override of the `foo`-function. Now you have to recompile M3 in order to update the local `A.foo` dispatch function implementation, accordingly. This means, existing generated code will break just because a dependency has been changed. This is bad. 2. Consider you have more than one compilation unit (e.g. *M2'*, *M2''*, ...) that have overrides of `foo` in package `m2`, but during compilation of M3 only M2 is on the compilation path, and at runtime M2', M2'' are loaded, too. Then the generated dispatch function `A.foo` in M3 will behave oddly. -Hence, this realization should **not** be used. Instead a V-table (V stands for virtual) approach should be used, which contains a mapping "Scope -> Type -> Method" and instead of calling a dispatch function `A.foo` a call to `A.foo` will generate to a lookup in the V-table and call the respective `_foo`-function. Done right this implementation can circumvent the "bad" behavior of local overides in combination with inline functions as shown above without the need to realize [proposal35](https://github.com/Kotlin/KEEP/pull/35). +Hence, this realization should **not** be used. Instead a V-table (V stands for virtual) approach should be used, which contains a mapping "Scope -> Type -> Method" and instead of calling a dispatch function `A.foo` a call to `A.foo` will generate to a lookup in the V-table and call the respective `_foo`-function. Done right this implementation can circumvent the "bad" behavior of local overrides in combination with inline functions as shown above without the need to realize [proposal35](https://github.com/Kotlin/KEEP/pull/35). ## Outlook -A possible addition to this proposal would be to allow to override `open` member functions via extension functions for (sub)classes that do not overide the given method of the superclass themselves like this (introducing a new keyword `overload`): +A possible addition to this proposal would be to allow to override `open` member functions via extension functions for (sub)classes that do not override the given method with a member method themselves, like this: ```kotlin // M1 @@ -592,7 +592,7 @@ override fun B.foo() { } ``` -Another addition would be to allow to explicitly overload methods in subtypes if they are defined open like this: +Another addition would be to allow to explicitly overload methods in subtypes (introducing a new keyword `overload`) if they are defined open like this: ```kotlin open class A { From dc01e0e03858bf9ec3e56c64de7a41eaac06e4c7 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 18:16:53 +0200 Subject: [PATCH 16/20] Update override-extension-methods.md local link test --- proposals/override-extension-methods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 172f63548..fca655b77 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -119,7 +119,7 @@ open class B: A() { } ``` -Now consider a scenario, where we have an existing compilation unit/module *M1* that is not under our control, e.g., a third-party library. For now, we have exactly one package per compilation unit equally named (with first character in lower case). For ease of presentation code snippets may contain code of different packages and even compilation units, but the areas are separated by comments. Section "Alternative Realization" elaborates on scenarios, where different compilation units use the same package. In M1 a set of interfaces exists, that all inherit from a base interface `A` and have a complex inheritance structure among each other (so very similar to the former issue): +Now consider a scenario, where we have an existing compilation unit/module *M1* that is not under our control, e.g., a third-party library. For now, we have exactly one package per compilation unit equally named (with first character in lower case). For ease of presentation code snippets may contain code of different packages and even compilation units, but the areas are separated by comments. Section [Alternative Realization](#alternative-realization) elaborates on scenarios, where different compilation units use the same package. In M1 a set of interfaces exists, that all inherit from a base interface `A` and have a complex inheritance structure among each other (so very similar to the former issue): ```kotlin // module M1 From ba92ff5f5cf8230cc1e0ef2bc666aef216da0505 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 18:21:29 +0200 Subject: [PATCH 17/20] Update override-extension-methods.md Added internal links between sections --- proposals/override-extension-methods.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index fca655b77..aab2eaa6b 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -260,7 +260,7 @@ fun B.foo() { B().foo() ``` -In order to be consistent with the current behavior in Kotlin, for inline methods the scope of the declaration-site could be used (although I dislike this... see open questions): +In order to be consistent with the current behavior in Kotlin, for inline methods the scope of the declaration-site could be used (although I dislike this... see [Open Questions](#open-questions)): ```kotlin interface A @@ -285,7 +285,7 @@ fun B.foo() { B().foo() ``` -If [proposal 35](https://github.com/Kotlin/KEEP/pull/35) becomes accepted (or a V-table is used... see "Alternative Realization"), the above mentioned behavior should change, of course. Then "inlining" would mean to really copy the body of the method and run the typechecker on the resulting code afterwards. So, the above code should print "B" then, too. +If [proposal 35](https://github.com/Kotlin/KEEP/pull/35) becomes accepted (or a V-table is used... see [Alternative Realization](#alternative-realization)), the above mentioned behavior should change, of course. Then "inlining" would mean to really copy the body of the method and run the typechecker on the resulting code afterwards. So, the above code should print "B" then, too. ## Interplay with Type Parameters @@ -557,16 +557,16 @@ val l = arrayOf(A(), B(), C(), D()) l.forEach { it.foo() } ``` -For "local method overrides" (see above) a `A.foo` dispatch function can be generated into the sourrounding function, which automatically has the correct scope (if inline methods should resolve a call to an overriden extension function at declaration site... see open questions). +For "local method overrides" (see above) a `A.foo` dispatch function can be generated into the sourrounding function, which automatically has the correct scope (if inline methods should resolve a call to an overriden extension function at declaration site... see [Open Questions](#open-questions]). ## Alternative Realization The possible realization in the example above shows the semantics in a translational fashion very well, but has a few major drawbacks: -1. Imagine you add in the example of the section "Possible Realization" M2 with another class `E` and a corresponding override of the `foo`-function. Now you have to recompile M3 in order to update the local `A.foo` dispatch function implementation, accordingly. This means, existing generated code will break just because a dependency has been changed. This is bad. +1. Imagine you add in the example of the section [Possible Realization](#possible-realization) M2 with another class `E` and a corresponding override of the `foo`-function. Now you have to recompile M3 in order to update the local `A.foo` dispatch function implementation, accordingly. This means, existing generated code will break just because a dependency has been changed. This is bad. 2. Consider you have more than one compilation unit (e.g. *M2'*, *M2''*, ...) that have overrides of `foo` in package `m2`, but during compilation of M3 only M2 is on the compilation path, and at runtime M2', M2'' are loaded, too. Then the generated dispatch function `A.foo` in M3 will behave oddly. -Hence, this realization should **not** be used. Instead a V-table (V stands for virtual) approach should be used, which contains a mapping "Scope -> Type -> Method" and instead of calling a dispatch function `A.foo` a call to `A.foo` will generate to a lookup in the V-table and call the respective `_foo`-function. Done right this implementation can circumvent the "bad" behavior of local overrides in combination with inline functions as shown above without the need to realize [proposal35](https://github.com/Kotlin/KEEP/pull/35). +Hence, this realization should **not** be used. Instead a V-table (V stands for virtual) approach should be used, which contains a mapping "Scope -> Type -> Method" and instead of calling a dispatch function `A.foo` a call to `A.foo` will generate to a lookup in the V-table and call the respective `_foo`-function. Done right this implementation can circumvent the "bad" behavior of local overrides in combination with inline functions as shown above without the need to realize [proposal 35](https://github.com/Kotlin/KEEP/pull/35). ## Outlook From 6efd298e3b8873c97e1415f92e845b4778b14a79 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Thu, 15 Sep 2016 18:25:30 +0200 Subject: [PATCH 18/20] Update override-extension-methods.md minor fix --- proposals/override-extension-methods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index aab2eaa6b..310e1ad06 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -557,7 +557,7 @@ val l = arrayOf(A(), B(), C(), D()) l.forEach { it.foo() } ``` -For "local method overrides" (see above) a `A.foo` dispatch function can be generated into the sourrounding function, which automatically has the correct scope (if inline methods should resolve a call to an overriden extension function at declaration site... see [Open Questions](#open-questions]). +For "local method overrides" (see above) a `A.foo` dispatch function can be generated into the sourrounding function, which automatically has the correct scope (if inline methods should resolve a call to an overriden extension function at declaration site... see [Open Questions](#open-questions)). ## Alternative Realization From 747ca77aace9c9c91a647e2b53720a5b215a6273 Mon Sep 17 00:00:00 2001 From: juangamnik Date: Fri, 16 Sep 2016 09:36:08 +0200 Subject: [PATCH 19/20] Update override-extension-methods.md More precise description of inlining. --- proposals/override-extension-methods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 310e1ad06..8ea39be4a 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -285,7 +285,7 @@ fun B.foo() { B().foo() ``` -If [proposal 35](https://github.com/Kotlin/KEEP/pull/35) becomes accepted (or a V-table is used... see [Alternative Realization](#alternative-realization)), the above mentioned behavior should change, of course. Then "inlining" would mean to really copy the body of the method and run the typechecker on the resulting code afterwards. So, the above code should print "B" then, too. +If [proposal 35](https://github.com/Kotlin/KEEP/pull/35) becomes accepted (or a V-table is used... see [Alternative Realization](#alternative-realization)), the above mentioned behavior should change, of course. Then "inlining" would behave as if the body of the method has been copied and the typechecker has been run on the resulting code afterwards (although this would be realized via copying annotated bytecode, where the annotated statements are replaced during inlining). So, the above code should print "B" then, too. ## Interplay with Type Parameters From 0228a9d4bb3c840309025d1ec870ca395fef083d Mon Sep 17 00:00:00 2001 From: juangamnik Date: Fri, 16 Sep 2016 09:37:06 +0200 Subject: [PATCH 20/20] Update override-extension-methods.md --- proposals/override-extension-methods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/override-extension-methods.md b/proposals/override-extension-methods.md index 8ea39be4a..f7d93ad0b 100644 --- a/proposals/override-extension-methods.md +++ b/proposals/override-extension-methods.md @@ -8,7 +8,7 @@ ## Feedback -Discussion has been started in [this pull request](https://github.com/Kotlin/KEEP/pull/35) regarding type-checking of reified type parameters so far. Further discussion should be done in [the pull request for this proposal](https://github.com/Kotlin/KEEP/pull/46). An issue in the KEEP project has to be opened. +Discussion has been started in [pull request 35](https://github.com/Kotlin/KEEP/pull/35) regarding type-checking of reified type parameters so far. Further discussion should be done in [the pull request for this proposal (46)](https://github.com/Kotlin/KEEP/pull/46). An issue in the KEEP project has to be opened. ## Summary