> 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