[Review] SE-0026 Abstract classes and methods

I just wanted to put focus on the visibility issues, which you could not see (it seems). Your general arguments about ways to improve protocols so that not only classes could be enhanced remain quite valid and inspiring.

Gwendal

···

Le 4 mars 2016 à 20:24, Gwendal Roué <gwendal.roue@gmail.com> a écrit :

Le 4 mars 2016 à 08:45, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

For modeling subtype requirements we would need the ability to declare protocol members with reduced access scope like private or internal and hopefully protected, as subtype requirements most often are not public.

I've spoken about this elsewhere, but:

- For both abstract classes and protocols, all required/abstract members need to be visible everywhere you're permitted to conform/inherit.
- There is currently no way in Swift to decouple a protocol's/class's visibility from the ability to conform to/inherit from it, so neither construct can currently offer this feature.
- However, this feature is likely to come at least for classes as part of resiliency. I think it's a good idea for protocols, too.

Brent, please consider the sample code below:

It is my humble opinion that if this is the core advantage of this proposal, it should not be accepted. This is a major change in the language, not just a small new feature to be added, and if it can be done with existing functionality (like protocols)

But it can't be done with protocols. That's the point.

- Already possible with a little different syntax. Nonetheless, if you use protocols properly the EXACT functionality can be achieved. The compiler WILL force you to write an implementation for a protocol.

It's not the same functionality.

- Again, protocols are shared between structs and classes, its a syntax unifier to use protocols. With a language that uses structs for 97% of its Standard Library, protocols seem as a better idea for the sake of clarity and unity.

Ah, now we're getting to the crux of it, I think. So, the argument is that because 97% of the Swift standard library is structs, we should not consider any new functionality that is limited to classes?

The reason we're discussing a class-based solution to the problem is that the problem is inherently related to having an inheritance hierarchy.

This is why I keep going back to the fundamental question: are classes not first-class citizens in Swift? Your argument seems to be that they're not.

If they are, however, then I think it warrants a solution that's limited to classes.

Each new feature has o be learned by all Swift programmers. Why add a different syntax (that has to be learned) for existing functionality, when protocols are shared between value and reference types?

Because protocols don't provide the feature I seek, no matter how many times people tell me otherwise ;)

This should be resolved when PwS
<http://article.gmane.org/gmane.comp.lang.swift.evolution/3082&gt;\(Protocol
with Storage) get alive:

  // Framework land
        public protocol DatabaseRecord {
                var persistedRow: DatabaseRow { get }
                static func databaseTableName() -> String
                init(row: DatabaseRow)
        }
      internal extension DatabaseRecord {
* var referenceRow: DatabaseRow*
                // or other storage. Syntax and place of declarations is
not defined.
       }
       public extension DatabaseRecord {
                var hasChanges: Bool {
                        // return complex computation based on referenceRow
and persistedRow
                }
        }

This is another proposal, but the idea is improve the current protocol
model instead to create another type just because a single skill.

···

Em sex, 4 de mar de 2016 às 16:28, Gwendal Roué <swift-evolution@swift.org> escreveu:

> Le 4 mars 2016 à 20:24, Gwendal Roué <gwendal.roue@gmail.com> a écrit :
>
>
>> Le 4 mars 2016 à 08:45, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> a écrit :
>>
>>> For modeling subtype requirements we would need the ability to declare
protocol members with reduced access scope like private or internal and
hopefully protected, as subtype requirements most often are not public.
>>
>> I've spoken about this elsewhere, but:
>>
>> - For both abstract classes and protocols, all required/abstract
members need to be visible everywhere you're permitted to conform/inherit.
>> - There is currently no way in Swift to decouple a protocol's/class's
visibility from the ability to conform to/inherit from it, so neither
construct can currently offer this feature.
>> - However, this feature is likely to come at least for classes as part
of resiliency. I think it's a good idea for protocols, too.
>
> Brent, please consider the sample code below:

I just wanted to put focus on the visibility issues, which you could not
see (it seems). Your general arguments about ways to improve protocols so
that not only classes could be enhanced remain quite valid and inspiring.

Gwendal

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

Classes are definitely first-class citizens. And I’ll concede that this proposal has its advantages. But I do strongly believe, that in accordance to the roadmap for Swift 3, this should not be accepted, and should instead be introduced in Swift 4. There is a precedence for good proposals being pushed to next year, since Swift 3’s goal is stabilization.

  Coleman,

···

On Mar 3, 2016, at 4:49 PM, Evan Maloney <emaloney@gilt.com> wrote:

It is my humble opinion that if this is the core advantage of this proposal, it should not be accepted. This is a major change in the language, not just a small new feature to be added, and if it can be done with existing functionality (like protocols)

But it can't be done with protocols. That's the point.

- Already possible with a little different syntax. Nonetheless, if you use protocols properly the EXACT functionality can be achieved. The compiler WILL force you to write an implementation for a protocol.

It's not the same functionality.

- Again, protocols are shared between structs and classes, its a syntax unifier to use protocols. With a language that uses structs for 97% of its Standard Library, protocols seem as a better idea for the sake of clarity and unity.

Ah, now we're getting to the crux of it, I think. So, the argument is that because 97% of the Swift standard library is structs, we should not consider any new functionality that is limited to classes?

The reason we're discussing a class-based solution to the problem is that the problem is inherently related to having an inheritance hierarchy.

This is why I keep going back to the fundamental question: are classes not first-class citizens in Swift? Your argument seems to be that they're not.

If they are, however, then I think it warrants a solution that's limited to classes.

Each new feature has o be learned by all Swift programmers. Why add a different syntax (that has to be learned) for existing functionality, when protocols are shared between value and reference types?

Because protocols don't provide the feature I seek, no matter how many times people tell me otherwise ;)

