From b08e3fedebd0d56b4272ba344be2c1b4fab965ad Mon Sep 17 00:00:00 2001 From: josh11b Date: Tue, 26 Oct 2021 09:25:30 -0700 Subject: [PATCH] Support `external impl` in class and adapter scopes. (#905) --- docs/design/generics/details.md | 65 +++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/docs/design/generics/details.md b/docs/design/generics/details.md index f3c8eb74d2ba..cd491eb77021 100644 --- a/docs/design/generics/details.md +++ b/docs/design/generics/details.md @@ -449,7 +449,49 @@ external impl Point3 as Vector { With this definition, `Point3` includes `Add` in its API but not `Scale`, while `Point3 as Vector` includes both. This maintains the property that you can -determine the API of a type by looking at its definition. +determine the API of a type by looking at its definition. In this case, the +`external impl` may be defined lexically inside the scope of the class. + +``` +class Point3 { + var x: Double; + var y: Double; + fn Add[me: Self](b: Self) -> Self { + return {.x = a.x + b.x, .y = a.y + b.y}; + } + // Type before `as` is optional and defaults to the current class. + external impl as Vector { + alias Add = Point3.Add; // Syntax TBD + fn Scale[me: Self](v: Double) -> Self { + return {.x = a.x * v, .y = a.y * v}; + } + } +} + +// OR: + +class Point3 { + var x: Double; + var y: Double; + external impl as Vector { + fn Add[me: Self](b: Self) -> Self { + return {.x = a.x + b.x, .y = a.y + b.y}; + } + fn Scale[me: Self](v: Double) -> Self { + return {.x = a.x * v, .y = a.y * v}; + } + } + alias Add = Vector.Add; // Syntax TBD +} +``` + +Being defined lexically inside the class means that implementation is available +to other members defined in the class. For example, it would allow implementing +another interface or method that requires this interface to be implemented. + +**Open question:** Do implementations need to be defined lexically inside the +class to get access to private members, or is it sufficient to be defined in the +same library as the class? **Rejected alternative:** We could allow types to have different APIs in different files based on explicit configuration in that file. For example, we @@ -1632,9 +1674,24 @@ Unlike the similar `class B extends A` notation, `adaptor B extends A` is permitted even if `A` is a final class. Also, there is no implicit conversion from `B` to `A`, matching `adapter`...`for` but unlike class extension. -**Future work:** We may need additional mechanisms for changing the API in the -adapter. For example, to resolve conflicts we might want to be able to move the -implementation of a specific interface into an [external impl](#external-impl). +To avoid or resolve name conflicts between interfaces, an `impl` may be declared +[`external`](#external-impl). The names in that interface may then be pulled in +individually or renamed using `alias` declarations. + +``` +adapter SongRenderToPrintDriver extends Song { + // Add a new `Print()` member function. + fn Print[me: Self]() { ... } + + // Avoid name conflict with new `Print` function by making + // the implementation of the `Printable` interface external. + external impl as Printable = Song as Printable; + + // Make the `Print` function from `Printable` available + // under the name `PrintToScreen`. + alias PrintToScreen = Printable.Print; +} +``` ### Use case: Using independent libraries together