[Proposal draft] Remove objc requirement for optional protocol methods/properties


(David Scrève) #1

## Introduction

When using protocol, we usually need to use optional method or property. We propose to remove the
requirement of @objc keyword for such protocol.

## Motivation

objc protocol have many restriction to regular Swift protocol : they cannot use Swift s
specific feature. For example, methods cannot use struct, tuple, etc...

Example :
Considere the following code :

struct Person {
    let name:String
    let surname:String
}

protocol PersonRetrievable {
    var personDescription : String { get }
    func loadPerson() -> Person
}

If we want to make the loadPerson() method optional, we need to add the attribut @objc
to the protocol :

@objc
protocol PersonRetrievable {
    var personDescription : String { get }
    optional func loadPerson() -> Person
}

But this code not longer compiles because struct are not supported on objc protocol.

The only workaround is to transform Person struct to class which involve a major change
because Person is no longer a value type.

## Proposed solution
We propose to remove objc requirement for optional methods and properties in protocol and
make this kind of code compilable :

struct Person {
    let name:String
    let surname:String
}

protocol PersonRetrievable {
    var personDescription : String { get }
    optional func loadPerson() -> Person
}

## Detailed design
There's no specific design : A non-objc protocol is still not visible in ObjC code.

Type is mandatory for abstract properties since it cannot be inferred.

## Impact on existing code
This change has no impact on existing code, but might change the ABI that is being
stabilizing in Swift 3.0.

## Alternatives considered
Without this feature, we must use objc objects as parameters.


(Chris Lattner) #2

Hi David,

This is certainly a glaring hole in the system, and one we need to discuss. Here are some problems with making optional requirements a first class thing in Swift:

1. They overlap heavily (but are syntactically privileged) with optional properties, consider the difference between "optional func f() -> Int" vs "var f : (() -> Int)? {get}”.

2. Protocols with default implementations provide the vastly most common use-cases for these.

3. They overload “optional” terminology in the language to mean something different.

4. They are non-sensical - how can something be a requirement and be optional? :)

Doug has it on his plate to explore what we can do about this, in the Swift 3 timeframe, but not in the immediate future. IMO, it would be really great if we could make Objective-C optional requirements disappear in the Clang importer, and transform them into requirements with a default implementation.

-Chris

···

On Jan 9, 2016, at 2:00 PM, David Scrève via swift-evolution <swift-evolution@swift.org> wrote:

## Introduction

When using protocol, we usually need to use optional method or property. We propose to remove the
requirement of @objc keyword for such protocol.


(Matthew Johnson) #3

## Introduction

When using protocol, we usually need to use optional method or property. We propose to remove the
requirement of @objc keyword for such protocol.

Hi David,

This is certainly a glaring hole in the system, and one we need to discuss. Here are some problems with making optional requirements a first class thing in Swift:

1. They overlap heavily (but are syntactically privileged) with optional properties, consider the difference between "optional func f() -> Int" vs "var f : (() -> Int)? {get}”.

2. Protocols with default implementations provide the vastly most common use-cases for these.

3. They overload “optional” terminology in the language to mean something different.

4. They are non-sensical - how can something be a requirement and be optional? :slight_smile:

Doug has it on his plate to explore what we can do about this, in the Swift 3 timeframe, but not in the immediate future. IMO, it would be really great if we could make Objective-C optional requirements disappear in the Clang importer, and transform them into requirements with a default implementation.

+1 to this approach.

···

On Jan 9, 2016, at 7:29 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 9, 2016, at 2:00 PM, David Scrève via swift-evolution <swift-evolution@swift.org> wrote:

-Chris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Scrève) #4

Hi Chris,

This is certainly a glaring hole in the system, and one we need to discuss. Here are some problems with making optional requirements a first class thing in Swift:

1. They overlap heavily (but are syntactically privileged) with optional properties, consider the difference between "optional func f() -> Int" vs "var f : (() -> Int)? {get}”.

  You are right. I suggest that we restrict optional keyword in protocol to methods only to fix this issue and keep the actual meaning.

2. Protocols with default implementations provide the vastly most common use-cases for these.

3. They overload “optional” terminology in the language to mean something different.

4. They are non-sensical - how can something be a requirement and be optional? :slight_smile:

  Actually, optional methods allow to add methods to interface without breaking existing code and for me, conforming to protocol means « you can optionally implement this methods but you must implements others ». Conforming does not mean implements all feature but the protocol defines minimum requirements.

