[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