> On Apr 7, 2016, at 6:12 PM, Douglas Gregor via swift-evolution 
> <[email protected]> wrote:
<snip>
> With my proposal, we’d have some compiler-synthesized attribute (let’s call 
> it @__caller_default_implementation) that gets places on Objective-C optional 
> requirements when they get imported, e.g.,
> 
> @objc protocol NSTableViewDelegate {
>   @__caller_default_implementation func tableView(_: NSTableView, viewFor: 
> NSTableColumn, row: Int) -> NSView?
>   @__caller_default_implementation func tableView(_: NSTableView, 
> heightOfRow: Int) -> CGFloat
> }

If I understand correctly:

1. Optional methods in protocols imported from objective C gain a special flag 
when imported as Swift methods
2. Such methods can be unimplemented (such that the message will fail, or 
responds(to:) returns false) in objective-c code and bridged swift instances.
3. To implement that protocol in swift, you must have implementations of every 
protocol method, including optional ones
4. This means that there may be manual work around bridging protocols with 
optional messages into swift.
5. If the method implementation is marked @nonobjc it will fulfill the swift 
requirement that there be a method implementation, but that implementation will 
not be exposed to Objective-C
6. Swift code can call such a @nonobjc implementation, while Objective-C code 
will consider there to be nothing there.
7 An implementation that is only be applied to the swift variant is possibly 
fine in extensions while generates a warning within a swift class, or perhaps 
requires an explicit @nonobjc attribute.

I like it, except for the requirement for a manual implementation in Swift 
before you can support the protocol. 

I looked at whether the protocol members might be implemented by returning the 
nil/zero/false value you would get if you sent the message to nil. 

Cursory search quickly hit NSAccessibilityElement, which has an optional 
“accessibilityIdentifier” method returning a non-nil NSString. I suspect that 
the method also requires the string to be non-empty. Thus, a default 
implementation that represents the right thing to represent in a generated 
default implementation would likely be brittle.

I could also see an imported protocol where *any* default implementation of the 
optional method would not meet the requirements of an actual implementation of 
the method (not being versed in this particular interface, I’ll just straw man 
that the identifier is required to be unique within an application)

Thus I wonder if there may be some other way to support the idea of two 
distinct protocols, the protocol as defined in Objective C, and the protocol as 
defined in Swift.

Options that sprang to mind:
- the methods which return optional values have a default implementation that 
returns nil. Methods which return non-optional values will have the Swift 
protocol modified to return an Optional value, which they will do by default. 
So for example, Still on NSAccessibilityElement,

        -(BOOL)isAccessibilityFocused

would be imported as

        func isAccessibilityFocused() -> Bool?

with a default implementation returning nil. To actually implement the 
objective C protocol’s optional method, you must implement the version with the 
correct nullability variant, so in this case:

     @objc func isAccessiblityFocused() -> Bool { return focused }

(Of course, this means that a non-optional result value would need to satisfy 
an optional result valued variant in a protocol)

- similar to the above, but rather than overriding result values to support a 
default variant, overload ErrorType. Imported variants which throw will by 
default throw a bridging-specific ErrorType when called from Swift. Optional 
methods which do not throw will have a throwing variant generated on the Swift 
side. 

Again similar to the above, to satisfy the objective-C protocol requirement 
your implementation would need to be non-throwing.

I like this better in terms of capturing to the best of ones ability the 
‘spirit’ of optional methods and behavior in swift. However, this seems like it 
will result in more deviation between the Swift and Objective-C protocol method 
signatures.

Comments?

-DW

> 
> And “optional” disappears from the language. Now, there’s no optionality 
> left, so our useDelegate example tries to just do correct calls:
> 
> func useDelegate(delegate: NSTableViewDelegate) -> NSView? {
>   let view = delegate.tableView(tableView, viewFor: column, row: row)
>   let height = delegate.tableView(tableView, heightOfRow: row)
> }
> 
> Of course, the code above will fail if the actual delegate doesn’t implement 
> both methods. We need some kind of default implementation to fall back on in 
> that case. I propose that the code above produce a compiler error on both 
> lines *unless* there is a “default implementation” visible. So, to make the 
> code above compile without error, one would have to add:
> 
> extension NSTableViewDelegate {
>   @nonobjc func tableView(_: NSTableView, viewFor: NSTableColumn, row: Int) 
> -> NSView? { return nil }
>   
>   @nonobjc func tableView(_: NSTableView, heightOfRow: Int) -> CGFloat { 
> return 17 }
> } 
> 
> Now, the useDelegate example compiles. If the actual delegate implements the 
> optional requirement, we’ll use that implementation. Otherwise, the caller 
> will use the default (Swift-only) implementation it sees. From an 
> implementation standpoint, the compiler would effectively produce the 
> following for the first of these calls:
> 
> if delegate.responds(to: 
> #selector(NSTableViewDelegate.tableView(_:viewFor:row:))) {
>   // call the @objc instance method with the selector 
> tableView:viewForTableColumn:row:
> } else {
>   // call the Swift-only implementation of tableView(_:viewFor:row:) in the 
> protocol extension above
> }
> 
> There are a number of reasons why I like this approach:
> 
> 1) It eliminates the notion of ‘optional’ requirements from the language. For 
> classes that are adopting the NSTableViewDelegate protocol, it is as if these 
> requirements had default implementations.
> 
> 2) Only the callers to these requirements have to deal with the lack of 
> default implementations. This was already the case for optional requirements, 
> so it’s not an extra burden in principle, and it’s generally going to be 
> easier to write one defaulted implementation than deal with it in several 
> different places. Additionally, most of these callers are probably in the 
> Cocoa frameworks, not application code, so the overall impact should be small.
> 
> Thoughts?
> 
>       - Doug
> 
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to