Doug has it on his plate to explore what we can do about this, in the Swift 3 timeframe, but not in the immediate future. IMO, it would be really great if we could make Objective-C optional requirements disappear in the Clang importer, and transform them into requirements with a default implementation.

  I agree that default implementation seems to be the right solution but I’m afraid it introduces other problems with scope.

  Consider the UITableViewDataSource protocol and the heightForRowAtIndexPath. The default implementation method uses UITableView internal properties and methods. Then, if we provide default implementation for this method in protocol, we will need to reference the called object in protocol.

  Actually, the default implementation may depend of the caller and can be different for each caller in the actual model.

  If you attach default implementation to protocol declaration, you will not be able to have caller specific default implementation and will have a basic and quite useless default implementation i.e. returning nil or invalid value.

  I thing this is a major issue because ObjC default implementation behavior is really powerful.

  One workaround would be to provided default implementation when declaring delegate attribute, but this would make syntax very complicated.

  Java has introduced default implementation in interface and it create confusion between classes and interfaces and does not solve the issue I exposed below.

  I think we should keep the actual model by limiting optional keyword to methods, which has really a meaning.

  Anyway, I did not think about this before Swift 3 or later.

David

···

Le 10 janv. 2016 à 02:29, Chris Lattner <clattner@apple.com> a écrit :


(Douglas Gregor) #5

Hi Chris,

This is certainly a glaring hole in the system, and one we need to discuss. Here are some problems with making optional requirements a first class thing in Swift:

1. They overlap heavily (but are syntactically privileged) with optional properties, consider the difference between "optional func f() -> Int" vs "var f : (() -> Int)? {get}”.

  You are right. I suggest that we restrict optional keyword in protocol to methods only to fix this issue and keep the actual meaning.

That wasn’t Chris’s point. Compare

  protocol P {
    optional func f() -> Int
  }

with

  protocol P {
    var f: (() -> Int)? { get }
  }

  extension P {
    var f: (() -> Int)? { return nil }
  }

Both will have the same “call-side” syntax, e.g., accessing the “f” member of something that conforms to “P” gives you a value of type “(() -> Int)?”. You can use ? to optionally call it like so:

  func testF<T : P>(t: T) {
    let x = t.f?() // call “f’ if it is available.
  }

2. Protocols with default implementations provide the vastly most common use-cases for these.

3. They overload “optional” terminology in the language to mean something different.

4. They are non-sensical - how can something be a requirement and be optional? :slight_smile:

  Actually, optional methods allow to add methods to interface without breaking existing code and for me, conforming to protocol means « you can optionally implement this methods but you must implements others ». Conforming does not mean implements all feature but the protocol defines minimum requirements.

Although it’s not implemented now, one will be able to add new requirements to a protocol so long as those new requirements have a default implementation. The only thing “optional” requirements give you beyond default implementations is the ability to determine whether the requirement was fulfilled.

Doug has it on his plate to explore what we can do about this, in the Swift 3 timeframe, but not in the immediate future. IMO, it would be really great if we could make Objective-C optional requirements disappear in the Clang importer, and transform them into requirements with a default implementation.

  I agree that default implementation seems to be the right solution but I’m afraid it introduces other problems with scope.

  Consider the UITableViewDataSource protocol and the heightForRowAtIndexPath. The default implementation method uses UITableView internal properties and methods. Then, if we provide default implementation for this method in protocol, we will need to reference the called object in protocol.

Given the tight integration between UITableViewDataSource and UITableView, it is completely reasonable for the default implementation of a requirement in UITableViewDataSource to use internal methods of UITableView.

  Actually, the default implementation may depend of the caller and can be different for each caller in the actual model.

  If you attach default implementation to protocol declaration, you will not be able to have caller specific default implementation and will have a basic and quite useless default implementation i.e. returning nil or invalid value.

If you need to distinguish between types that did or did not provide their own implementation, you can use a “nil” result or the property trick above.

  I thing this is a major issue because ObjC default implementation behavior is really powerful.

Given that we can get the same behavior in Swift without optional requirements, it’s not any more powerful… it’s just more convenient for those cases. So, the question is whether those cases are common enough—or the workarounds awful enough—to warrant having two very similar features in Swift.

  Java has introduced default implementation in interface and it create confusion between classes and interfaces and does not solve the issue I exposed below.

We already have default implementations for protocols (via extensions) as well as classes with implementation inheritance, so any related confusion is already there, so I don’t see how the comparison matters.

  I think we should keep the actual model by limiting optional keyword to methods, which has really a meaning.

I don’t see why you would limit it to methods. There are a number of optional properties in Cocoa[Touch], for example, and it “wrap the result in an extra level of optional” is a reasonable semantic approach here.

  - Doug

