[swift-evolution] Keyword for protocol conformance

Pyry Jahkola pyry.jahkola at iki.fi
Fri Aug 26 06:35:36 CDT 2016


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 <https://en.wikibooks.org/wiki/Haskell/Classes_and_types#Classes_and_instances> and Clojure's protocols <http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/extend> 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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160826/c0004e68/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 842 bytes
Desc: Message signed with OpenPGP using GPGMail
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160826/c0004e68/attachment.sig>


More information about the swift-evolution mailing list