Yes. But I wanted to talk about visibility, not about storage. Quoting myself:

The problem with protocols is that the implementation detail referenceRow has to be public (when it should remain an implementation detail), and that the conforming types must provide it (when they should not even know about its existence):

  // Framework land
  public abstract class DatabaseRecord {
    // DatabaseRow initializer
    public required init(row: DatabaseRow) {
      referenceRow = row
    }

    // The table name
    public abstract class func databaseTableName() -> String

    // What should be stored in the database
    public abstract var persistedRow: DatabaseRow
    
    // True if record has been changed since last fetch
    public var hasChanges: Bool {
      // return complex computation based on referenceRow and persistedRow
    }
    
    // Hidden implementation detail
    internal var referenceRow: DatabaseRow
  }
  
  // User land
  class Person : DatabaseRecord {
    var name: String
    class func databaseTableName() -> String { return "persons" }
    init(row: DatabaseRow) {
      name = row["name"]
      super.init(row)
    }
    var persistedRow: DatabaseRow { return Row("name": name) }
  }

  let person = Person(row: …)
  person.name // "foo"
  person.hasChanges // false
  person.name = "bar"
  person.hasChanges // true

How do we express this with protocols?

  // Framework land
  public protocol DatabaseRecord {
    var referenceRow: DatabaseRow { get set }
    var persistedRow: DatabaseRow { get }
    static func databaseTableName() -> String
    init(row: DatabaseRow)
  }

  extension DatabaseRecord {
    public var hasChanges: Bool {
      // return complex computation based on referenceRow and persistedRow
    }
  }
  
  // User land
  class Person: DatabaseRecord {
    var referenceRow: DatabaseRow
    var name: String
    init(row: DatabaseRow) {
      name = row["name"]
      super.init(row)
    }
    class func databaseTableName() -> String { return « persons" }
    var persistedRow: DatabaseRow { return Row("name": name) }
  }

  let person = Person(row: …)
  person.name // "foo"
  person.hasChanges // false
  person.name = "bar"
  person.hasChanges // true
  person.referenceRow = … // messed up internal state

···

Le 4 mars 2016 à 21:28, Wallacy <wallacyf@gmail.com> a écrit :

This should be resolved when PwS <http://article.gmane.org/gmane.comp.lang.swift.evolution/3082&gt;\(Protocol with Storage) get alive:

[…]

This is another proposal, but the idea is improve the current protocol model instead to create another type just because a single skill.

This should be resolved when PwS <http://article.gmane.org/gmane.comp.lang.swift.evolution/3082&gt;\(Protocol with Storage) get alive:

  // Framework land
        public protocol DatabaseRecord {
      internal extension DatabaseRecord {
                var referenceRow: DatabaseRow
       public extension DatabaseRecord {

Yay for visibility and storage in protocols, and generally for being able to provide a different API to the implementers of the protocol compared to the clients of the protocol.

But.

So explain to me: if this code worked:

  protocol ActivityViewControlling: UIViewController {
    func retrieveText() -> String
  }
  extension ActivityViewControlling {
    @IBOutlet var messageLabel: UILabel!
    
    override func viewWillAppear(animated: Bool) {
      super.viewWillAppear(animated)
      messageLabel.text = retrieveText()
    }
  }

What would you feel was missing compared to this?

  abstract class ActivityViewController: UIViewController {
    abstract func retrieveText() -> String
    
    @IBOutlet var messageLabel: UILabel!
    
    override func viewWillAppear(animated: Bool) {
      super.viewWillAppear(animated)
      messageLabel.text = retrieveText()
    }
  }

For all intents and purposes, you made an abstract class here, but calling it a protocol.

It has advantages:

+ you can achieve multiple inheritance that way
+ ... hm? what else?

and disadvantages:

+ it's still, for all intents and purposes, an abstract class, but for some reason you're now calling it a protocol — THAT'S confusing, if you ask me; now there's two kinds of protocols, the normal ones and the ones that do magic stuff when you implement them (I personally think those latter thingies should be called mixins)
+ it has additional limitations (I don't think you can call super, which is often very useful)
+ it's a concept that doesn't work or exist, and there's no telling if it'll be a part of Swift 3 or not

I believe we're confusing conceptual and implementation details here. Conceptually, both approaches seem to be using two different names for the same thing; I don't really care if the functionality I need comes through a supercharged protocol or a slightly enhanced class.

Are we really discussing anything more than a name? If yes, what is it?

Implementation-wise, the compiler can always translate abstract classes into protocols internally, so I don't really see the implementation difference as important.

A.

abstract classes may have impact on ABI : that’s the reason we may add this in release 3 if we want to be able to introduction binary interrop in Swift 4 (such as dynamic code loading,etc…).

David

···

Le 3 mars 2016 à 22:52, Alsey Miller via swift-evolution <swift-evolution@swift.org> a écrit :

Classes are definitely first-class citizens. And I’ll concede that this proposal has its advantages. But I do strongly believe, that in accordance to the roadmap for Swift 3, this should not be accepted, and should instead be introduced in Swift 4. There is a precedence for good proposals being pushed to next year, since Swift 3’s goal is stabilization.

  Coleman,

  abstract classes may have impact on ABI : that’s the reason we may add this in release 3 if we want to be able to introduction binary interrop in Swift 4 (such as dynamic code loading,etc…).

If making an existing public concrete class into an abstract class would be a nonresilient change anyway—and I cannot *possibly* imagine how it would not be—then any abstract classes would have to be new, post-Swift 4 code, and there would be no existing code to break ABI compatibility with.

Swift 3 is not last call for features which require additions to the ABI; it's last call for features which might *change* existing ABI.

···

--
Brent Royal-Gordon
Architechies