···

On Jan 10, 2016, at 1:31 AM, David Scrève via swift-evolution <swift-evolution@swift.org> wrote:

Le 10 janv. 2016 à 02:29, Chris Lattner <clattner@apple.com> a écrit :


(David Scrève) #6

Hi Chris,

This is certainly a glaring hole in the system, and one we need to discuss. Here are some problems with making optional requirements a first class thing in Swift:

1. They overlap heavily (but are syntactically privileged) with optional properties, consider the difference between "optional func f() -> Int" vs "var f : (() -> Int)? {get}”.

  You are right. I suggest that we restrict optional keyword in protocol to methods only to fix this issue and keep the actual meaning.

That wasn’t Chris’s point. Compare

  protocol P {
    optional func f() -> Int
  }

with

  protocol P {
    var f: (() -> Int)? { get }
  }

  extension P {
    var f: (() -> Int)? { return nil }
  }

Both will have the same “call-side” syntax, e.g., accessing the “f” member of something that conforms to “P” gives you a value of type “(() -> Int)?”. You can use ? to optionally call it like so:

  func testF<T : P>(t: T) {
    let x = t.f?() // call “f’ if it is available.
  }

  Yes, I agree with you…I didn’t read carefully the answer from Chris. But the implementation is more complex :

struct Person {
    let name:String
    let surname:String
}

protocol PersonRetrievable {
    var personDescription : String { get }
    var loadPerson:(() -> Person)? { get }
}

extension PersonRetrievable {
    var loadPerson:(() -> Person)? { return nil }
}

class GroupPerson : PersonRetrievable {
    var personDescription : String {
        return "Hello World";
    }
    
    var loadPerson:(() -> Person)? {
        return { return Person(name: "Scrève", surname: "David")}
    }
}

  Double returns is quite complex to write and understand..but I agree this problem can be solved like this.

  Java has introduced default implementation in interface and it create confusion between classes and interfaces and does not solve the issue I exposed below.

We already have default implementations for protocols (via extensions) as well as classes with implementation inheritance, so any related confusion is already there, so I don’t see how the comparison matters.

  Another question is about priority : Is there any guarantee that implementation of protocol will be called instead of extension ? I don’t remembre reading this anywhere.

  I think we should keep the actual model by limiting optional keyword to methods, which has really a meaning.

I don’t see why you would limit it to methods. There are a number of optional properties in Cocoa[Touch], for example, and it “wrap the result in an extra level of optional” is a reasonable semantic approach here.

  I wrote that because properties already have optional behavior with their type.

  Another idea would be to remove the optional keyword and use the same syntax for method with question mark :

    func loadPerson?()

  And the compiler generates the code with the double return and property.

  What do you think about this ?

David

···

Le 11 janv. 2016 à 05:56, Douglas Gregor <dgregor@apple.com> a écrit :

On Jan 10, 2016, at 1:31 AM, David Scrève via swift-evolution <swift-evolution@swift.org> wrote:

Le 10 janv. 2016 à 02:29, Chris Lattner <clattner@apple.com> a écrit :


(Douglas Gregor) #7

Hi Chris,

This is certainly a glaring hole in the system, and one we need to discuss. Here are some problems with making optional requirements a first class thing in Swift:

1. They overlap heavily (but are syntactically privileged) with optional properties, consider the difference between "optional func f() -> Int" vs "var f : (() -> Int)? {get}”.

  You are right. I suggest that we restrict optional keyword in protocol to methods only to fix this issue and keep the actual meaning.

That wasn’t Chris’s point. Compare

  protocol P {
    optional func f() -> Int
  }

with

  protocol P {
    var f: (() -> Int)? { get }
  }

  extension P {
    var f: (() -> Int)? { return nil }
  }

Both will have the same “call-side” syntax, e.g., accessing the “f” member of something that conforms to “P” gives you a value of type “(() -> Int)?”. You can use ? to optionally call it like so:

  func testF<T : P>(t: T) {
    let x = t.f?() // call “f’ if it is available.
  }

  Yes, I agree with you…I didn’t read carefully the answer from Chris. But the implementation is more complex :

struct Person {
    let name:String
    let surname:String
}

protocol PersonRetrievable {
    var personDescription : String { get }
    var loadPerson:(() -> Person)? { get }
}

extension PersonRetrievable {
    var loadPerson:(() -> Person)? { return nil }
}

class GroupPerson : PersonRetrievable {
    var personDescription : String {
        return "Hello World";
    }
    
