> On 21 Jul 2016, at 03:41, Brent Royal-Gordon <[email protected]> wrote:
>
>> On Jul 20, 2016, at 3:26 PM, Karl <[email protected]> wrote:
>>
>>> Sealed is *non-committal*. It makes no promises to wider scopes about
>>> whether there are other subclasses/overrides; it merely states that code
>>> outside the module may not subclass/override. `final`, on the other hand,
>>> is an *affirmative* statement: there are no subclasses/overrides and there
>>> never will be. Code outside the module is permitted to rely on that
>>> fact—for instance, it can generate static calls and conflate dynamic Self
>>> with static Self in conformances.
>>
>> This is exactly what I'm talking about - this is actually a very simple
>> discussion. Throwing around words like “non-committal” and “affirmative” and
>> speaking abstractly doesn’t disguise the brunt of what you’re saying:
>> limiting “open” to public classes only lets you be sloppy inside your own
>> module. That’s the only reason to make it like that. If I were describing
>> the concept of “sloppiness” while trying my hardest not to use the word
>> itself, I would probably say pretty much what you just wrote - wanting to
>> remain non-committal, avoid definite, affirmative statements, etc.
>
> No, that is *not* the only reason. The other reason is library evolution.
>
> A class that is closed in 1.0 can be opened in 1.1; a class that is `final`
> in 1.0 *cannot* be opened in 1.1 (or at least it's a breaking change if it
> is). This is a really important distinction for binary compatibility—but
> binary compatibility only matters at the public boundary. An
> `internal`-or-less class does not have to worry about maintaining a
> compatible binary interface between different versions, but a `public` class
> does.
>
> When I say it's "non-commital", that's what I mean: It does not commit future
> versions of the module to either permit or forbid subclasses. A future
> version can either make it `open` or `final` as the module's evolving design
> demands. A `final` class must remain `final` forever. It's the difference
> between a door that's locked and a door that's bricked up.
>
> Yes, it is *also* true that sealed-by-default permits sloppiness as long as
> it's contained to internal scope. But that's quite typical of Swift. It's why
> the default access control is `internal`, not `private` (the minimally-sloppy
> solution) or `public` (the minimally-bureaucratic solution). But these are
> two independent arguments for sealed. Railing against internal sloppiness
> doesn't change the library evolution argument.
>
> --
> Brent Royal-Gordon
> Architechies
>
I would be okay with the inferred situation for classes being a semantic
“final”. That is, that they cannot be subclassed, but they won’t be
automatically optimised in a fragile way either. We could call it “sealed” if
you want to explicitly specify it - the difference is that it doesn’t only
apply at the module boundary, and it’s just an annotation for the external
modules’ type-checker that it shouldn’t allow this - it’s not license for the
internal module’s compiler to give up flexibility. So:
public class Foo {} // Implicitly “sealed”. Cannot be
subclassed anywhere. Does not provide optimiser guarantees of “final”.
public(sealed) class Foo {} // as above
public final class Foo {} // Implicitly “sealed”. Cannot be
subclassed anywhere. Allows resilience-breaking optimisations.
public(sealed) final class Foo {} // as above
public internal(open) class Foo {} // “open” overrides
“sealed” for the internal scope. Cannot be subclassed externally; may be
subclassed internally. Does not provide optimiser guarantees of “final”.
public(sealed) internal(open) class Foo {} // as above
public(sealed) internal(open) final class Foo {} // Error: A class cannot be
both open and final
I believe that would meet the goals of:
- Not allowing subclassing from external modules unless explicitly allowed (the
original goal)
- Making classes which are internally-subclassed easier to locally reason about
(my nice-to-have)
- Maintain binary compatibility
- Do not give up binary flexibility unless the user explicitly asks for it (in
the LibraryEvolution docs)
Is there anything I missed?
Karl_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution