Keyword for protocol conformance

Hi Xiaodi and Charles,

As I said, this discussion has already happened several times. I'm literally just repeating what people said eight months ago, six months ago, and four months ago. There's not a good answer to this and perhaps several other issues, which is why I don't see a way forward for the proposal. After all, I was the one proposing the same idea last winter, so I've had a few months to think about it.

I've been following the discussion from the side both now and before. Without being able to cover everything that's been said, I hope this message brings at least a bit of new air into the discussion.

— — —

Here's one more approach that, AFAICT, would enable both retroactive modelling and fairly good error diagnostics: bring the full protocol conformance together with a dedicated definition block which can both define implementations and refer to implementations defined elsewhere.

Details:

1. Allow marking the conformance to a given protocol with a block of code. This could be nested inside a struct/enum/class body or its extension:

    struct Vector {
      conformance Hashable { /* TBD, read on… */ }
    }
    extension Vector {
      conformance CustomStringConvertible { /* … */ }
    }

or at the top level, as a clearly marked single-conformance extension:

    conformance Vector : CustomDebugStringConvertible { /* … */ }

2. Inside the body of that block, all protocol (non-extension) methods must be marked with `override`. That includes both the required interface and the customisable interface with library-provided default implementations:

    struct Vector {
      conformance Hashable {
        override static func ==(lhs: Self, rhs: Self) -> Bool { /* … */ }
        // Ok.

        var hashValue: Int { /* … */ }
        // error: property 'hashValue' is required by Hashable.
        // (Fixit: mark with 'override')

        override static func < (lhs: Self, rhs: Self) -> Bool { /* … */ }
        // error: protocol Hashable does not require 'static func <'.
        // (Fixit #1: remove override. Etc.)
      }
    }

3. When conformance is made explicit as described above, no other part of code is allowed to interfere with that type's conformance to the protocol under question:

    conformance Vector : Collection {
      override var startIndex: Int { return 0 }
      override var endIndex: Int { return _count }
      override subscript(index: Int) -> Double { return _elements[index] }
      override func index(after i: Int) -> Int { return i + 1 }
    }
    extension Vector {
      var count: Int { return _count }
      // error: property 'count' was introduced by explicit conformance to Collection.
      // (Fixit: Add explicit override to the conformance block.)
    }

4a. When using a `conformance` block for retroactive modelling, or to explicitly include methods, properties etc. defined elsewhere, it is enough to list those in the body of the `conformance` block:

    protocol CountHaving {
      associatedtype Count : Integer
      var count: Count
    }
    conformance Array : HasCount {
      override var count: Int // Ok, explicit retroactive modelling.
    }

4b. Any override declarations without a body within an explicit `conformance` block are requirements for the definitions to exist elsewhere. That includes overridden properties without getters and setters. For example, here's an alternative way of fixing the error in #4:

    conformance Vector : Collection {
      override var startIndex: Int { return 0 }
      override var endIndex: Int { return _count }
      override subscript(index: Int) -> Double { return _elements[index] }
      override func index(after i: Int) -> Int { return i + 1 }
      override var count: Int // No body; defined elsewhere.
    }
    extension Vector {
      var count: Int { return _count } // Ok.
    }

5. Just like any other extensions, `conformance` blocks could introduce other non-`override` methods etc. to its needs, but they probably should keep it at minimum.

— — —

Downsides. Now, there are obvious reasons not to introduce this complexity to the language:

− It adds quite a bit of boilerplate to the conformance definition. (OTOH, programmers in a hurry would still be able to use the existing implicit conformance syntax, thereby avoiding the boilerplate on the one hand, and the better diagnostics on the other.)

− It would make it harder to add new required methods with default implementations without breaking code elsewhere. Because explicitly conforming types which already happened to have a matching function, property, or subscript would need to include it in the explicit conformance block. (OTOH, maybe that's what they indeed asked for by making the conformance explicit!)

− It would add yet another keyword to the language, context-specific but regardless. And the amount of `override` noise isn't looking very pretty.

− It possibly complicates some other convenient use of protocols that I can't think of right now. Discuss.

On the plus sides,

+ There are precedents of explicit conformances in other languages: Haskell's type classes <Haskell/Classes and types - Wikibooks, open books for an open world; and Clojure's protocols <clojure.core - Clojure v1.11 API documentation; come to mind as most similar.

+ It better documents how exactly the type conforms to the protocol, without leaving any missing "magic" elsewhere in the codebase. And it would become a compiler-enforced way to the already existing practice of keeping a conformance interface together within an extension block.

Alternatives to consider:

A. Instead of nesting `conformance ProtocolName`, we could say `conformance Self : ProtocolName`.

B. Instead of introducing a new keyword `conformance` for top-level single-conformance extensions, we could state that `private/internal/public` in front of the `extension` keyword is used to define an explicit conformance of one protocol:

    internal extension Vector : Collection { /* just Collection things */ }

— Pyry

That is not exactly what Brent was speaking of. We are talking about this
scenario:

File A:

internal struct S {
  func foo() { }
}

File B:

```
private protocol P {
  func foo()
}

extension P {
  func foo() { }
}

// With your proposal, I can't write the following line:
extension S : P { }
// In file A, S.foo() isn't overriding anything, so I can't add `override`
// But in file B, S can't conform to P,
// because S.foo() isn't overriding P.foo() without `override` in file A

First of all, I cannot take credit for the proposal, as the thread was
started by David, not me, so if the proposal is anyone’s, it’s his. With
that said, what he proposes is:

*Proposal:*
Adding a keyword to the methods conforming protocols. As an example
please check the following piece of code which uses the keyword `conform`
to explicitly indicate that `myMethod` is a method conforming a protocol.

The wording here is that the keyword is needed for methods conforming to
protocols. My reading of that is that:

File A:

protocol P {
func foo()
}

struct S: P {
conform func foo() {
// I am declared as conforming to P, so in adding this method I am doing
so to conform to P. Thus, I need the keyword.
}
}

- - whereas: - -

File A:

struct S {
func foo() {
// I am not declared as conforming to any protocol; I just think that
being able to foo is an ability that I need to have.
}
}

File B:

private protocol P {
func foo()
}

extension S: P {
// The area of contention.
}

- - - - - -

The proposal doesn’t really mention what to do here, so we can argue a
bit about it. There are multiple viewpoints one could take on this. A few
could be:

1. The extension should get some kind of keyword, “retro” or “@retro” or
something better-sounding that someone smarter than I comes up with.

2. The extension is unmarked, but declares foo() inside it with some sort
of annotation to indicate that it represents a method that already exists.

3. Just leave the extension exactly as written, since it’s not declaring
any methods, and thus doesn’t have to indicate what those nonexistent
method declarations conform to.

I began this discussion leaning toward #1, but now I’m starting to
consider #3, since the purpose of the keyword is to declare one’s
intentions. The intentions of an empty extension that does nothing but
conform to a protocol is actually quite clear; the methods have to be
already declared somewhere else, or it makes no sense. At any rate, the
“problem” in your scenario is entirely confined to File B, so if any
annotations are necessary, that is where they belong. File A does not know
about the protocol, it does not know that it is conforming to the protocol,
and indeed, the protocol is none of File A’s business. So since File A is
not intending to conform to the protocol, File A does not have to declare
its intent to conform to the protocol. If we require that, it’s all in File
B’s court.

As I said, this discussion has already happened several times. I'm
literally just repeating what people said eight months ago, six months ago,
and four months ago. There's not a good answer to this and perhaps several
other issues, which is why I don't see a way forward for the proposal.
After all, I was the one proposing the same idea last winter, so I've had a
few months to think about it.

If option (3) were allowed, then no keyword could ever be mandatory; it's
always possible to refactor so that a conformance declaration is an empty
extension. So we're back at an optional keyword, which has its own
problems. This kind of thinking is how I've come to the conclusion that the
status quo, with better diagnostics, is the least bad solution.

Two responses to that thought:

1) In general, I look at these things under the assumption that most
developers are not going to be making efforts to deliberately break the
system, and not to try to expend excessive amounts of time in preventing
them from doing so. Because really, just about *anything* can be worked
around. I can work around the “override” keyword and override things
without it, but that doesn’t mean that I think we should remove the
“override” keyword.

Really? I wasn't aware that you could work around the `override` keyword
(the one that's required for classes). How do you do that?

For that matter, just about *everything* in Objective-C was
workaroundable, and usually rather trivially at that, but that didn’t make
us all go back to typing everything as ‘id’ just because the explicit types
could be spoofed.

Swift, by design, is rather different from Objective-C; it makes rather
stronger guarantees about the facilities it provides. I don't know of very
many (any?) keywords that attempt to enforce safety but are optional. Are
you?

2) Even if you disagree with everything I just said, that’s just as much an

argument for option #1 as for the status quo.

The status quo has the benefit of not being source breaking, and simpler.

···

On Fri, Aug 26, 2016 at 5:08 AM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 26, 2016, at 4:34 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Aug 26, 2016 at 4:24 AM, Charles Srstka <cocoadev@charlessoft.com> > wrote:

On Aug 26, 2016, at 4:02 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Aug 22, 2016, at 4:30 PM, David Cordero via swift-evolution < >> swift-evolution@swift.org> wrote:

Charles

Hi Xiaodi and Charles,

As I said, this discussion has already happened several times. I'm
literally just repeating what people said eight months ago, six months ago,
and four months ago. There's not a good answer to this and perhaps several
other issues, which is why I don't see a way forward for the proposal.
After all, I was the one proposing the same idea last winter, so I've had a
few months to think about it.

I've been following the discussion from the side both now and before.
Without being able to cover everything that's been said, I hope this
message brings at least a bit of new air into the discussion.

— — —

Here's one more approach that, AFAICT, would enable both retroactive
modelling and fairly good error diagnostics: *bring the full protocol
conformance together with a dedicated definition block* which can both
define implementations and refer to implementations defined elsewhere.

This is a very well explained proposal. Thanks for taking the time to write
it out. But I don't think it solves the problem, and I'll respond inline.

*Details:*

1. Allow marking the conformance to a given protocol with a block of code.
This could be nested inside a struct/enum/class body or its extension:

    *struct* Vector {
      *conformance* Hashable { /* TBD, read on… */ }
    }
    *extension* Vector {
      *conformance* CustomStringConvertible { /* … */ }
    }

In general, I think there is an opposition to organizing nested blocks of
members like this (there was strong opposition to proposals for access
level grouping, for instance). But besides that somewhat subjective
opinion, there might also be difficulty in terms of `private` scoping.
These are minor, more speculative points.

Mainly, I think it is confusing because it looks like a nested type and
invites proposals to namespace protocol conforming members (i.e.
`Vector.Hashable.hashValue`), which is not what is going on and definitely
not the direction in which I would want to take this proposal.

or at the top level, as a clearly marked *single-conformance extension*:

    *conformance* Vector : CustomDebugStringConvertible { /* … */ }

This is an improvement. But, you are introducing a second keyword,
`conformance` without justification. The same purpose that you outline
later could be served by the existing spelling `extension Vector :
CustomDebugStringConvertible`, and this has been proposed earlier in this
thread and also in previous threads.

2. Inside the body of that block, all protocol (non-extension) methods
*must* be marked with `override`. That includes both the required
interface and the customisable interface with library-provided default
implementations:

    *struct* Vector {
      *conformance* Hashable {
        *override static func* ==(lhs: *Self*, rhs: *Self*) -> Bool { /*
… */ }
        // Ok.

        *var* hashValue: Int { /* … */ }
        // *error:* property 'hashValue' is required by Hashable.
        // (Fixit: mark with 'override')

        *override static func* < (lhs: *Self*, rhs: *Self*) -> Bool { /*
… */ }
        // *error:* protocol Hashable does not require 'static func <'.
        // (Fixit #1: remove override. Etc.)
      }
    }

This is similar to a previous proposal that any member declared in
extensions that add a conformance must, if that member has the same or
greater visibility than the lesser of the type and protocol, implement a
protocol requirement. However, in your proposal, there's a pervasive
requirement for `override`, which adds noise but doesn't provide an
advantage over that alternative proposal. Moreover, I have a problem with
your proposed spelling: if there is no default implementation, why should
an implementation be said to `override` anything?

3. When conformance is made explicit as described above, no other part of
code is allowed to interfere with that type's conformance to the protocol
under question:

    *conformance* Vector : Collection {
      *override var *startIndex: Int { *return* 0 }
      *override **var* endIndex: Int { *return* _count }
      *override **subscript*(index: Int) -> Double { *return*
_elements[index] }
      *override **func* index(after i: Int) -> Int { *return* i + 1 }
    }
    *extension* Vector {
      *var* count: Int { *return* _count }
      // *error:* property 'count' was introduced by explicit conformance to
Collection.
      // (Fixit: Add explicit override to the conformance block.)
    }

I'm not sure what safety is gained by this particular rule. The language
should make the most simple cases the most straightforward. In the scenario
where a struct S, with only a small number of methods, conforms to protocol
P, which has only a small number of requirements and no default
implementations, it is not confusing at all to put all requirements in the
same block of code. Your proposed syntax, if made mandatory [see below
about mandatory vs. optional], would greatly increase the visual clutter
and difficulty of correctly conforming one simple struct to one simple
protocol.

4a. When using a `conformance` block for retroactive modelling, or to

explicitly include methods, properties etc. defined elsewhere, it is enough
to list those in the body of the `conformance` block:

    *protocol* CountHaving {
      *associatedtype* Count : Integer
      *var* count: Count
    }
    *conformance* Array : HasCount {
      *override var* count: Int // Ok, explicit retroactive modelling.
    }

This might be unworkable for certain types of retroactive modeling. If I
add a new default implementation to a requirement of `Collection`, I would
have to write out conformance statements for *every single Collection type*
in the standard library, probably Foundation and Dispatch also, plus any
other libraries I use, in addition to my own Collection types. The only way
to solve this issue, as far as I can tell, is to say that none of these
keywords are required across module boundaries, which carves out an
exception (which might be justifiable, but it's still an exception to the
rule).

4b. Any override declarations without a body within an explicit
`conformance` block are requirements for the definitions to exist
elsewhere. That includes overridden properties without getters and setters.
For example, here's an alternative way of fixing the error in #4:

    *conformance* Vector : Collection {
      *override var *startIndex: Int { *return* 0 }
      *override **var* endIndex: Int { *return* _count }
      *override **subscript*(index: Int) -> Double { *return* _elements[index]
}
      *override **func* index(after i: Int) -> Int { *return* i + 1 }
      *override var* count: Int // No body; defined elsewhere.
    }
    *extension* Vector {
      *var* count: Int { *return* _count } // Ok.
    }

This is clever, but also confusing (IMO) because you've now introduced more
than one way of splitting up code that fulfills protocol requirements, when
your stated goal is to improve the situation by putting all requirements in
the same place.

Although you've insisted on a central location where there's a catalog of
overrides, it might increase rather than decrease confusion about where
these requirements are being implemented, because now there's at least two
declarations. For derived classes that conform to protocols but inherit
their implementation from a base class, there are additional difficulties:
Does `override` mean you're overriding the base class implementation, or
are you using `override` to say that the base class implementation (which
you're not overriding in the derived class) should override the protocol's
default implementation? Also, even though I see a declaration of a protocol
requirement with no body, I might not find the implementation anywhere in
the derived class, because it's in another file, and I may not even have
access to that source code. In general, it's exceedingly weird that the
derived class would have to re-declare an inherited method with the word
`override`, then never override or mention that method again.

5. Just like any other extensions, `conformance` blocks could introduce

other non-`override` methods etc. to its needs, but they probably should
keep it at minimum.

As mentioned earlier, I think a more preferable and terse rule was proposed
earlier, where extensions that add conformance are not allowed to have
public members (or members equal or more visible than the protocol or type)
that don't implement a requirement.

— — —

*Downsides. *Now, there are obvious reasons not to introduce this
complexity to the language:

− It adds quite a bit of boilerplate to the conformance definition. (OTOH,
programmers in a hurry would still be able to use the existing implicit
conformance syntax, thereby avoiding the boilerplate on the one hand, and
the better diagnostics on the other.)

It does add quite a bit of boilerplate; the idea of having this boilerplate
has been pitched before, though not with the `conformance` keyword. It's
not great, but it might not in and of itself be a deal-breaker.

However, the key weakness here IMO is that you are trying to address the
weaknesses above by saying that this whole new syntax is _optional_. This
is, IMO, a critical flaw. I could _maybe_ get behind a single opt-in
keyword or attribute; but I think a whole new alternative syntax for
something as fundamental as conformance is a non-starter. This is a very,
very essential part of Swift; there should be *one* syntax for protocol
conformance, not two. We should find the one best syntax, though obviously
we are now constrained by backwards compatibility issues.

− It would make it harder to add new required methods with default
implementations without breaking code elsewhere. Because explicitly
conforming types which already happened to have a matching function,
property, or subscript would need to include it in the explicit conformance
block. (OTOH, maybe that's what they indeed asked for by making the
conformance explicit!)

− It would add yet another keyword to the language, context-specific but
regardless. And the amount of `override` noise isn't looking very pretty.

Yes, these two weaknesses are definitely issues. In general, from the
perspective of library evolution, there's a key question to be answered: if
the library vendor adds a new default implementation to a protocol, should
they need to be concerned that it could cause existing code using a
previous version of the library to stop compiling?

Currently, the answer is no, and I think it is the right answer most (if
not all) of the the time. I have this opinion because types that conform to
the protocol using a previous version of the library cannot possibly change
their behavior if a new default implementation is supplied, since these
types necessarily have their own implementation of the same requirement and
since protocol requirements are dynamically dispatched and there's no way
to break that encapsulation. Therefore, it is unnecessarily disruptive to
stop compiling that code in order to force the end user to acknowledge a
change that cannot have any effect on their own code. I conclude that any
keyword should be _optional_ (if you _want_ your code to break
unnecessarily, I guess that can be up to you...), but then see above about
my reasoning regarding an optional syntax for protocol conformance.

− It possibly complicates some other convenient use of protocols that I

···

On Fri, Aug 26, 2016 at 6:35 AM, Pyry Jahkola <pyry.jahkola@iki.fi> wrote:

can't think of right now. Discuss.

*On the plus sides,*

+ There are precedents of explicit conformances in other languages: Haskell's
type classes
<Haskell/Classes and types - Wikibooks, open books for an open world;
and Clojure's protocols
<clojure.core - Clojure v1.11 API documentation; come
to mind as most similar.

+ It better documents how exactly the type conforms to the protocol,
without leaving any missing "magic" elsewhere in the codebase. And it would
become a compiler-enforced way to the already existing practice of keeping
a conformance interface together within an extension block.

*Alternatives to consider:*

A. Instead of nesting `conformance ProtocolName`, we could say
`conformance Self : ProtocolName`.

B. Instead of introducing a new keyword `conformance` for top-level
single-conformance extensions, we could state that
`private/internal/public` in front of the `extension` keyword is used to
define an explicit conformance of one protocol:

* internal extension* Vector : Collection { /* just Collection things
*/ }

— Pyry

By implementing the subclass’s method before the superclass’s. You can try this yourself:

- - - Library code: - - -

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
    }
}

- - - App code: - - -

import FooLibrary

class Subclass: Superclass {
    func bar() { print("bar called in the app") }
}

let obj = Subclass()

obj.foo()

- - - output: - - -

foo called in library
Program ended with exit code: 0

- - - - - -

Now: Change the library code to:

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
        bar()
    }
    
    // Hey look, I didn't even use that stupid new 'open' keyword.
    public func bar() { print("bar called in library") }
}

- - - Run the app again without compiling it, and: - - -

foo called in library
bar called in the app
Program ended with exit code: 0

- - -

Voilà: I overrode a method (a supposedly non-overridable one, at that) with no “override” keyword. Just as File A in your earlier example can implement a protocol method without realizing it, Subclass here has unintentionally overridden a superclass method. This is because ‘override’ does not, to the best of my knowledge, mean anything to the actual machine code that is produced; rather, it signals the developer’s *intent,* thus allowing the compiler to assist in making sure the developer does the right thing.

I’d actually argue that the example above is a much, much bigger problem than the objection you raised, as it can actually produce unintended behavior at runtime, whereas the example with protocols can’t.

As for the protocol example, I’d like to refine Option 3 from last night slightly:

Option 4: A keyword is required on a method declaration if and only if the containing type is declared as conforming to its protocol, either in its definition or in an extension that is visible within the current scope. This allows the extension to remain empty in your example, but puts the responsibility on the developer to declare conforming methods when it is reasonable to know that they will satisfy a protocol.

Charles

···

On Aug 26, 2016, at 11:02 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Really? I wasn't aware that you could work around the `override` keyword (the one that's required for classes). How do you do that?

Really? I wasn't aware that you could work around the `override` keyword
(the one that's required for classes). How do you do that?

By implementing the subclass’s method before the superclass’s. You can try
this yourself:

- - - Library code: - - -

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
    }
}

- - - App code: - - -

import FooLibrary

class Subclass: Superclass {
    func bar() { print("bar called in the app") }
}

let obj = Subclass()

obj.foo()

- - - output: - - -

*foo called in library*
*Program ended with exit code: 0*

- - - - - -

Now: Change the library code to:

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
        bar()
    }

    // Hey look, I didn't even use that stupid new 'open' keyword.
    public func bar() { print("bar called in library") }
}

- - - Run the app again without compiling it, and: - - -

*foo called in library*
*bar called in the app*
*Program ended with exit code: 0*

- - -

Voilà: I overrode a method (a supposedly non-overridable one, at that)
with no “override” keyword.

This is clearly a bug in the implementation, not part of the design. Expect
it to be fixed as the code for `open` matures. It's certainly not a
precedent to be emulated for designing another feature.

Just as File A in your earlier example can implement a protocol method
without realizing it, Subclass here has unintentionally overridden a
superclass method. This is because ‘override’ does not, to the best of my
knowledge, mean anything to the actual machine code that is produced;
rather, it signals the developer’s *intent,* thus allowing the compiler to
assist in making sure the developer does the right thing.

I’d actually argue that the example above is a much, much bigger problem
than the objection you raised, as it can actually produce unintended
behavior at runtime, whereas the example with protocols can’t.

As for the protocol example, I’d like to refine Option 3 from last night
slightly:

Option 4: A keyword is required on a method declaration if and only if the
containing type is declared as conforming to its protocol, either in its
definition or in an extension that is visible within the current scope.

Extensions are not first-class entities and have no visibility of their
own. In any case, you have not solved the problem, which has nothing to do
with whether something is "reasonable to know": when a default
implementation A is overridden by implementation B, implementation A may be
visible only in a *more restricted* access scope than implementation B.
(This is not the case with overriding superclass members in subclasses.) It
can be perfectly "reasonable to know" about both A and B, but there is
still no way you can indicate this knowledge by appending a keyword to the
declaration for implementation B if the access scope for implementation A
is unutterable where B is declared.

···

On Fri, Aug 26, 2016 at 1:27 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 26, 2016, at 11:02 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

This allows the extension to remain empty in your example, but puts the
responsibility on the developer to declare conforming methods when it is
reasonable to know that they will satisfy a protocol.

Charles

Really? I wasn't aware that you could work around the `override` keyword (the one that's required for classes). How do you do that?

By implementing the subclass’s method before the superclass’s. You can try this yourself:

- - - Library code: - - -

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
    }
}

- - - App code: - - -

import FooLibrary

class Subclass: Superclass {
    func bar() { print("bar called in the app") }
}

let obj = Subclass()

obj.foo()

- - - output: - - -

foo called in library
Program ended with exit code: 0

- - - - - -

Now: Change the library code to:

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
        bar()
    }
    
    // Hey look, I didn't even use that stupid new 'open' keyword.
    public func bar() { print("bar called in library") }
}

- - - Run the app again without compiling it, and: - - -

foo called in library
bar called in the app
Program ended with exit code: 0

- - -

Voilà: I overrode a method (a supposedly non-overridable one, at that) with no “override” keyword.

This is clearly a bug in the implementation, not part of the design. Expect it to be fixed as the code for `open` matures. It's certainly not a precedent to be emulated for designing another feature.

It’s a side effect of using straight string equality for conformance, which is the status quo that several proposals here have been hoping to change with protocols.

Just as File A in your earlier example can implement a protocol method without realizing it, Subclass here has unintentionally overridden a superclass method. This is because ‘override’ does not, to the best of my knowledge, mean anything to the actual machine code that is produced; rather, it signals the developer’s *intent,* thus allowing the compiler to assist in making sure the developer does the right thing.

I’d actually argue that the example above is a much, much bigger problem than the objection you raised, as it can actually produce unintended behavior at runtime, whereas the example with protocols can’t.

As for the protocol example, I’d like to refine Option 3 from last night slightly:

Option 4: A keyword is required on a method declaration if and only if the containing type is declared as conforming to its protocol, either in its definition or in an extension that is visible within the current scope.

Extensions are not first-class entities and have no visibility of their own.

I didn’t express that very well. Let me clarify what I mean:

internal struct S {}

private protocol P {
    func foo()
}

extension S: P {
    func foo() {}
}

- - - choose “Generated Interface” and you get: - - -

internal struct S {
}

extension S {

    internal func foo()
}

- - -

While the extension is visible, the fact that it adds conformance to P is not. Therefore, as far as code outside this file is concerned, there is no conformance to P.

In any case, you have not solved the problem, which has nothing to do with whether something is "reasonable to know": when a default implementation A is overridden by implementation B, implementation A may be visible only in a *more restricted* access scope than implementation B. (This is not the case with overriding superclass members in subclasses.) It can be perfectly "reasonable to know" about both A and B, but there is still no way you can indicate this knowledge by appending a keyword to the declaration for implementation B if the access scope for implementation A is unutterable where B is declared.

Could you provide an example of code where this would present a problem?

Charles

···

On Aug 26, 2016, at 1:45 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Aug 26, 2016 at 1:27 PM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Aug 26, 2016, at 11:02 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Really? I wasn't aware that you could work around the `override` keyword
(the one that's required for classes). How do you do that?

By implementing the subclass’s method before the superclass’s. You can
try this yourself:

- - - Library code: - - -

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
    }
}

- - - App code: - - -

import FooLibrary

class Subclass: Superclass {
    func bar() { print("bar called in the app") }
}

let obj = Subclass()

obj.foo()

- - - output: - - -

*foo called in library*
*Program ended with exit code: 0*

- - - - - -

Now: Change the library code to:

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
        bar()
    }

    // Hey look, I didn't even use that stupid new 'open' keyword.
    public func bar() { print("bar called in library") }
}

- - - Run the app again without compiling it, and: - - -

*foo called in library*
*bar called in the app*
*Program ended with exit code: 0*

- - -

Voilà: I overrode a method (a supposedly non-overridable one, at that)
with no “override” keyword.

This is clearly a bug in the implementation, not part of the design.
Expect it to be fixed as the code for `open` matures. It's certainly not a
precedent to be emulated for designing another feature.

I misread your example. You have to run the app *without compiling*; your
two versions of the library have a compatible ABI. The Swift compiler won't
compile your app code, so how's that an example of "working around"
anything in the language?

Just as File A in your earlier example can implement a protocol method

···

On Fri, Aug 26, 2016 at 1:45 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Aug 26, 2016 at 1:27 PM, Charles Srstka <cocoadev@charlessoft.com> > wrote:

On Aug 26, 2016, at 11:02 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
without realizing it, Subclass here has unintentionally overridden a
superclass method. This is because ‘override’ does not, to the best of my
knowledge, mean anything to the actual machine code that is produced;
rather, it signals the developer’s *intent,* thus allowing the compiler to
assist in making sure the developer does the right thing.

I’d actually argue that the example above is a much, much bigger problem
than the objection you raised, as it can actually produce unintended
behavior at runtime, whereas the example with protocols can’t.

As for the protocol example, I’d like to refine Option 3 from last night
slightly:

Option 4: A keyword is required on a method declaration if and only if
the containing type is declared as conforming to its protocol, either in
its definition or in an extension that is visible within the current scope.

Extensions are not first-class entities and have no visibility of their
own. In any case, you have not solved the problem, which has nothing to do
with whether something is "reasonable to know": when a default
implementation A is overridden by implementation B, implementation A may be
visible only in a *more restricted* access scope than implementation B.
(This is not the case with overriding superclass members in subclasses.) It
can be perfectly "reasonable to know" about both A and B, but there is
still no way you can indicate this knowledge by appending a keyword to the
declaration for implementation B if the access scope for implementation A
is unutterable where B is declared.

This allows the extension to remain empty in your example, but puts the
responsibility on the developer to declare conforming methods when it is
reasonable to know that they will satisfy a protocol.

Charles

It is an example of an override occurring without the “override” keyword, and a demonstration that the “override” keyword’s purpose is to indicate programmer intent rather than do anything to the actual generated machine code (otherwise, the override here wouldn’t have happened). Despite this, no one argues against the existence of the “override” keyword.

Charles

···

On Aug 26, 2016, at 2:29 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I misread your example. You have to run the app *without compiling*; your two versions of the library have a compatible ABI. The Swift compiler won't compile your app code, so how's that an example of "working around" anything in the language?

Really? I wasn't aware that you could work around the `override` keyword
(the one that's required for classes). How do you do that?

By implementing the subclass’s method before the superclass’s. You can
try this yourself:

- - - Library code: - - -

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
    }
}

- - - App code: - - -

import FooLibrary

class Subclass: Superclass {
    func bar() { print("bar called in the app") }
}

let obj = Subclass()

obj.foo()

- - - output: - - -

*foo called in library*
*Program ended with exit code: 0*

- - - - - -

Now: Change the library code to:

open class Superclass {
    public init() {}
    public func foo() {
        print("foo called in library")
        bar()
    }

    // Hey look, I didn't even use that stupid new 'open' keyword.
    public func bar() { print("bar called in library") }
}

- - - Run the app again without compiling it, and: - - -

*foo called in library*
*bar called in the app*
*Program ended with exit code: 0*

- - -

Voilà: I overrode a method (a supposedly non-overridable one, at that)
with no “override” keyword.

This is clearly a bug in the implementation, not part of the design.
Expect it to be fixed as the code for `open` matures. It's certainly not a
precedent to be emulated for designing another feature.

It’s a side effect of using straight string equality for conformance,
which is the status quo that several proposals here have been hoping to
change with protocols.

Just as File A in your earlier example can implement a protocol method

without realizing it, Subclass here has unintentionally overridden a
superclass method. This is because ‘override’ does not, to the best of my
knowledge, mean anything to the actual machine code that is produced;
rather, it signals the developer’s *intent,* thus allowing the compiler to
assist in making sure the developer does the right thing.

I’d actually argue that the example above is a much, much bigger problem
than the objection you raised, as it can actually produce unintended
behavior at runtime, whereas the example with protocols can’t.

As for the protocol example, I’d like to refine Option 3 from last night
slightly:

Option 4: A keyword is required on a method declaration if and only if
the containing type is declared as conforming to its protocol, either in
its definition or in an extension that is visible within the current scope.

Extensions are not first-class entities and have no visibility of their
own.

I didn’t express that very well. Let me clarify what I mean:

internal struct S {}

private protocol P {
    func foo()
}

extension S: P {
    func foo() {}
}

- - - choose “Generated Interface” and you get: - - -

internal struct S {
}

extension S {

    internal func foo()
}

- - -

While the extension is visible, the fact that it adds conformance to P is
not. Therefore, as far as code outside this file is concerned, there is no
conformance to P.

That's correct, but why should the compiler accept the code in this file,
when both S and P are visible, and S is not overriding default
implementations in P as it should?

In any case, you have not solved the problem, which has nothing to do with
whether something is "reasonable to know": when a default implementation A
is overridden by implementation B, implementation A may be visible only in
a *more restricted* access scope than implementation B. (This is not the
case with overriding superclass members in subclasses.) It can be perfectly
"reasonable to know" about both A and B, but there is still no way you can
indicate this knowledge by appending a keyword to the declaration for
implementation B if the access scope for implementation A is unutterable
where B is declared.

Could you provide an example of code where this would present a problem?

I'm still talking about the same example. How do you rationalize your
statement that no keyword is needed because it's not "reasonable to know"
about both S and P when compiling the file in which P is declared?

···

On Fri, Aug 26, 2016 at 2:29 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 26, 2016, at 1:45 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Aug 26, 2016 at 1:27 PM, Charles Srstka <cocoadev@charlessoft.com> > wrote:

On Aug 26, 2016, at 11:02 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Charles

I misread your example. You have to run the app *without compiling*; your
two versions of the library have a compatible ABI. The Swift compiler won't
compile your app code, so how's that an example of "working around"
anything in the language?

It is an example of an override occurring without the “override” keyword,
and a demonstration that the “override” keyword’s purpose is to indicate
programmer intent rather than do anything to the actual generated machine
code (otherwise, the override here wouldn’t have happened). Despite this,
no one argues against the existence of the “override” keyword.

This is not an example of that at all. You can't get the Swift compiler to
compile the code. Failure to use `override` is a compile-time error, and
compile-time errors don't happen at runtime. Why are we talking about
'generated machine code'?

···

On Fri, Aug 26, 2016 at 2:34 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 26, 2016, at 2:29 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Charles

internal struct S {}

private protocol P {
    func foo()
}

extension S: P {
    func foo() {}
}

- - - choose “Generated Interface” and you get: - - -

internal struct S {
}

extension S {

    internal func foo()
}

- - -

While the extension is visible, the fact that it adds conformance to P is not. Therefore, as far as code outside this file is concerned, there is no conformance to P.

That's correct, but why should the compiler accept the code in this file, when both S and P are visible, and S is not overriding default implementations in P as it should?

I am confused. Why should S, above, be required to override P’s default implementation of foo()?

In any case, you have not solved the problem, which has nothing to do with whether something is "reasonable to know": when a default implementation A is overridden by implementation B, implementation A may be visible only in a *more restricted* access scope than implementation B. (This is not the case with overriding superclass members in subclasses.) It can be perfectly "reasonable to know" about both A and B, but there is still no way you can indicate this knowledge by appending a keyword to the declaration for implementation B if the access scope for implementation A is unutterable where B is declared.

Could you provide an example of code where this would present a problem?

I'm still talking about the same example. How do you rationalize your statement that no keyword is needed because it's not "reasonable to know" about both S and P when compiling the file in which P is declared?

When compiling the file in which P is declared, you would add the keyword. Code outside this file that happened to define methods named foo() would not, since they would not be consciously conforming to the protocol.

Charles

···

On Aug 26, 2016, at 2:34 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Exactly; compile-time errors are intended to enforce correct behavior on the part of the developer. Your objection, as I understand it, is that a type that declares a method, which another file retroactively conforms to a private protocol that the original type can’t see, can’t indicate conformance in its original declaration, and that this would, in some way, cause a practical problem. I am failing to see what the problem is: the keywords, if properly designed, can allow all parties to be clear as to what they mean and catch any mistakes involved in doing so, which is all we need here. Once compiled, everything will behave identically to how it behaves today.

Charles

···

On Aug 26, 2016, at 2:38 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Aug 26, 2016 at 2:34 PM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Aug 26, 2016, at 2:29 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

I misread your example. You have to run the app *without compiling*; your two versions of the library have a compatible ABI. The Swift compiler won't compile your app code, so how's that an example of "working around" anything in the language?

It is an example of an override occurring without the “override” keyword, and a demonstration that the “override” keyword’s purpose is to indicate programmer intent rather than do anything to the actual generated machine code (otherwise, the override here wouldn’t have happened). Despite this, no one argues against the existence of the “override” keyword.

This is not an example of that at all. You can't get the Swift compiler to compile the code. Failure to use `override` is a compile-time error, and compile-time errors don't happen at runtime. Why are we talking about 'generated machine code'?

internal struct S {}

private protocol P {
    func foo()
}

extension S: P {
    func foo() {}
}

- - - choose “Generated Interface” and you get: - - -

internal struct S {
}

extension S {

    internal func foo()
}

- - -

While the extension is visible, the fact that it adds conformance to P is
not. Therefore, as far as code outside this file is concerned, there is no
conformance to P.

That's correct, but why should the compiler accept the code in this file,
when both S and P are visible, and S is not overriding default
implementations in P as it should?

I am confused. Why should S, above, be required to override P’s default
implementation of foo()?

Sorry, I was referring to my original example: S is in a different file
from P and has its own implementation of foo(), and P has its own default
implementation of foo(). I assumed you simplified the example above to
discuss the generated interface. I didn't realize you were moving on to a
different example.

In any case, you have not solved the problem, which has nothing to do
with whether something is "reasonable to know": when a default
implementation A is overridden by implementation B, implementation A may be
visible only in a *more restricted* access scope than implementation B.
(This is not the case with overriding superclass members in subclasses.) It
can be perfectly "reasonable to know" about both A and B, but there is
still no way you can indicate this knowledge by appending a keyword to the
declaration for implementation B if the access scope for implementation A
is unutterable where B is declared.

Could you provide an example of code where this would present a problem?

I'm still talking about the same example. How do you rationalize your
statement that no keyword is needed because it's not "reasonable to know"
about both S and P when compiling the file in which P is declared?

When compiling the file in which P is declared, you would add the keyword.
Code outside this file that happened to define methods named foo() would
not, since they would not be consciously conforming to the protocol.

What keyword could you add to P, when the overriding member is in S?

File A:

internal struct S {
  func foo() { }
}

File B:

private protocol P {
  func foo()
}

extension P {
  func foo() { }
}

extension S : P { }

Where do I append your proposed keyword? Or, how do you rationalize its not
being required on the basis of "reasonable to know"?

Charles

···

On Fri, Aug 26, 2016 at 2:42 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 26, 2016, at 2:34 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Sorry, I was referring to my original example: S is in a different file from P and has its own implementation of foo(), and P has its own default implementation of foo(). I assumed you simplified the example above to discuss the generated interface. I didn't realize you were moving on to a different example.

You are correct; it was a simplified example to discuss the generated interface. I was confused by your objection’s proximity to it; sorry for the noise.

In any case, you have not solved the problem, which has nothing to do with whether something is "reasonable to know": when a default implementation A is overridden by implementation B, implementation A may be visible only in a *more restricted* access scope than implementation B. (This is not the case with overriding superclass members in subclasses.) It can be perfectly "reasonable to know" about both A and B, but there is still no way you can indicate this knowledge by appending a keyword to the declaration for implementation B if the access scope for implementation A is unutterable where B is declared.

Could you provide an example of code where this would present a problem?

I'm still talking about the same example. How do you rationalize your statement that no keyword is needed because it's not "reasonable to know" about both S and P when compiling the file in which P is declared?

When compiling the file in which P is declared, you would add the keyword. Code outside this file that happened to define methods named foo() would not, since they would not be consciously conforming to the protocol.

What keyword could you add to P, when the overriding member is in S?

File A:

internal struct S {
  func foo() { }
}

File B:

private protocol P {
  func foo()
}

extension P {
  func foo() { }
}

extension S : P { }

Where do I append your proposed keyword? Or, how do you rationalize its not being required on the basis of "reasonable to know"?

File A does not need the keyword, since it is not implementing P by adding foo() and cannot be expected to know about it, and thus it’s rather arguable to say that it is “overriding” anything. The extension that conforms S to P declares no methods, so it is clear that this is meant to be retroactively conforming, so I am not convinced a keyword is needed here. However, there is room for debate here, and if we determined that a keyword should be required, then I would say it should go in the extension that conforms S to P, since that is where the “problem” occurs.

If S had been declared in File B, however, there would be a keyword attached to its declaration of foo(), since the information that S conforms to P, as well as the fact that foo() is a requirement of said protocol, would all be visible in the current scope.

Charles

···

On Aug 26, 2016, at 2:49 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I misread your example. You have to run the app *without compiling*; your
two versions of the library have a compatible ABI. The Swift compiler won't
compile your app code, so how's that an example of "working around"
anything in the language?

It is an example of an override occurring without the “override” keyword,
and a demonstration that the “override” keyword’s purpose is to indicate
programmer intent rather than do anything to the actual generated machine
code (otherwise, the override here wouldn’t have happened). Despite this,
no one argues against the existence of the “override” keyword.

This is not an example of that at all. You can't get the Swift compiler to
compile the code. Failure to use `override` is a compile-time error, and
compile-time errors don't happen at runtime. Why are we talking about
'generated machine code'?

Exactly; compile-time errors are intended to enforce correct behavior on
the part of the developer. Your objection, as I understand it, is that a
type that declares a method, which another file retroactively conforms to a
private protocol that the original type can’t see, can’t indicate
conformance in its original declaration, and that this would, in some way,
cause a practical problem. I am failing to see what the problem is:

We were discussing your option (3), which would mean that trivial
refactoring would lead to a lack of compile-time errors even if no one ever
used your proposed keyword, effectively making the keyword optional. You
wrote: 'I can work around the “override” keyword and override things
without it, but that doesn’t mean that I think we should remove the
“override” keyword.' This was surprising to me, because if it were possible
to avoid using `override` without a compile-time error, I would indeed
argue that we should remove the `override` keyword. However, you gave an
example where *not compiling* means you don't get a *compile-time error*,
which is not a workaround at all.

···

On Fri, Aug 26, 2016 at 2:48 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 26, 2016, at 2:38 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Aug 26, 2016 at 2:34 PM, Charles Srstka <cocoadev@charlessoft.com> > wrote:

On Aug 26, 2016, at 2:29 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

the keywords, if properly designed, can allow all parties to be clear as
to what they mean and catch any mistakes involved in doing so, which is all
we need here. Once compiled, everything will behave identically to how it
behaves today.

Charles

Sorry, I was referring to my original example: S is in a different file
from P and has its own implementation of foo(), and P has its own default
implementation of foo(). I assumed you simplified the example above to
discuss the generated interface. I didn't realize you were moving on to a
different example.

You are correct; it was a simplified example to discuss the generated
interface. I was confused by your objection’s proximity to it; sorry for
the noise.

In any case, you have not solved the problem, which has nothing to do
with whether something is "reasonable to know": when a default
implementation A is overridden by implementation B, implementation A may be
visible only in a *more restricted* access scope than implementation B.
(This is not the case with overriding superclass members in subclasses.) It
can be perfectly "reasonable to know" about both A and B, but there is
still no way you can indicate this knowledge by appending a keyword to the
declaration for implementation B if the access scope for implementation A
is unutterable where B is declared.

Could you provide an example of code where this would present a problem?

I'm still talking about the same example. How do you rationalize your
statement that no keyword is needed because it's not "reasonable to know"
about both S and P when compiling the file in which P is declared?

When compiling the file in which P is declared, you would add the
keyword. Code outside this file that happened to define methods named foo()
would not, since they would not be consciously conforming to the protocol.

What keyword could you add to P, when the overriding member is in S?

File A:

internal struct S {
  func foo() { }
}

File B:

private protocol P {
  func foo()
}

extension P {
  func foo() { }
}

extension S : P { }

Where do I append your proposed keyword? Or, how do you rationalize its
not being required on the basis of "reasonable to know"?

File A does not need the keyword, since it is not implementing P by adding
foo() and cannot be expected to know about it, and thus it’s rather
arguable to say that it is “overriding” anything. The extension that
conforms S to P declares no methods, so it is clear that this is meant to
be retroactively conforming, so I am not convinced a keyword is needed
here. However, there is room for debate here, and if we determined that a
keyword should be required, then I would say it should go in the extension
that conforms S to P, since that is where the “problem” occurs.

If S had been declared in File B, however, there would be a keyword
attached to its declaration of foo(), since the information that S conforms
to P, as well as the fact that foo() is a requirement of said protocol,
would all be visible in the current scope.

Why? S.foo() is visible throughout the module, but outside this file,
S.foo() doesn't override anything. Why should it be marked as an `override`
when, as you say, the generated interface shows no sign of conformance to P?
In addition, how would you justify a requirement to fiddle with these
keywords when someone simply refactors an internally visible type with no
private members from one file to another? Your proposed rule just broke
copy-and-paste...

···

On Fri, Aug 26, 2016 at 2:57 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 26, 2016, at 2:49 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Charles

We were talking about retroactive modeling, and the effects that adding things after the fact can have on previously-existing code. This was, I think, a pretty close analogue for classes. The original app compiles, the library compiles, the refactored library compiles.

If you want it all to compile at the same time, well, we’ve still got access to all the Objective-C runtime functions in Swift. One can also write the assembly by hand. Things like “override” aren’t security features; they’re there to help you do the right thing. If you’re deliberately trying to do the wrong thing, I don’t think that can strictly be prevented.

Charles

···

On Aug 26, 2016, at 2:58 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Aug 26, 2016 at 2:48 PM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Aug 26, 2016, at 2:38 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

On Fri, Aug 26, 2016 at 2:34 PM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Aug 26, 2016, at 2:29 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

I misread your example. You have to run the app *without compiling*; your two versions of the library have a compatible ABI. The Swift compiler won't compile your app code, so how's that an example of "working around" anything in the language?

It is an example of an override occurring without the “override” keyword, and a demonstration that the “override” keyword’s purpose is to indicate programmer intent rather than do anything to the actual generated machine code (otherwise, the override here wouldn’t have happened). Despite this, no one argues against the existence of the “override” keyword.

This is not an example of that at all. You can't get the Swift compiler to compile the code. Failure to use `override` is a compile-time error, and compile-time errors don't happen at runtime. Why are we talking about 'generated machine code'?

Exactly; compile-time errors are intended to enforce correct behavior on the part of the developer. Your objection, as I understand it, is that a type that declares a method, which another file retroactively conforms to a private protocol that the original type can’t see, can’t indicate conformance in its original declaration, and that this would, in some way, cause a practical problem. I am failing to see what the problem is:

We were discussing your option (3), which would mean that trivial refactoring would lead to a lack of compile-time errors even if no one ever used your proposed keyword, effectively making the keyword optional. You wrote: 'I can work around the “override” keyword and override things without it, but that doesn’t mean that I think we should remove the “override” keyword.' This was surprising to me, because if it were possible to avoid using `override` without a compile-time error, I would indeed argue that we should remove the `override` keyword. However, you gave an example where *not compiling* means you don't get a *compile-time error*, which is not a workaround at all.

Because it indicates the programmer’s *intent.* If the protocol is visible at the time you’re writing the declaration for S.foo(), then you should have to tell the compiler what you mean by that and what you’re doing with it.

Charles

···

On Aug 26, 2016, at 3:02 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Aug 26, 2016 at 2:57 PM, Charles Srstka <cocoadev@charlessoft.com <mailto:cocoadev@charlessoft.com>> wrote:

On Aug 26, 2016, at 2:49 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Sorry, I was referring to my original example: S is in a different file from P and has its own implementation of foo(), and P has its own default implementation of foo(). I assumed you simplified the example above to discuss the generated interface. I didn't realize you were moving on to a different example.

You are correct; it was a simplified example to discuss the generated interface. I was confused by your objection’s proximity to it; sorry for the noise.

In any case, you have not solved the problem, which has nothing to do with whether something is "reasonable to know": when a default implementation A is overridden by implementation B, implementation A may be visible only in a *more restricted* access scope than implementation B. (This is not the case with overriding superclass members in subclasses.) It can be perfectly "reasonable to know" about both A and B, but there is still no way you can indicate this knowledge by appending a keyword to the declaration for implementation B if the access scope for implementation A is unutterable where B is declared.

Could you provide an example of code where this would present a problem?

I'm still talking about the same example. How do you rationalize your statement that no keyword is needed because it's not "reasonable to know" about both S and P when compiling the file in which P is declared?

When compiling the file in which P is declared, you would add the keyword. Code outside this file that happened to define methods named foo() would not, since they would not be consciously conforming to the protocol.

What keyword could you add to P, when the overriding member is in S?

File A:

internal struct S {
  func foo() { }
}

File B:

private protocol P {
  func foo()
}

extension P {
  func foo() { }
}

extension S : P { }

Where do I append your proposed keyword? Or, how do you rationalize its not being required on the basis of "reasonable to know"?

File A does not need the keyword, since it is not implementing P by adding foo() and cannot be expected to know about it, and thus it’s rather arguable to say that it is “overriding” anything. The extension that conforms S to P declares no methods, so it is clear that this is meant to be retroactively conforming, so I am not convinced a keyword is needed here. However, there is room for debate here, and if we determined that a keyword should be required, then I would say it should go in the extension that conforms S to P, since that is where the “problem” occurs.

If S had been declared in File B, however, there would be a keyword attached to its declaration of foo(), since the information that S conforms to P, as well as the fact that foo() is a requirement of said protocol, would all be visible in the current scope.

Why? S.foo() is visible throughout the module, but outside this file, S.foo() doesn't override anything. Why should it be marked as an `override` when, as you say, the generated interface shows no sign of conformance to P?
In addition, how would you justify a requirement to fiddle with these keywords when someone simply refactors an internally visible type with no private members from one file to another? Your proposed rule just broke copy-and-paste...

I misread your example. You have to run the app *without compiling*;
your two versions of the library have a compatible ABI. The Swift compiler
won't compile your app code, so how's that an example of "working around"
anything in the language?

It is an example of an override occurring without the “override”
keyword, and a demonstration that the “override” keyword’s purpose is to
indicate programmer intent rather than do anything to the actual generated
machine code (otherwise, the override here wouldn’t have happened). Despite
this, no one argues against the existence of the “override” keyword.

This is not an example of that at all. You can't get the Swift compiler
to compile the code. Failure to use `override` is a compile-time error, and
compile-time errors don't happen at runtime. Why are we talking about
'generated machine code'?

Exactly; compile-time errors are intended to enforce correct behavior on
the part of the developer. Your objection, as I understand it, is that a
type that declares a method, which another file retroactively conforms to a
private protocol that the original type can’t see, can’t indicate
conformance in its original declaration, and that this would, in some way,
cause a practical problem. I am failing to see what the problem is:

We were discussing your option (3), which would mean that trivial
refactoring would lead to a lack of compile-time errors even if no one ever
used your proposed keyword, effectively making the keyword optional. You
wrote: 'I can work around the “override” keyword and override things
without it, but that doesn’t mean that I think we should remove the
“override” keyword.' This was surprising to me, because if it were possible
to avoid using `override` without a compile-time error, I would indeed
argue that we should remove the `override` keyword. However, you gave an
example where *not compiling* means you don't get a *compile-time error*,
which is not a workaround at all.

We were talking about retroactive modeling, and the effects that adding
things after the fact can have on previously-existing code. This was, I
think, a pretty close analogue for classes. The original app compiles, the
library compiles, the refactored library compiles.

We were talking about language design and rules to be enforced at compile
time; there is simply no analog here because your example was about the
runtime behavior of apps that call into libraries.

If you want it all to compile at the same time, well, we’ve still got

access to all the Objective-C runtime functions in Swift. One can also
write the assembly by hand. Things like “override” aren’t security
features; they’re there to help you do the right thing. If you’re
deliberately trying to do the wrong thing, I don’t think that can strictly
be prevented.

The current syntax is not the "wrong thing"--and a future language feature
shouldn't leave a loophole where someone can choose either one or another
totally different set of rules for conforming a type to a protocol; there
should be one syntax for doing that in Swift.

···

On Fri, Aug 26, 2016 at 3:09 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 26, 2016, at 2:58 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Aug 26, 2016 at 2:48 PM, Charles Srstka <cocoadev@charlessoft.com> > wrote:

On Aug 26, 2016, at 2:38 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Aug 26, 2016 at 2:34 PM, Charles Srstka <cocoadev@charlessoft.com >> > wrote:

On Aug 26, 2016, at 2:29 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Charles

Sorry, I was referring to my original example: S is in a different file
from P and has its own implementation of foo(), and P has its own default
implementation of foo(). I assumed you simplified the example above to
discuss the generated interface. I didn't realize you were moving on to a
different example.

You are correct; it was a simplified example to discuss the generated
interface. I was confused by your objection’s proximity to it; sorry for
the noise.

In any case, you have not solved the problem, which has nothing to do
with whether something is "reasonable to know": when a default
implementation A is overridden by implementation B, implementation A may be
visible only in a *more restricted* access scope than implementation B.
(This is not the case with overriding superclass members in subclasses.) It
can be perfectly "reasonable to know" about both A and B, but there is
still no way you can indicate this knowledge by appending a keyword to the
declaration for implementation B if the access scope for implementation A
is unutterable where B is declared.

Could you provide an example of code where this would present a problem?

I'm still talking about the same example. How do you rationalize your
statement that no keyword is needed because it's not "reasonable to know"
about both S and P when compiling the file in which P is declared?

When compiling the file in which P is declared, you would add the
keyword. Code outside this file that happened to define methods named foo()
would not, since they would not be consciously conforming to the protocol.

What keyword could you add to P, when the overriding member is in S?

File A:

internal struct S {
  func foo() { }
}

File B:

private protocol P {
  func foo()
}

extension P {
  func foo() { }
}

extension S : P { }

Where do I append your proposed keyword? Or, how do you rationalize its
not being required on the basis of "reasonable to know"?

File A does not need the keyword, since it is not implementing P by
adding foo() and cannot be expected to know about it, and thus it’s rather
arguable to say that it is “overriding” anything. The extension that
conforms S to P declares no methods, so it is clear that this is meant to
be retroactively conforming, so I am not convinced a keyword is needed
here. However, there is room for debate here, and if we determined that a
keyword should be required, then I would say it should go in the extension
that conforms S to P, since that is where the “problem” occurs.

If S had been declared in File B, however, there would be a keyword
attached to its declaration of foo(), since the information that S conforms
to P, as well as the fact that foo() is a requirement of said protocol,
would all be visible in the current scope.

Why? S.foo() is visible throughout the module, but outside this file,
S.foo() doesn't override anything. Why should it be marked as an `override`
when, as you say, the generated interface shows no sign of conformance to P?
In addition, how would you justify a requirement to fiddle with these
keywords when someone simply refactors an internally visible type with no
private members from one file to another? Your proposed rule just broke
copy-and-paste...

Because it indicates the programmer’s *intent.* If the protocol is visible
at the time you’re writing the declaration for S.foo(), then you should
have to tell the compiler what you mean by that and what you’re doing with
it.

Where a type is located in relation to a protocol is a poor proxy for
whether one was written _before_ the other. And while the writer's most
likely intent might differ depending on the order in which two things are
written, how one declares members on a type should not be determined by a
set of arbitrary rules for the compiler to guess whether the type or
protocol was written first. This kind of functionality, if you want it,
might be appropriate for a linter. It could probably even look at last
modified dates, or with a git repo, go line by line to see when each piece
of the code was written.

···

On Fri, Aug 26, 2016 at 3:10 PM, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Aug 26, 2016, at 3:02 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Aug 26, 2016 at 2:57 PM, Charles Srstka <cocoadev@charlessoft.com> > wrote:

On Aug 26, 2016, at 2:49 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Charles