[swift-evolution] [Proposal draft] Make Optional Requirements Objective-C-only

Douglas Gregor dgregor at apple.com
Fri Apr 22 22:02:22 CDT 2016



Sent from my iPhone

> On Apr 22, 2016, at 5:56 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
> 
> Not an expert on Obj-C compatibility in Swift by any means, but this
> reads like it's largely a change of nomenclature. To me, though,
> `objcoptional` reads exceedingly poorly. Why not emphasize the Obj-C
> compatibility angle by requiring the `@objc` attribute to precede each
> use of `optional`? (In other words, effectively rename `optional` to
> `@objc optional`.)

That is a great idea. 
> 
> 
> On Fri, Apr 22, 2016 at 7:35 PM, Douglas Gregor via swift-evolution
> <swift-evolution at swift.org> wrote:
>> Proposal link:
>> https://github.com/DougGregor/swift-evolution/blob/objc-optional/proposals/NNNN-optional-requirements.md
>> 
>> After a whole lot of discussion and thrashing on optional requirements, I
>> have a draft for a modest proposal: change the ‘optional’ keyword to
>> something that indicates that this feature is only for compatibility with
>> Objective-C and will not be supported on Swift protocols. Comments welcome!
>> 
>> - Doug
>> 
>> Make Optional Requirements Objective-C-only
>> 
>> Proposal: SE-NNNN
>> Author(s): Doug Gregor
>> Status: Awaiting review
>> Review manager: TBD
>> 
>> Introduction
>> 
>> Swift currently has support for "optional" requirements in Objective-C
>> protocols, to match with the corresponding feature of Objective-C. We don't
>> want to make optional requirements a feature of Swift protocols (for reasons
>> described below), nor can we completely eliminate the notion of the language
>> (for different reasons also described below). Therefore, to prevent
>> confusion about our direction, this proposal changes the optional keyword
>> objcoptional to indicate that this is an Objective-C compatibility feature.
>> 
>> Swift-evolution threads: eliminate optional requirements, make Swift
>> protocols support optional requirements and make optional protocol
>> requirements first class citizens.
>> 
>> Motivation
>> 
>> Having optional only work for Objective-C requirements is very weird: it
>> feels like a general feature with a compiler bug that prevents it from
>> working generally. However, we don't want to make it a feature of Swift
>> protocols and we can't eliminate it (see alternatives considered), so we
>> propose to rename the keyword to make it clear that this feature is intended
>> only for compatibility with Objective-C.
>> 
>> Proposed solution
>> 
>> Rename the optional contextual keyword to objcoptional. Note that:
>> 
>> It would read better as objc_optional or objcOptional, but keywords in Swift
>> run the words together, and
>> 
>> It should not be an attribute @objcOptional because it changes the effective
>> type of the declaration. Referencing an optional requirement wraps the
>> result in one more level of optional, which is used to test whether the
>> requirement was implemented.
>> 
>> This means that:
>> 
>> @objc protocol NSTableViewDelegate {
>>  optional func tableView(_: NSTableView, viewFor: NSTableColumn, row: Int)
>> -> NSView?
>>  optional func tableView(_: NSTableView, heightOfRow: Int) -> CGFloat
>> }
>> 
>> becomes:
>> 
>> @objc protocol NSTableViewDelegate {
>>  objcoptional func tableView(_: NSTableView, viewFor: NSTableColumn, row:
>> Int) -> NSView?
>>  objcoptional func tableView(_: NSTableView, heightOfRow: Int) -> CGFloat
>> }
>> 
>> Impact on existing code
>> 
>> Any code that declares @objc protocols with optional requirements will need
>> to be changed to use the objcoptionalkeyword. However, it is trivial for the
>> migrator to update the code and for the compiler to provide Fix-Its, so the
>> actual impact on users should be small.
>> 
>> Alternatives considered
>> 
>> It's a fairly common request to make optional requirements work in Swift
>> protocols (as in the aforementioned threads, hereand here). However, such
>> proposals have generally met with resistance because optional requirements
>> have significant overlap with other protocol features: "default"
>> implementations via protocol extensions and protocol inheritance. For the
>> former case, the author of the protocol can provide a "default"
>> implementation via a protocol extension that encodes the default case
>> (rather than putting it at the call site). In the latter case, the protocol
>> author can separate the optional requirements into a different protocol that
>> a type can adopt to opt-in to whatever behavior they customize. While not
>> exactlythe same as optional requirements, which allow one to perform
>> per-requirement checking to determine whether the type implemented that
>> requirement, the gist of the threads is that doing so is generally
>> considered an anti-pattern: one would be better off factoring the protocol
>> in a different way. Therefore, we do not propose to make optional
>> requirements work for Swift protocols.
>> 
>> The second alternative would be to eliminate optional requirements entirely
>> from the language. The primary challenge here is Cocoa interoperability,
>> because Cocoa's protocols (primarily delegates and data sources) have a
>> large number of optional requirements that would have to be handled somehow
>> in Swift. These optional requirements would have to be mapped to some other
>> construct in Swift, but the code generation model must remain the same
>> because the Cocoa frameworks rely on the ability to ask the question "was
>> this requirement implemented by the type?" in Objective-C code at run time.
>> 
>> The most popular approach to try to map optional requirements into existing
>> Swift constructs is to turn an optional method requirement into a property
>> of optional closure type. For example, this Objective-C protocol:
>> 
>> @protocol NSTableViewDelegate
>> @optional
>> - (nullable NSView *)tableView:(NSTableView *)tableView
>> viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row;
>> - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row;
>> @end
>> 
>> which currently imports into Swift as:
>> 
>> @objc protocol NSTableViewDelegate {
>>  optional func tableView(_: NSTableView, viewFor: NSTableColumn, row: Int)
>> -> NSView?
>>  optional func tableView(_: NSTableView, heightOfRow: Int) -> CGFloat
>> }
>> 
>> would become, e.g.,
>> 
>> @objc protocol NSTableViewDelegate {
>>  var tableView: ((NSTableView, viewFor: NSTableColumn, row: Int) ->
>> NSView?)? { get }
>>  var tableView: ((NSTableView, heightOfRow: Int) -> CGFloat)? { get }
>> }
>> 
>> Unfortunately, this introduces an overloaded property named tableView. To
>> really make this work, we would need to introduce the ability for a property
>> to have a compound name, which would also let us take the labels out of the
>> function type:
>> 
>> @objc protocol NSTableViewDelegate {
>>  var tableView(_:viewFor:row:): ((NSTableView, NSTableColumn, Int) ->
>> NSView?)? { get }
>>  var tableView(_:heightOfRow:): ((NSTableView, Int) -> CGFloat)? { get }
>> }
>> 
>> By itself, that is a good feature. However, we're not dont, because we would
>> need yet another extension to the language: one would want to be able to
>> provide a method in a class that is used to conform to a property in the
>> protocol, e.g.,
>> 
>> class MyDelegate : NSObject, NSTableViewDelegate {
>>  func tableView(_: NSTableView, viewFor: NSTableColumn, row: Int) ->
>> NSView? { ... }
>>  func tableView(_: NSTableView, heightOfRow: Int) -> CGFloat { ... }
>> }
>> 
>> Indeed, the Objective-C implementation model effectively requires us to
>> satisfy these property-of-optional-closure requirements with methods so that
>> Objective-C clients can use -respondsToSelector:. In other words, one would
>> not be able to implement these requirements in by copy-pasting from the
>> protocol to the implementing class:
>> 
>> class MyDelegate : NSObject, NSTableViewDelegate {
>>  // Note: The Objective-C entry points for these would return blocks, which
>> is incorrect
>>  var tableView(_:viewFor:row:): ((NSTableView, NSTableColumn, Int) ->
>> NSView?)? { return ...   }
>>  var tableView(_:heightOfRow:): ((NSTableView, Int) -> CGFloat)? { return
>> ... }
>> }
>> 
>> That is both a strange technical restriction that would be limited to
>> Objective-C protocols and a serious usability problem: the easiest way to
>> stub out the contents of your type when it conforms to a given protocol is
>> to copy the declarations from the protocol into your type, then fill in the
>> details. This change would break that usage scenario badly.
>> 
>> There have been other ideas to eliminate optional requirements. For example,
>> Objective-C protocols could be annotated with attributes that say what the
>> default implementation for each optional requirement is (to be used only in
>> Swift), but such a massive auditing effort is impractical. There is a
>> related notion of caller-site default implementations that was not
>> well-received due to its complexity.
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> 


More information about the swift-evolution mailing list