Interesting proposal, but I wanted to mention a couple of potential issues off
the top of my head. I know when I was using optional requirements in Objective
C, I would often use the presence/lack of the method (not just whether it
returned nil) in the logic of my program. I used the presence of a method as a
way for the implementor of a delegate to naturally communicate whether they
wanted a more advanced feature. The absence of the method itself is
information which can be utilized, not just whether it returns nil, and I
believe that is part of what people are asking for when they say they want
optional methods in Swift.
Let me try to give a simplified example which I am not sure how you would work
around in this proposal:
Let’s say there is a datasource protocol which optionally asks for an image
associated with a particular piece of data (imagine a table or collection view
type custom control). If the method is not implemented in the data source,
then a different view is shown for each data point that doesn’t have a place
for images at all. If the method is implemented, but returns nil, then a
default image is used as a placeholder instead (in a view which has a place for
images).
tl;dr: Optional methods are often used as customization points, and the
methods, if implemented, may also have another meaning/use for nil.
Similarly, a different back-end implementation may be used in the case where an
optional method is not implemented. Let’s say you have something like a
tableview with an optional method giving rowHeights. If that method is
unimplemented, it is possible to have a much more efficient layout algorithm…
and in some cases, you may check for the existence of the optional method when
the delegate is set, and swap out a different layout object based on which
customizations are needed (and again nil might mean that a height/etc... should
be automatically calculated). This is the ability I miss the most.
Not saying the proposal is unworkable, just wanted to add some food for
thought. I know I really miss optional methods in Swift. In some areas Swift
is a lot more powerful, but there are lots of things I used to do in Obj C that
I haven’t figured out how to do in Swift yet (if they are even possible). I am
kind of disturbed by the trend/desire to get rid of the smalltalk-ness, as
opposed to finding new and safer ways to support that
flexibility/expressiveness. I would really like to see swift deliver on it’s
promise of being a more modern alternative to ObjC (which it isn’t yet, IMHO)
instead of just a more modern alternative to C++/Java.
Thanks,
Jon
> Proposed Solution: Caller-side default implementations
>
> Default implementations and optional requirements differ most on the caller
> side. For example, let’s use NSTableView delegate as it’s imported today:
>
> func useDelegate(delegate: NSTableViewDelegate) {
> if let getView = delegate.tableView(_:viewFor:row:) { // since the
> requirement is optional, a reference to the method produces a value of
> optional function type
> // I can call getView here
> }
>
> if let getHeight = delegate.tableView(_:heightOfRow:) {
> // I can call getHeight here
> }
> }
>
> 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
> }
>
> 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