[swift-evolution] [Completing Generics] Completing protocol extension diagnostics

Tim Schmelter tschmelter at gmail.com
Tue Mar 8 10:34:15 CST 2016


If we adopt the "one extension per conformance" model, would the compiler
then warn us if we include, say, helper functions that exist only to aid in
that conformance (and would therefore be appropriate to include in the
extension)? Wouldn't that lead to polluting the main type with those helper
methods?

--T


On Fri, Mar 4, 2016 at 3:38 AM, Haravikk via swift-evolution <
swift-evolution at swift.org> wrote:

> I like the idea of encouraging "one extension per conformance", though I’d
> maybe adjust it to simply “add conformance through extension”, as sometimes
> it makes sense for a single extension to add conformance to multiple
> protocols at a time, for example an extension to add conformance to
> CustomDebugStringConvertible and CustomStringConvertible as a single item.
> I’m not sure if you intended to prevent that, but I think it should still
> be possible to allow multiple protocols with the same basic concept that
> conformance must be exact.
>
> Regarding keyword usage for implementations, I’d say that we should have
> the override keyword at least on methods that shadow default
> implementations provided for protocol methods, as this should make things
> clearer. It could also help to tidy up documentation as currently default
> implementations that have been shadowed still show up in many places, even
> though they’re no longer strictly relevant.
>
> On 4 Mar 2016, at 00:08, Joe Groff via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> Under the umbrella of completing generics, I think we should make room for
> improving our diagnostics around protocol extensions. They're a
> well-received feature, but they introduce a lot of surprising behavior, and
> introduce opportunity for subtle bugs. We didn't have time to put much
> diagnostic work in last year, but now that users have had time to work with
> the feature, we have evidence of some of the more surprising and
> problematic behavior. Among the most common things we've seen reported:
>
> A) When a protocol requirement has an extension implementation requirement
> available, we'll silently ignore when a conforming type attempts to conform
> to the protocol but misses, by type or spelling:
>
> protocol Channel {
>   func receive() -> NSData
> }
> extension Channel {
>   func receive() -> NSData { return NSData() }
> }
>
> // Silently fails to replace the default implementation:
> struct ClumsyChannel: Channel {
>   // Wrong spelling
>   func recieve() -> NSData { return NSData(bytes: "oops", length: 4) }
>   // Wrong return type
>   func receive() -> [UInt8] { return Array("whoopsie".utf8) }
>   // Wrong throwiness
>   func receive() throws -> NSData { throw "doh" }
> }
>
> B) Protocol requirements aren't real class members, and can't be
> overridden by subclasses unless the base class satisfies the requirement
> with one of its own methods rather than with a protocol extension method,
> but we silently allow subclasses to shadow:
>
> class BaseChannel: Channel { } // gets default imp from extension
>
> class SubChannel: BaseChannel {
>   // Doesn't 'override' protocol requirement; silently shadows it
>   func receive() -> NSData { return NSData(bytes: "oof", length: 3) }
> }
>
> C) Similarly, protocol extension methods aren't protocol requirements, so
> extension methods that don't match a requirement can't be specialized in a
> way available to generic code, but we silently allow concrete type
> implementations to shadow:
>
> extension Channel {
>   func receiveAsString() -> String {
>     return String(data: receive(), encoding: NSUTF8Encoding)
>   }
> }
>
> struct StringyChannel {
>   func receive() -> NSData { return NSData(bytes: "data", 4) }
>   // Does not affect generic code calling receiveAsString
>   func receiveAsString() -> String { return "string" }
> }
>
> func foo<T: Channel>(chan: T) {
>   print(chan.receiveAsString())
> }
>
> foo(StringyChannel()) // Prints "data"
>
> (B) and (C) could be mitigated by shadowing warnings, and we've also
> floated ideas for making them behave as intended, by implicitly forwarding
> protocol requirements into class methods to address (B) and/or introducing
> dynamic dispatch for protocol extensions to address (C). (A) is a bit
> trickier, since with overloading it's tricky to divine whether a
> declaration was really intended to match another one with a different type
> in isolation. We've discussed a couple approaches to this problem:
>
> - Adopting an explicit 'implements' modifier, in the spirit of 'override',
> to mark a declaration as being intended to fulfill a requirement. This adds
> boilerplate we'd like to avoid, and also interferes with retroactive
> modeling.
> - Encourage "one extension per conformance" style, where each protocol
> conformance's requirements are defined in a dedicated extension. We can
> then warn about any declarations in an extension that don't satisfy a
> requirement:
>
> struct InconsistentChannel {}
>
> extension InconsistentChannel: Channel {
>   func receive() -> NSData { ... } // OK
>   func recieve() -> NSData { ... } // Warning: Declaration in conformance
> extension doesn't satisfy any requirements from 'Channel'
>   func receive() -> NSData? { ... } // Warning
> }
>
> There are likely others too. It'd be great if we could give users better
> guidance about this feature.
>
> -Joe
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160308/86c6b778/attachment.html>


More information about the swift-evolution mailing list