    var loadPerson:(() -> Person)? {
        return { return Person(name: "Scrève", surname: "David")}
    }
}

  Double returns is quite complex to write and understand..but I agree this problem can be solved like this.

Right. One could extend the language with a rule that allows a method to satisfy a read-only property requirement when that property is of (possibly optional) function type.

  Java has introduced default implementation in interface and it create confusion between classes and interfaces and does not solve the issue I exposed below.

We already have default implementations for protocols (via extensions) as well as classes with implementation inheritance, so any related confusion is already there, so I don’t see how the comparison matters.

  Another question is about priority : Is there any guarantee that implementation of protocol will be called instead of extension ? I don’t remembre reading this anywhere.

The rules are a little muddy and are being discussed in other threads (e.g., those about dynamic dispatch of members of protocol extensions).

  I think we should keep the actual model by limiting optional keyword to methods, which has really a meaning.

I don’t see why you would limit it to methods. There are a number of optional properties in Cocoa[Touch], for example, and it “wrap the result in an extra level of optional” is a reasonable semantic approach here.

  I wrote that because properties already have optional behavior with their type.

  Another idea would be to remove the optional keyword and use the same syntax for method with question mark :

    func loadPerson?()

  And the compiler generates the code with the double return and property.

  What do you think about this ?

Per my first comment about about satisfying a read-only property of function type with a method, I don’t think we need a special syntax at all to make it easy to conform to these protocols.

  - Doug

···

On Jan 11, 2016, at 12:08 AM, David Scrève <david.screve@dlta-studio.com> wrote:

Le 11 janv. 2016 à 05:56, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> a écrit :

On Jan 10, 2016, at 1:31 AM, David Scrève via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Le 10 janv. 2016 à 02:29, Chris Lattner <clattner@apple.com <mailto:clattner@apple.com>> a écrit :


(David Scrève) #8

    var loadPerson:(() -> Person)? {
        return { return Person(name: "Scrève", surname: "David")}
    }
}

  Double returns is quite complex to write and understand..but I agree this problem can be solved like this.

Right. One could extend the language with a rule that allows a method to satisfy a read-only property requirement when that property is of (possibly optional) function type.

  Yes, this could be a really good solution…Should I post a proposal for that ?

  And the compiler generates the code with the double return and property.

  What do you think about this ?

Per my first comment about about satisfying a read-only property of function type with a method, I don’t think we need a special syntax at all to make it easy to conform to these protocols.

  I agree :slight_smile:

David

···

Le 11 janv. 2016 à 18:20, Douglas Gregor <dgregor@apple.com> a écrit :


(Douglas Gregor) #9

If you want to tackle the larger proposal of “remove optional requirements” and the various pieces required to make that happen, sure. One issue I haven’t thought about much is how to deal with the argument labels when performing this transformation, because we cannot overload properties. For example, importing UITableViewDataSource would end up with several dozen things named “tableview” :slight_smile:

  - Doug

···

On Jan 11, 2016, at 12:24 PM, David Scrève <david.screve@dlta-studio.com> wrote:

Le 11 janv. 2016 à 18:20, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> a écrit :

    var loadPerson:(() -> Person)? {
        return { return Person(name: "Scrève", surname: "David")}
    }
}

  Double returns is quite complex to write and understand..but I agree this problem can be solved like this.

Right. One could extend the language with a rule that allows a method to satisfy a read-only property requirement when that property is of (possibly optional) function type.

  Yes, this could be a really good solution…Should I post a proposal for that ?


(David Scrève) #10

Hum…not sure to have enough experience with Swift right now…But I’ll think about it :slight_smile:

David

···

Le 11 janv. 2016 à 21:39, Douglas Gregor <dgregor@apple.com> a écrit :

On Jan 11, 2016, at 12:24 PM, David Scrève <david.screve@dlta-studio.com <mailto:david.screve@dlta-studio.com>> wrote:

Le 11 janv. 2016 à 18:20, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> a écrit :

    var loadPerson:(() -> Person)? {
        return { return Person(name: "Scrève", surname: "David")}
    }
}

  Double returns is quite complex to write and understand..but I agree this problem can be solved like this.

Right. One could extend the language with a rule that allows a method to satisfy a read-only property requirement when that property is of (possibly optional) function type.

  Yes, this could be a really good solution…Should I post a proposal for that ?

If you want to tackle the larger proposal of “remove optional requirements” and the various pieces required to make that happen, sure. One issue I haven’t thought about much is how to deal with the argument labels when performing this transformation, because we cannot overload properties. For example, importing UITableViewDataSource would end up with several dozen things named “tableview” :slight_smile: