[Draft] Allow declaration of abstract functions and properties on classes

After Thorsten reminded me yesterday that I wanted this feature too, I spent some time writing up proposal to allow declaration of abstract functions and properties on Swift classes:

https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c

This is the first draft; all comments welcome!

Thanks,
E.

···

---

Allow declaration of abstract functions and properties on classes

Proposal: SE-NNNN
Author: Evan Maloney <https://github.com/emaloney>
Status: Draft
Review manager: TBD
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#introduction>Introduction

Swift protocols are useful for the declaring interfaces that must be provided by conforming entities (in other words: structs, classes and enums). Adopters of a given protocol are free to provide any implementation they wish, so long as it supplies the necessary interfaces.

Separating interface from implementation is widely considered to be a best practice in software design, and the Swift protocol is designed for this use-case.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-problem>The Problem

Unfortunately, the protocol does not cover all of the cases where a developer might want to specify an interface to be implemented by another entity.

For example, consider the class, which allows the creation of an inheritance hierarchy. Often, a class in a hierarchy exists merely to provide a common implementation to subclasses. Such classes aren't ever intended to be instantiated directly; only subclasses will be instantiated.

To illustrate the point, imagine a view controller class that:

Places an animating UIActivityIndicatorView onscreen
Performs some operation to retrieve some text
Puts the text in a UITextView and places it onscreen
Hides the UIActivityIndicatorView
Now imagine you had many cases in your application where you could benefit from such a view controller, and each case differed only in the operation required to retrieve the text (represented by Step 2 above).

Ideally, you would be able to achieve this by declaring the interface for a function without needing to specify an implementation, the same way you would with a protocol:

func retrieveText() -> String
In other languages, such as C++, this concept exists in the form of an abstract class. However, Swift does not support this, so developers are forced to provide useless implementations such as:

func retrieveText() -> String
{
    fatalError("Subclasses must implement retrieveText()")
}
The idea here is that subclasses should always provide a retrieveText() implementation, and therefore the call to fatalError() should never be hit.

This has a few significant downsides:

It forces the developer to write code that should never be executed under normal conditions. This seems like a waste.

Because a default implementation is provided--the one that calls fatalError()--the compiler has no way of knowing that the subclasses are supposed to provide an implementation, too.

If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#proposed-solution>Proposed Solution

The proposed solution involves adding support for abstract classes to Swift.

This would entail:

Allowing functions and properties to be declared abstract. An abstract function or property declares the interface without specifying the implementation.

Allowing abstract classes to be defined by partially unimplemented protocol conformances. If a class declares conformance to a protocol without providing an implementation for each of that protocol's properties and functions, it is an abstract class.

Requiring classes to be explicitly declared as abstract if it has one or more unimplemented functions or properties.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#detailed-design>Detailed Design

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-functions>Abstract functions

Functions can be declared abstract using the abstract keyword, which must appear before the func keyword in the declaration. Otherwise, the notation is identical to how the function would be declared if it were to appear in a protocol:

public abstract func retrieveText() -> String
As long as the abstract keyword appears before the func, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public func retrieveText() -> String
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-properties>Abstract properties

Abstract property declarations are identical to what would be found in a protocol, but are prefixed with the abstractkeyword, which must appear first:

abstract var fileName: String { get }
abstract var favoriteColor: UIColor { get set }
As is typical with protocol declarations, var is always used and not let.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-protocol-conformance>Abstract protocol conformance

A class can be made abstract by declaring conformance to a protocol that it does not implement fully.

For example, say you had a protocol Vehicle:

protocol Vehicle
{
    var name: String { get }
    var color: UIColor { get }
    var numberOfWheels: Int { get }
    var isParked: Bool { get set }

    func driveTo(destination: Location) throws
}
In your code, you're able to factor out everything except the driveTo() function, the implementation of which is vehicle-specific. The common code goes into a BaseVehicle class:

abstract class BaseVehicle: Vehicle
{
    let name: String
    let color: UIColor
    let numberOfWheels: Int
    var isParked: Bool

    init(name: String, color: UIColor, numberOfWheels: Int, isParked: Bool = true)
    {
        self.name = name
        self.color = color
        self.numberOfWheels = numberOfWheels
        self.isParked = isParked
    }
}
The BaseVehicle class partially conforms to the Vehicle protocol: the name, color, numberOfWheels and isParked properties are provided, but the driveTo() function remains unimplemented.

As a result, BaseVehicle is an abstract class and must be declared as such.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-abstract-class-declaration>The abstract class declaration

A class must be declared as abstract if any of the following are true:

If the class declares one or more abstract functions or properties
If the class declares conformance to a protocol but does not supply implementations for every one of the functions and properties declared in that protocol.
If the class inherits from an abstract class and does not supply an implementation for every one of the unimplemented functions or properties.
Classes are marked as abstract by placing the abstract keyword before the class keyword at the top of the class declaration, eg.:

public abstract class MyAbstractClass
{
    // ...code...
}
As long as the abstract keyword appears before the class, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public class MyAbstractClass
{
    // ...code...
}
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#instantiation>Instantiation

Because an abstract class is not a complete implementation, the compiler will not allow instantiation of abstract classes.

Attempting to instantiate an abstract class will result in a compiler error.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#impact-on-existing-code>Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#citations>Citations

This idea has been discussed in the following swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution> mailing list threads:

[Review] SE-0030 Property Behaviors <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160222/010876.html>

Unfortunately, the protocol does not cover all of the cases where a developer might want to specify an interface to be implemented by another entity.

For example, consider the class, which allows the creation of an inheritance hierarchy. Often, a class in a hierarchy exists merely to provide a common implementation to subclasses. Such classes aren't ever intended to be instantiated directly; only subclasses will be instantiated.

To illustrate the point, imagine a view controller class that:

  • Places an animating UIActivityIndicatorView onscreen
  • Performs some operation to retrieve some text
  • Puts the text in a UITextView and places it onscreen
  • Hides the UIActivityIndicatorView
Now imagine you had many cases in your application where you could benefit from such a view controller, and each case differed only in the operation required to retrieve the text (represented by Step 2 above).

Ideally, you would be able to achieve this by declaring the interface for a function without needing to specify an implementation, the same way you would with a protocol:

func retrieveText() -> String
In other languages, such as C++, this concept exists in the form of an abstract class. However, Swift does not support this, so developers are forced to provide useless implementations such as:

func retrieveText() -> String

{
    fatalError(
"Subclasses must implement retrieveText()"
)
}

The idea here is that subclasses should always provide a retrieveText() implementation, and therefore the call to fatalError() should never be hit.

This has a few significant downsides:

  • It forces the developer to write code that should never be executed under normal conditions. This seems like a waste.

  • Because a default implementation is provided--the one that calls fatalError()--the compiler has no way of knowing that the subclasses are supposed to provide an implementation, too.

  • If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.

That's one alternative, yes. Others include:

1. Having a delegate provide the `retrieveText()` method.

2. Having a closure property implement the `retrieveText()` method.

3. Declaring a protocol `ActivityViewControlling` that requires `retrieveText()` and adding the other logic in an `extension ActivityViewControlling where Self: UIViewController`.

I think that 1 or 2 are usually the best way to handle something like this, but let's explore 3 for a minute, because that's a place where Swift could probably be improved.

Currently, Swift allows you to constrain a protocol to only class types:

  protocol ActivityViewControlling: class {
    func retrieveText() -> String
  }
  extension ActivityViewControlling where Self: UIViewController {
    ...
  }
  class MyActivityViewController: UIViewController, ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

But when you do that, Swift permits you to use any class type, which is a bit weird semantically—ActivityViewControlling can be applied to any class, but it's really only meant to be applied to subclasses of UIViewController. An ActivityViewControlling type which isn't a view controller is kind of meaningless.

  // Why can I do this?
  class PossibleButUseless: ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

Suppose instead we allow a protocol to require that the conforming class inherit from another class. We could then omit the `where` clause and possibly even make the inheritance itself implicit in conforming to the protocol:

  protocol ActivityViewControlling: UIViewController {
    func retrieveText() -> String
  }
  extension ActivityViewControlling {
    ...
  }
  class MyActivityViewController: ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

This would also relieve some of the pressure on us to support class-plus-protocol typed variables. In many of these cases, *all* types conforming to the protocol should be subclasses of a particular class, but there's no way to express that in the type system. Subclass-only protocols would correct that shortcoming.

Subclass-only protocols would cover most, if not all, of the use cases of abstract classes, but I think they would be a less dramatic change to Swift, because protocols are already types which can't be instantiated and impose requirements on their subtypes. I therefore believe they would be a better, more Swift-like approach to the problem abstract classes are trying to solve.

···

--
Brent Royal-Gordon
Architechies

+1.

A previous discussion on the topic : https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005728.html

Gwendal

···

Le 24 févr. 2016 à 17:36, Evan Maloney via swift-evolution <swift-evolution@swift.org> a écrit :

After Thorsten reminded me yesterday that I wanted this feature too, I spent some time writing up proposal to allow declaration of abstract functions and properties on Swift classes:

https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c

This is the first draft; all comments welcome!

Thanks,
E.

---

Allow declaration of abstract functions and properties on classes
Proposal: SE-NNNN
Author: Evan Maloney
Status: Draft
Review manager: TBD
Introduction

Swift protocols are useful for the declaring interfaces that must be provided by conforming entities (in other words: structs, classes and enums). Adopters of a given protocol are free to provide any implementation they wish, so long as it supplies the necessary interfaces.

Separating interface from implementation is widely considered to be a best practice in software design, and the Swift protocol is designed for this use-case.

The Problem

Unfortunately, the protocol does not cover all of the cases where a developer might want to specify an interface to be implemented by another entity.

For example, consider the class, which allows the creation of an inheritance hierarchy. Often, a class in a hierarchy exists merely to provide a common implementation to subclasses. Such classes aren't ever intended to be instantiated directly; only subclasses will be instantiated.

To illustrate the point, imagine a view controller class that:

Places an animating UIActivityIndicatorView onscreen
Performs some operation to retrieve some text
Puts the text in a UITextView and places it onscreen
Hides the UIActivityIndicatorView
Now imagine you had many cases in your application where you could benefit from such a view controller, and each case differed only in the operation required to retrieve the text (represented by Step 2 above).

Ideally, you would be able to achieve this by declaring the interface for a function without needing to specify an implementation, the same way you would with a protocol:

func retrieveText() -> String
In other languages, such as C++, this concept exists in the form of an abstract class. However, Swift does not support this, so developers are forced to provide useless implementations such as:

func retrieveText() -> String
{
    fatalError("Subclasses must implement retrieveText()")
}
The idea here is that subclasses should always provide a retrieveText() implementation, and therefore the call to fatalError() should never be hit.

This has a few significant downsides:

It forces the developer to write code that should never be executed under normal conditions. This seems like a waste.

Because a default implementation is provided--the one that calls fatalError()--the compiler has no way of knowing that the subclasses are supposed to provide an implementation, too.

If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.

Proposed Solution

The proposed solution involves adding support for abstract classes to Swift.

This would entail:

Allowing functions and properties to be declared abstract. An abstract function or property declares the interface without specifying the implementation.

Allowing abstract classes to be defined by partially unimplemented protocol conformances. If a class declares conformance to a protocol without providing an implementation for each of that protocol's properties and functions, it is an abstract class.

Requiring classes to be explicitly declared as abstract if it has one or more unimplemented functions or properties.

Detailed Design

Abstract functions

Functions can be declared abstract using the abstract keyword, which must appear before the func keyword in the declaration. Otherwise, the notation is identical to how the function would be declared if it were to appear in a protocol:

public abstract func retrieveText() -> String
As long as the abstract keyword appears before the func, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public func retrieveText() -> String
Abstract properties

Abstract property declarations are identical to what would be found in a protocol, but are prefixed with the abstractkeyword, which must appear first:

abstract var fileName: String { get }
abstract var favoriteColor: UIColor { get set }
As is typical with protocol declarations, var is always used and not let.

Abstract protocol conformance

A class can be made abstract by declaring conformance to a protocol that it does not implement fully.

For example, say you had a protocol Vehicle:

protocol Vehicle
{
    var name: String { get }
    var color: UIColor { get }
    var numberOfWheels: Int { get }
    var isParked: Bool { get set }

    func driveTo(destination: Location) throws
}
In your code, you're able to factor out everything except the driveTo() function, the implementation of which is vehicle-specific. The common code goes into a BaseVehicle class:

abstract class BaseVehicle: Vehicle
{
    let name: String
    let color: UIColor
    let numberOfWheels: Int
    var isParked: Bool

    init(name: String, color: UIColor, numberOfWheels: Int, isParked: Bool = true)
    {
        self.name = name
        self.color = color
        self.numberOfWheels = numberOfWheels
        self.isParked = isParked
    }
}
The BaseVehicle class partially conforms to the Vehicle protocol: the name, color, numberOfWheels and isParked properties are provided, but the driveTo() function remains unimplemented.

As a result, BaseVehicle is an abstract class and must be declared as such.

The abstract class declaration

A class must be declared as abstract if any of the following are true:

If the class declares one or more abstract functions or properties
If the class declares conformance to a protocol but does not supply implementations for every one of the functions and properties declared in that protocol.
If the class inherits from an abstract class and does not supply an implementation for every one of the unimplemented functions or properties.
Classes are marked as abstract by placing the abstract keyword before the class keyword at the top of the class declaration, eg.:

public abstract class MyAbstractClass
{
    // ...code...
}
As long as the abstract keyword appears before the class, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public class MyAbstractClass
{
    // ...code...
}
Instantiation

Because an abstract class is not a complete implementation, the compiler will not allow instantiation of abstract classes.

Attempting to instantiate an abstract class will result in a compiler error.

Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

Citations

This idea has been discussed in the following swift-evolution mailing list threads:

[Review] SE-0030 Property Behaviors

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

+1 I’m all for abstract classes.

While admittedly I need them much less regularly in Swift thanks to the way that protocols and extensions can be used, they are definitely still useful when dealing with class inheritance.

My only feedback is that I’m not sure about why you specifically mention that the keyword order isn’t important, I actually wonder whether it might be better to be stricter with the order for the sake of consistency? i.e- it must appear immediately before the class, func or var keyword. I dunno if we have a formal specification for keyword order in these places, but I prefer how public abstract class and similar read personally.

···

On 24 Feb 2016, at 22:36, Evan Maloney via swift-evolution <swift-evolution@swift.org> wrote:

After Thorsten reminded me yesterday that I wanted this feature too, I spent some time writing up proposal to allow declaration of abstract functions and properties on Swift classes:

https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c

This is the first draft; all comments welcome!

Thanks,
E.

---

Allow declaration of abstract functions and properties on classes

Proposal: SE-NNNN
Author: Evan Maloney <https://github.com/emaloney>
Status: Draft
Review manager: TBD
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#introduction>Introduction

Swift protocols are useful for the declaring interfaces that must be provided by conforming entities (in other words: structs, classes and enums). Adopters of a given protocol are free to provide any implementation they wish, so long as it supplies the necessary interfaces.

Separating interface from implementation is widely considered to be a best practice in software design, and the Swift protocol is designed for this use-case.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-problem>The Problem

Unfortunately, the protocol does not cover all of the cases where a developer might want to specify an interface to be implemented by another entity.

For example, consider the class, which allows the creation of an inheritance hierarchy. Often, a class in a hierarchy exists merely to provide a common implementation to subclasses. Such classes aren't ever intended to be instantiated directly; only subclasses will be instantiated.

To illustrate the point, imagine a view controller class that:

Places an animating UIActivityIndicatorView onscreen
Performs some operation to retrieve some text
Puts the text in a UITextView and places it onscreen
Hides the UIActivityIndicatorView
Now imagine you had many cases in your application where you could benefit from such a view controller, and each case differed only in the operation required to retrieve the text (represented by Step 2 above).

Ideally, you would be able to achieve this by declaring the interface for a function without needing to specify an implementation, the same way you would with a protocol:

func retrieveText() -> String
In other languages, such as C++, this concept exists in the form of an abstract class. However, Swift does not support this, so developers are forced to provide useless implementations such as:

func retrieveText() -> String
{
    fatalError("Subclasses must implement retrieveText()")
}
The idea here is that subclasses should always provide a retrieveText() implementation, and therefore the call to fatalError() should never be hit.

This has a few significant downsides:

It forces the developer to write code that should never be executed under normal conditions. This seems like a waste.

Because a default implementation is provided--the one that calls fatalError()--the compiler has no way of knowing that the subclasses are supposed to provide an implementation, too.

If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#proposed-solution>Proposed Solution

The proposed solution involves adding support for abstract classes to Swift.

This would entail:

Allowing functions and properties to be declared abstract. An abstract function or property declares the interface without specifying the implementation.

Allowing abstract classes to be defined by partially unimplemented protocol conformances. If a class declares conformance to a protocol without providing an implementation for each of that protocol's properties and functions, it is an abstract class.

Requiring classes to be explicitly declared as abstract if it has one or more unimplemented functions or properties.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#detailed-design>Detailed Design

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-functions>Abstract functions

Functions can be declared abstract using the abstract keyword, which must appear before the func keyword in the declaration. Otherwise, the notation is identical to how the function would be declared if it were to appear in a protocol:

public abstract func retrieveText() -> String
As long as the abstract keyword appears before the func, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public func retrieveText() -> String
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-properties>Abstract properties

Abstract property declarations are identical to what would be found in a protocol, but are prefixed with the abstractkeyword, which must appear first:

abstract var fileName: String { get }
abstract var favoriteColor: UIColor { get set }
As is typical with protocol declarations, var is always used and not let.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-protocol-conformance>Abstract protocol conformance

A class can be made abstract by declaring conformance to a protocol that it does not implement fully.

For example, say you had a protocol Vehicle:

protocol Vehicle
{
    var name: String { get }
    var color: UIColor { get }
    var numberOfWheels: Int { get }
    var isParked: Bool { get set }

    func driveTo(destination: Location) throws
}
In your code, you're able to factor out everything except the driveTo() function, the implementation of which is vehicle-specific. The common code goes into a BaseVehicle class:

abstract class BaseVehicle: Vehicle
{
    let name: String
    let color: UIColor
    let numberOfWheels: Int
    var isParked: Bool

    init(name: String, color: UIColor, numberOfWheels: Int, isParked: Bool = true)
    {
        self.name = name
        self.color = color
        self.numberOfWheels = numberOfWheels
        self.isParked = isParked
    }
}
The BaseVehicle class partially conforms to the Vehicle protocol: the name, color, numberOfWheels and isParked properties are provided, but the driveTo() function remains unimplemented.

As a result, BaseVehicle is an abstract class and must be declared as such.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-abstract-class-declaration>The abstract class declaration

A class must be declared as abstract if any of the following are true:

If the class declares one or more abstract functions or properties
If the class declares conformance to a protocol but does not supply implementations for every one of the functions and properties declared in that protocol.
If the class inherits from an abstract class and does not supply an implementation for every one of the unimplemented functions or properties.
Classes are marked as abstract by placing the abstract keyword before the class keyword at the top of the class declaration, eg.:

public abstract class MyAbstractClass
{
    // ...code...
}
As long as the abstract keyword appears before the class, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public class MyAbstractClass
{
    // ...code...
}
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#instantiation>Instantiation

Because an abstract class is not a complete implementation, the compiler will not allow instantiation of abstract classes.

Attempting to instantiate an abstract class will result in a compiler error.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#impact-on-existing-code>Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#citations>Citations

This idea has been discussed in the following swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution> mailing list threads:

[Review] SE-0030 Property Behaviors <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160222/010876.html>

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

• If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.

That's one alternative, yes. Others include:

1. Having a delegate provide the `retrieveText()` method.

I'm assuming the delegate will be a simple get/set property?

How can the compiler enforce that the developer supplies a delegate?

By declaring a function abstract, the compiler can enforce that the implementation is supplied. I don't see how you can do that with a delegate.

Using this solution, any bugs won't be caught until runtime.

2. Having a closure property implement the `retrieveText()` method.

My same objections above apply. You can make a closure property, but you can't guarantee that the developer supplies a value. So you end up with something like this:

var retrieveText: () -> String = {
    fatalError("You forgot to supply a retrieveText() closure!")
}

Again, this solution does not allow the compiler to enforce anything.

The beauty of the abstract class is that the compiler can catch these types of errors.

3. Declaring a protocol `ActivityViewControlling` that requires `retrieveText()` and adding the other logic in an `extension ActivityViewControlling where Self: UIViewController`.

Show me how to do that in a way that enforces the developer supply an implementation. I don't see how it can be done, and if it can, it's certainly going to be far more convoluted than an abstract class.

There's a time I'm thinking of "Properties on Default Protocol
Implementations
<http://thread.gmane.org/gmane.comp.lang.swift.evolution/2996>" But I not
yet reached a consistent design. I am waiting for some other improvements
get ready to propose something that really works in the long run.

However I believe that all problems related to abstract classes can rightly
be dealt with by improving current protocols.

···

Em qua, 24 de fev de 2016 às 19:43, Evan Maloney via swift-evolution < swift-evolution@swift.org> escreveu:

After Thorsten reminded me yesterday that I wanted this feature too, I
spent some time writing up proposal to allow declaration of abstract
functions and properties on Swift classes:

https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c

This is the first draft; all comments welcome!

Thanks,
E.

---

Allow declaration of abstract functions and properties on classes

   - Proposal: SE-NNNN
   - Author: Evan Maloney <https://github.com/emaloney>
   - Status: *Draft*
   - Review manager: TBD

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#introduction>
Introduction

Swift protocols are useful for the declaring interfaces that must be
provided by conforming entities (in other words: structs, classes and enums).
Adopters of a given protocol are free to provide any implementation they
wish, so long as it supplies the necessary interfaces.

Separating *interface* from *implementation* is widely considered to be a
best practice in software design, and the Swift protocol is designed for
this use-case.
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-problem>The
Problem

Unfortunately, the protocol does not cover all of the cases where a
developer might want to specify an interface to be implemented by another
entity.

For example, consider the class, which allows the creation of an
inheritance hierarchy. Often, a class in a hierarchy exists merely to
provide a common implementation to subclasses. Such classes aren't ever
intended to be instantiated directly; only subclasses will be instantiated.

To illustrate the point, imagine a view controller class that:

   1. Places an animating UIActivityIndicatorView onscreen
   2. Performs some operation to retrieve some text
   3. Puts the text in a UITextView and places it onscreen
   4. Hides the UIActivityIndicatorView

Now imagine you had many cases in your application where you could benefit
from such a view controller, and each case differed only in the operation
required to retrieve the text (represented by Step 2 above).

Ideally, you would be able to achieve this by declaring the interface for
a function without needing to specify an implementation, the same way you
would with a protocol:

func retrieveText() -> String

In other languages, such as C++, this concept exists in the form of an *abstract
class*. However, Swift does not support this, so developers are forced to
provide useless implementations such as:

func retrieveText() -> String
{
    fatalError("Subclasses must implement retrieveText()")
}

The idea here is that subclasses should always provide a retrieveText() implementation,
and therefore the call to fatalError() should never be hit.

This has a few significant downsides:

   1.

   It forces the developer to write code that should never be executed
   under normal conditions. This seems like a waste.
   2.

   Because a default implementation is provided--the one that calls
   fatalError()--the compiler has no way of knowing that the subclasses
   are supposed to provide an implementation, too.
   3.

   If a subclass implementor forgets to provide a retrieveText() function,
   the error will not be caught until runtime, and not until a user navigates
   to the affected portion of the application. This may not occur until the
   application has shipped.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#proposed-solution>Proposed
Solution

The proposed solution involves adding support for abstract classes to
Swift.

This would entail:

   -

   Allowing functions and properties to be declared abstract. An abstract
   function or property declares the interface without specifying the
   implementation.
   -

   Allowing abstract classes to be defined by partially unimplemented
   protocol conformances. If a class declares conformance to a protocol
   without providing an implementation for each of that protocol's properties
   and functions, it is an abstract class.
   -

   Requiring classes to be explicitly declared as abstract if it has one
   or more unimplemented functions or properties.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#detailed-design>Detailed
Design
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-functions>Abstract
functions

Functions can be declared abstract using the abstract keyword, which must
appear before the func keyword in the declaration. Otherwise, the
notation is identical to how the function would be declared if it were to
appear in a protocol:

public abstract func retrieveText() -> String

As long as the abstract keyword appears before the func, the order of
appearance of the abstract keyword relative to any public, private or
internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public func retrieveText() -> String

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-properties>Abstract
properties

Abstract property declarations are identical to what would be found in a
protocol, but are prefixed with the abstractkeyword, which must appear
first:

abstract var fileName: String { get }
abstract var favoriteColor: UIColor { get set }

As is typical with protocol declarations, var is always used and not let.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-protocol-conformance>Abstract
protocol conformance

A class can be made abstract by declaring conformance to a protocol that
it does not implement fully.

For example, say you had a protocol Vehicle:

protocol Vehicle
{
    var name: String { get }
    var color: UIColor { get }
    var numberOfWheels: Int { get }
    var isParked: Bool { get set }

    func driveTo(destination: Location) throws
}

In your code, you're able to factor out everything except the driveTo() function,
the implementation of which is vehicle-specific. The common code goes into
a BaseVehicle class:

abstract class BaseVehicle: Vehicle
{
    let name: String
    let color: UIColor
    let numberOfWheels: Int
    var isParked: Bool

    init(name: String, color: UIColor, numberOfWheels: Int, isParked: Bool = true)
    {
        self.name = name
        self.color = color
        self.numberOfWheels = numberOfWheels
        self.isParked = isParked
    }
}

The BaseVehicle class partially conforms to the Vehicle protocol: the name
, color, numberOfWheels and isParked properties are provided, but the
driveTo() function remains unimplemented.

As a result, BaseVehicle is an abstract class and must be declared as
such.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-abstract-class-declaration>
The abstract class declaration

A class must be declared as abstract if any of the following are true:

   1. If the class declares one or more abstract functions or properties
   2. If the class declares conformance to a protocol but does not supply
   implementations for every one of the functions and properties declared in
   that protocol.
   3. If the class inherits from an abstract class and does not supply an
   implementation for every one of the unimplemented functions or properties.

Classes are marked as abstract by placing the abstract keyword before the
class keyword at the top of the class declaration, eg.:

public abstract class MyAbstractClass
{
    // ...code...
}

As long as the abstract keyword appears before the class, the order of
appearance of the abstract keyword relative to any public, private or
internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public class MyAbstractClass
{
    // ...code...
}

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#instantiation>
Instantiation

Because an abstract class is not a complete implementation, the compiler
will not allow instantiation of abstract classes.

Attempting to instantiate an abstract class will result in a compiler
error.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#impact-on-existing-code>Impact
on Existing Code

None, since this does not affect any existing constructs. Implementation
of this proposal will not result in any code breakage.
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#citations>Citations

This idea has been discussed in the following swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution> mailing list
threads:

   - [Review] SE-0030 Property Behaviors
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160222/010876.html>

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

1 Like

+1 to Brent

···

Le 25 févr. 2016 à 02:27, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

Unfortunately, the protocol does not cover all of the cases where a developer might want to specify an interface to be implemented by another entity.

For example, consider the class, which allows the creation of an inheritance hierarchy. Often, a class in a hierarchy exists merely to provide a common implementation to subclasses. Such classes aren't ever intended to be instantiated directly; only subclasses will be instantiated.

To illustrate the point, imagine a view controller class that:

  • Places an animating UIActivityIndicatorView onscreen
  • Performs some operation to retrieve some text
  • Puts the text in a UITextView and places it onscreen
  • Hides the UIActivityIndicatorView
Now imagine you had many cases in your application where you could benefit from such a view controller, and each case differed only in the operation required to retrieve the text (represented by Step 2 above).

Ideally, you would be able to achieve this by declaring the interface for a function without needing to specify an implementation, the same way you would with a protocol:

func retrieveText() -> String
In other languages, such as C++, this concept exists in the form of an abstract class. However, Swift does not support this, so developers are forced to provide useless implementations such as:

func retrieveText() -> String

{
   fatalError(
"Subclasses must implement retrieveText()"
)
}

The idea here is that subclasses should always provide a retrieveText() implementation, and therefore the call to fatalError() should never be hit.

This has a few significant downsides:

  • It forces the developer to write code that should never be executed under normal conditions. This seems like a waste.

  • Because a default implementation is provided--the one that calls fatalError()--the compiler has no way of knowing that the subclasses are supposed to provide an implementation, too.

  • If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.

That's one alternative, yes. Others include:

1. Having a delegate provide the `retrieveText()` method.

2. Having a closure property implement the `retrieveText()` method.

3. Declaring a protocol `ActivityViewControlling` that requires `retrieveText()` and adding the other logic in an `extension ActivityViewControlling where Self: UIViewController`.

I think that 1 or 2 are usually the best way to handle something like this, but let's explore 3 for a minute, because that's a place where Swift could probably be improved.

Currently, Swift allows you to constrain a protocol to only class types:

  protocol ActivityViewControlling: class {
    func retrieveText() -> String
  }
  extension ActivityViewControlling where Self: UIViewController {
    ...
  }
  class MyActivityViewController: UIViewController, ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

But when you do that, Swift permits you to use any class type, which is a bit weird semantically—ActivityViewControlling can be applied to any class, but it's really only meant to be applied to subclasses of UIViewController. An ActivityViewControlling type which isn't a view controller is kind of meaningless.

  // Why can I do this?
  class PossibleButUseless: ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

Suppose instead we allow a protocol to require that the conforming class inherit from another class. We could then omit the `where` clause and possibly even make the inheritance itself implicit in conforming to the protocol:

  protocol ActivityViewControlling: UIViewController {
    func retrieveText() -> String
  }
  extension ActivityViewControlling {
    ...
  }
  class MyActivityViewController: ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

This would also relieve some of the pressure on us to support class-plus-protocol typed variables. In many of these cases, *all* types conforming to the protocol should be subclasses of a particular class, but there's no way to express that in the type system. Subclass-only protocols would correct that shortcoming.

Subclass-only protocols would cover most, if not all, of the use cases of abstract classes, but I think they would be a less dramatic change to Swift, because protocols are already types which can't be instantiated and impose requirements on their subtypes. I therefore believe they would be a better, more Swift-like approach to the problem abstract classes are trying to solve.

--
Brent Royal-Gordon
Architechies

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

I think there is one major use case that is not covered by your 3rd idea:
Protocols and protocol extensions are always the top level in an inheritance hierarchy and the methods implemented in protocol extensions are only used when neither the class conforming to it nor any superclass implements the method. In particular this means that you would not be able override viewDidLoad for classes conforming ActivityViewControlling and start the operation to retrieve the text from there, which – as I understood it – was Evan’s idea.
Because of the ability to override method’s in superclasses I think that abstract classes are a fundamentally different concept than protocols.

– Alex

···

On 25 Feb 2016, at 02:27, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

That's one alternative, yes. Others include:

1. Having a delegate provide the `retrieveText()` method.

2. Having a closure property implement the `retrieveText()` method.

3. Declaring a protocol `ActivityViewControlling` that requires `retrieveText()` and adding the other logic in an `extension ActivityViewControlling where Self: UIViewController`.

I think that 1 or 2 are usually the best way to handle something like this, but let's explore 3 for a minute, because that's a place where Swift could probably be improved.

Currently, Swift allows you to constrain a protocol to only class types:

  protocol ActivityViewControlling: class {
    func retrieveText() -> String
  }
  extension ActivityViewControlling where Self: UIViewController {
    ...
  }
  class MyActivityViewController: UIViewController, ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

But when you do that, Swift permits you to use any class type, which is a bit weird semantically—ActivityViewControlling can be applied to any class, but it's really only meant to be applied to subclasses of UIViewController. An ActivityViewControlling type which isn't a view controller is kind of meaningless.

  // Why can I do this?
  class PossibleButUseless: ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

Suppose instead we allow a protocol to require that the conforming class inherit from another class. We could then omit the `where` clause and possibly even make the inheritance itself implicit in conforming to the protocol:

  protocol ActivityViewControlling: UIViewController {
    func retrieveText() -> String
  }
  extension ActivityViewControlling {
    ...
  }
  class MyActivityViewController: ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

This would also relieve some of the pressure on us to support class-plus-protocol typed variables. In many of these cases, *all* types conforming to the protocol should be subclasses of a particular class, but there's no way to express that in the type system. Subclass-only protocols would correct that shortcoming.

Subclass-only protocols would cover most, if not all, of the use cases of abstract classes, but I think they would be a less dramatic change to Swift, because protocols are already types which can't be instantiated and impose requirements on their subtypes. I therefore believe they would be a better, more Swift-like approach to the problem abstract classes are trying to solve.

--
Brent Royal-Gordon
Architechies

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

I would like to see declarations of abstract associatedtype incorporated into the proposal.

I had a case some days ago where the combination abstract class + abstract associatedtype was exactly what I needed: I wanted to create an abstract subclass of NSOperation to parse NSData into some type that conforms to JSONDecodable.

The proposed syntax for this example would look like the following:

abstract class JsonParseOperation: NSOperation {
  abstract associatedtype DestinationType: JSONDecodable
}

Technically associatedtype (without the abstract keyword) would be sufficient since typealias was not renamed to associatedtype in classes (at least I think so), but I believe abstract provides a nice visual clue that this type has to be instantiated by subclasses and that the class has to be abstract.

– Alex

···

On 24 Feb 2016, at 23:36, Evan Maloney via swift-evolution <swift-evolution@swift.org> wrote:

After Thorsten reminded me yesterday that I wanted this feature too, I spent some time writing up proposal to allow declaration of abstract functions and properties on Swift classes:

https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c

This is the first draft; all comments welcome!

Thanks,
E.

---

Allow declaration of abstract functions and properties on classes

Proposal: SE-NNNN
Author: Evan Maloney <https://github.com/emaloney>
Status: Draft
Review manager: TBD
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#introduction>Introduction

Swift protocols are useful for the declaring interfaces that must be provided by conforming entities (in other words: structs, classes and enums). Adopters of a given protocol are free to provide any implementation they wish, so long as it supplies the necessary interfaces.

Separating interface from implementation is widely considered to be a best practice in software design, and the Swift protocol is designed for this use-case.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-problem>The Problem

Unfortunately, the protocol does not cover all of the cases where a developer might want to specify an interface to be implemented by another entity.

For example, consider the class, which allows the creation of an inheritance hierarchy. Often, a class in a hierarchy exists merely to provide a common implementation to subclasses. Such classes aren't ever intended to be instantiated directly; only subclasses will be instantiated.

To illustrate the point, imagine a view controller class that:

Places an animating UIActivityIndicatorView onscreen
Performs some operation to retrieve some text
Puts the text in a UITextView and places it onscreen
Hides the UIActivityIndicatorView
Now imagine you had many cases in your application where you could benefit from such a view controller, and each case differed only in the operation required to retrieve the text (represented by Step 2 above).

Ideally, you would be able to achieve this by declaring the interface for a function without needing to specify an implementation, the same way you would with a protocol:

func retrieveText() -> String
In other languages, such as C++, this concept exists in the form of an abstract class. However, Swift does not support this, so developers are forced to provide useless implementations such as:

func retrieveText() -> String
{
    fatalError("Subclasses must implement retrieveText()")
}
The idea here is that subclasses should always provide a retrieveText() implementation, and therefore the call to fatalError() should never be hit.

This has a few significant downsides:

It forces the developer to write code that should never be executed under normal conditions. This seems like a waste.

Because a default implementation is provided--the one that calls fatalError()--the compiler has no way of knowing that the subclasses are supposed to provide an implementation, too.

If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#proposed-solution>Proposed Solution

The proposed solution involves adding support for abstract classes to Swift.

This would entail:

Allowing functions and properties to be declared abstract. An abstract function or property declares the interface without specifying the implementation.

Allowing abstract classes to be defined by partially unimplemented protocol conformances. If a class declares conformance to a protocol without providing an implementation for each of that protocol's properties and functions, it is an abstract class.

Requiring classes to be explicitly declared as abstract if it has one or more unimplemented functions or properties.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#detailed-design>Detailed Design

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-functions>Abstract functions

Functions can be declared abstract using the abstract keyword, which must appear before the func keyword in the declaration. Otherwise, the notation is identical to how the function would be declared if it were to appear in a protocol:

public abstract func retrieveText() -> String
As long as the abstract keyword appears before the func, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public func retrieveText() -> String
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-properties>Abstract properties

Abstract property declarations are identical to what would be found in a protocol, but are prefixed with the abstractkeyword, which must appear first:

abstract var fileName: String { get }
abstract var favoriteColor: UIColor { get set }
As is typical with protocol declarations, var is always used and not let.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-protocol-conformance>Abstract protocol conformance

A class can be made abstract by declaring conformance to a protocol that it does not implement fully.

For example, say you had a protocol Vehicle:

protocol Vehicle
{
    var name: String { get }
    var color: UIColor { get }
    var numberOfWheels: Int { get }
    var isParked: Bool { get set }

    func driveTo(destination: Location) throws
}
In your code, you're able to factor out everything except the driveTo() function, the implementation of which is vehicle-specific. The common code goes into a BaseVehicle class:

abstract class BaseVehicle: Vehicle
{
    let name: String
    let color: UIColor
    let numberOfWheels: Int
    var isParked: Bool

    init(name: String, color: UIColor, numberOfWheels: Int, isParked: Bool = true)
    {
        self.name = name
        self.color = color
        self.numberOfWheels = numberOfWheels
        self.isParked = isParked
    }
}
The BaseVehicle class partially conforms to the Vehicle protocol: the name, color, numberOfWheels and isParked properties are provided, but the driveTo() function remains unimplemented.

As a result, BaseVehicle is an abstract class and must be declared as such.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-abstract-class-declaration>The abstract class declaration

A class must be declared as abstract if any of the following are true:

If the class declares one or more abstract functions or properties
If the class declares conformance to a protocol but does not supply implementations for every one of the functions and properties declared in that protocol.
If the class inherits from an abstract class and does not supply an implementation for every one of the unimplemented functions or properties.
Classes are marked as abstract by placing the abstract keyword before the class keyword at the top of the class declaration, eg.:

public abstract class MyAbstractClass
{
    // ...code...
}
As long as the abstract keyword appears before the class, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public class MyAbstractClass
{
    // ...code...
}
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#instantiation>Instantiation

Because an abstract class is not a complete implementation, the compiler will not allow instantiation of abstract classes.

Attempting to instantiate an abstract class will result in a compiler error.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#impact-on-existing-code>Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#citations>Citations

This idea has been discussed in the following swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution> mailing list threads:

[Review] SE-0030 Property Behaviors <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160222/010876.html>

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

I personally would rather Swift double-down on mixin/trait behavior and protocol-oriented programming rather than supporting abstract classes.

Exposing “Base” classes are an anti-pattern in my opinion (for reasons details more below my sig for TL;DR reasons)

Swift is a multi-paradigm type system with both traditional class inheritance and protocol-oriented programming w/ light trait support, but I would prefer features not needed for objc compatibility have parity between reference and value types, meaning that developer get tools to aid protocol implementation (usable by classes, structs and enums) rather than via class inheritance (unusable by structs and enums).

-DW

You can model all external interactions as being via explicit and implicit protocols. By this I mean -when you declare a class/struct/enum, you could consider the methods you declare to be defining a form of implicit protocol for usage.

For value types it is not possible for this implicit protocol to be leveraged in other contexts, but for classes it becomes possible for you to support this protocol by subclassing. At this point, you have gone from implicitly defining a protocol for usage, but also implicitly defining the behavior of this protocol based on the implementation of the class - the Liskov substitution principle in action.

You have several things fall out of having subclass relationships propitiate these implicit protocols:
- “missing super call” warnings/errors if the mechanisms in the super class are not being used, risking breaking the implicit contract.
- abstract methods (and thus abstract classes) when your use of classes is solely to define the implicit protocol interface and behavior, and not to define a working root class.
- protected-level access to define an interface to separate out the implicit public protocol from an implicit internal protocol for subclass customization of behavior
- required/convenience methods (most commonly initializers) to attempt to minimize the number of methods which need to be handled in subclasses to get new behavior, and/or define when subclass behavior may be invoked multiple times due to the base class calling the public interface

My own opinion is that a “robust” class design allowing inheritance is hard to create when using the semantics given to you by mainstream languages. This difficulty is specifically due to the number of implicit factors you are managing:
1. the API and underlying behavior for public usage
2. API and behavior for subclasses to implement
3. internal API for subclasses to use as part of their implementation
4. internal API for a class’s own private usage
5. internal API for other code within a library/framework
6. substitutability of operations acting between multiple instances of an object (a classic example is implementing equality)

···

On Feb 24, 2016, at 3:54 PM, Gwendal Roué via swift-evolution <swift-evolution@swift.org> wrote:

+1.

A previous discussion on the topic : https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005728.html

Gwendal

Le 24 févr. 2016 à 17:36, Evan Maloney via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

After Thorsten reminded me yesterday that I wanted this feature too, I spent some time writing up proposal to allow declaration of abstract functions and properties on Swift classes:

https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c

This is the first draft; all comments welcome!

Thanks,
E.

---

Allow declaration of abstract functions and properties on classes

Proposal: SE-NNNN
Author: Evan Maloney <https://github.com/emaloney>
Status: Draft
Review manager: TBD
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#introduction>Introduction

Swift protocols are useful for the declaring interfaces that must be provided by conforming entities (in other words: structs, classes and enums). Adopters of a given protocol are free to provide any implementation they wish, so long as it supplies the necessary interfaces.

Separating interface from implementation is widely considered to be a best practice in software design, and the Swift protocol is designed for this use-case.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-problem>The Problem

Unfortunately, the protocol does not cover all of the cases where a developer might want to specify an interface to be implemented by another entity.

For example, consider the class, which allows the creation of an inheritance hierarchy. Often, a class in a hierarchy exists merely to provide a common implementation to subclasses. Such classes aren't ever intended to be instantiated directly; only subclasses will be instantiated.

To illustrate the point, imagine a view controller class that:

Places an animating UIActivityIndicatorView onscreen
Performs some operation to retrieve some text
Puts the text in a UITextView and places it onscreen
Hides the UIActivityIndicatorView
Now imagine you had many cases in your application where you could benefit from such a view controller, and each case differed only in the operation required to retrieve the text (represented by Step 2 above).

Ideally, you would be able to achieve this by declaring the interface for a function without needing to specify an implementation, the same way you would with a protocol:

func retrieveText() -> String
In other languages, such as C++, this concept exists in the form of an abstract class. However, Swift does not support this, so developers are forced to provide useless implementations such as:

func retrieveText() -> String
{
    fatalError("Subclasses must implement retrieveText()")
}
The idea here is that subclasses should always provide a retrieveText() implementation, and therefore the call to fatalError() should never be hit.

This has a few significant downsides:

It forces the developer to write code that should never be executed under normal conditions. This seems like a waste.

Because a default implementation is provided--the one that calls fatalError()--the compiler has no way of knowing that the subclasses are supposed to provide an implementation, too.

If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#proposed-solution>Proposed Solution

The proposed solution involves adding support for abstract classes to Swift.

This would entail:

Allowing functions and properties to be declared abstract. An abstract function or property declares the interface without specifying the implementation.

Allowing abstract classes to be defined by partially unimplemented protocol conformances. If a class declares conformance to a protocol without providing an implementation for each of that protocol's properties and functions, it is an abstract class.

Requiring classes to be explicitly declared as abstract if it has one or more unimplemented functions or properties.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#detailed-design>Detailed Design

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-functions>Abstract functions

Functions can be declared abstract using the abstract keyword, which must appear before the func keyword in the declaration. Otherwise, the notation is identical to how the function would be declared if it were to appear in a protocol:

public abstract func retrieveText() -> String
As long as the abstract keyword appears before the func, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public func retrieveText() -> String
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-properties>Abstract properties

Abstract property declarations are identical to what would be found in a protocol, but are prefixed with the abstractkeyword, which must appear first:

abstract var fileName: String { get }
abstract var favoriteColor: UIColor { get set }
As is typical with protocol declarations, var is always used and not let.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-protocol-conformance>Abstract protocol conformance

A class can be made abstract by declaring conformance to a protocol that it does not implement fully.

For example, say you had a protocol Vehicle:

protocol Vehicle
{
    var name: String { get }
    var color: UIColor { get }
    var numberOfWheels: Int { get }
    var isParked: Bool { get set }

    func driveTo(destination: Location) throws
}
In your code, you're able to factor out everything except the driveTo() function, the implementation of which is vehicle-specific. The common code goes into a BaseVehicle class:

abstract class BaseVehicle: Vehicle
{
    let name: String
    let color: UIColor
    let numberOfWheels: Int
    var isParked: Bool

    init(name: String, color: UIColor, numberOfWheels: Int, isParked: Bool = true)
    {
        self.name = name
        self.color = color
        self.numberOfWheels = numberOfWheels
        self.isParked = isParked
    }
}
The BaseVehicle class partially conforms to the Vehicle protocol: the name, color, numberOfWheels and isParked properties are provided, but the driveTo() function remains unimplemented.

As a result, BaseVehicle is an abstract class and must be declared as such.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-abstract-class-declaration>The abstract class declaration

A class must be declared as abstract if any of the following are true:

If the class declares one or more abstract functions or properties
If the class declares conformance to a protocol but does not supply implementations for every one of the functions and properties declared in that protocol.
If the class inherits from an abstract class and does not supply an implementation for every one of the unimplemented functions or properties.
Classes are marked as abstract by placing the abstract keyword before the class keyword at the top of the class declaration, eg.:

public abstract class MyAbstractClass
{
    // ...code...
}
As long as the abstract keyword appears before the class, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public class MyAbstractClass
{
    // ...code...
}
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#instantiation>Instantiation

Because an abstract class is not a complete implementation, the compiler will not allow instantiation of abstract classes.

Attempting to instantiate an abstract class will result in a compiler error.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#impact-on-existing-code>Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#citations>Citations

This idea has been discussed in the following swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution> mailing list threads:

[Review] SE-0030 Property Behaviors <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160222/010876.html>

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

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

2 Likes

I concur. Protocols are a new tool that needs fostering, but not at all costs.

As Alex says, there's no `super` with protocols.

Besides, protocols are more complex that classes. As many other threads have shown here, the overloading of protocol methods is a complex topic, which requires a high-level understanding of whether a method declaration should enter a protocol definition, or a protocol extension. Surely the topic will improve, but it is likely to remain complex.

So please educate people using protocols, but don't say it is a universal simple panacea. If Swift is actually and actively a research language, it may not have to be a language only for researchers.

Gwendal

···

Le 25 févr. 2016 à 08:04, Alex Hoppen via swift-evolution <swift-evolution@swift.org> a écrit :

I think there is one major use case that is not covered by your 3rd idea:
Protocols and protocol extensions are always the top level in an inheritance hierarchy and the methods implemented in protocol extensions are only used when neither the class conforming to it nor any superclass implements the method. In particular this means that you would not be able override viewDidLoad for classes conforming ActivityViewControlling and start the operation to retrieve the text from there, which – as I understood it – was Evan’s idea.
Because of the ability to override method’s in superclasses I think that abstract classes are a fundamentally different concept than protocols.

– Alex

On 25 Feb 2016, at 02:27, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

That's one alternative, yes. Others include:

1. Having a delegate provide the `retrieveText()` method.

2. Having a closure property implement the `retrieveText()` method.

3. Declaring a protocol `ActivityViewControlling` that requires `retrieveText()` and adding the other logic in an `extension ActivityViewControlling where Self: UIViewController`.

I think that 1 or 2 are usually the best way to handle something like this, but let's explore 3 for a minute, because that's a place where Swift could probably be improved.

Currently, Swift allows you to constrain a protocol to only class types:

   protocol ActivityViewControlling: class {
       func retrieveText() -> String
   }
   extension ActivityViewControlling where Self: UIViewController {
       ...
   }
   class MyActivityViewController: UIViewController, ActivityViewControlling {
       func retrieveText() -> String { ... }
   }

But when you do that, Swift permits you to use any class type, which is a bit weird semantically—ActivityViewControlling can be applied to any class, but it's really only meant to be applied to subclasses of UIViewController. An ActivityViewControlling type which isn't a view controller is kind of meaningless.

   // Why can I do this?
   class PossibleButUseless: ActivityViewControlling {
       func retrieveText() -> String { ... }
   }

Suppose instead we allow a protocol to require that the conforming class inherit from another class. We could then omit the `where` clause and possibly even make the inheritance itself implicit in conforming to the protocol:

   protocol ActivityViewControlling: UIViewController {
       func retrieveText() -> String
   }
   extension ActivityViewControlling {
       ...
   }
   class MyActivityViewController: ActivityViewControlling {
       func retrieveText() -> String { ... }
   }

This would also relieve some of the pressure on us to support class-plus-protocol typed variables. In many of these cases, *all* types conforming to the protocol should be subclasses of a particular class, but there's no way to express that in the type system. Subclass-only protocols would correct that shortcoming.

Subclass-only protocols would cover most, if not all, of the use cases of abstract classes, but I think they would be a less dramatic change to Swift, because protocols are already types which can't be instantiated and impose requirements on their subtypes. I therefore believe they would be a better, more Swift-like approach to the problem abstract classes are trying to solve.

--
Brent Royal-Gordon
Architechies

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

I concur. Protocols are a new tool that needs fostering, but not at all costs.

As Alex says, there's no `super` with protocols.

Besides, protocols are more complex that classes. As many other threads have shown here, the overloading of protocol methods is a complex topic, which requires a high-level understanding of whether a method declaration should enter a protocol definition, or a protocol extension. Surely the topic will improve, but it is likely to remain complex.

So please educate people using protocols, but don't say it is a universal simple panacea. If Swift is actually and actively a research language, it may not have to be a language only for researchers.

Gwendal

···

Le 25 févr. 2016 à 08:04, Alex Hoppen via swift-evolution <swift-evolution@swift.org> a écrit :

I think there is one major use case that is not covered by your 3rd idea:
Protocols and protocol extensions are always the top level in an inheritance hierarchy and the methods implemented in protocol extensions are only used when neither the class conforming to it nor any superclass implements the method. In particular this means that you would not be able override viewDidLoad for classes conforming ActivityViewControlling and start the operation to retrieve the text from there, which – as I understood it – was Evan’s idea.
Because of the ability to override method’s in superclasses I think that abstract classes are a fundamentally different concept than protocols.

– Alex

On 25 Feb 2016, at 02:27, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

That's one alternative, yes. Others include:

1. Having a delegate provide the `retrieveText()` method.

2. Having a closure property implement the `retrieveText()` method.

3. Declaring a protocol `ActivityViewControlling` that requires `retrieveText()` and adding the other logic in an `extension ActivityViewControlling where Self: UIViewController`.

I think that 1 or 2 are usually the best way to handle something like this, but let's explore 3 for a minute, because that's a place where Swift could probably be improved.

Currently, Swift allows you to constrain a protocol to only class types:

  protocol ActivityViewControlling: class {
      func retrieveText() -> String
  }
  extension ActivityViewControlling where Self: UIViewController {
      ...
  }
  class MyActivityViewController: UIViewController, ActivityViewControlling {
      func retrieveText() -> String { ... }
  }

But when you do that, Swift permits you to use any class type, which is a bit weird semantically—ActivityViewControlling can be applied to any class, but it's really only meant to be applied to subclasses of UIViewController. An ActivityViewControlling type which isn't a view controller is kind of meaningless.

  // Why can I do this?
  class PossibleButUseless: ActivityViewControlling {
      func retrieveText() -> String { ... }
  }

Suppose instead we allow a protocol to require that the conforming class inherit from another class. We could then omit the `where` clause and possibly even make the inheritance itself implicit in conforming to the protocol:

  protocol ActivityViewControlling: UIViewController {
      func retrieveText() -> String
  }
  extension ActivityViewControlling {
      ...
  }
  class MyActivityViewController: ActivityViewControlling {
      func retrieveText() -> String { ... }
  }

This would also relieve some of the pressure on us to support class-plus-protocol typed variables. In many of these cases, *all* types conforming to the protocol should be subclasses of a particular class, but there's no way to express that in the type system. Subclass-only protocols would correct that shortcoming.

Subclass-only protocols would cover most, if not all, of the use cases of abstract classes, but I think they would be a less dramatic change to Swift, because protocols are already types which can't be instantiated and impose requirements on their subtypes. I therefore believe they would be a better, more Swift-like approach to the problem abstract classes are trying to solve.

--
Brent Royal-Gordon
Architechies

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

How can the compiler enforce that the developer supplies a delegate?

By making the delegate/closure property non-optional, the compiler can ensure that there is always a valid delegate/closure set. The property would have to be initialized, so presumably the initializer would have to take the delegate/closure as a parameter.

(If you're willing to accept a little less compile-time safety to get more convenience, the property could be either an implicitly unwrapped optional or be marked @deferred using the upcoming behaviors feature. That would, for instance, make it possible to set this up entirely in Interface Builder without making a subclass just to override the initializer.)

Show me how to do that in a way that enforces the developer supply an implementation. I don't see how it can be done, and if it can, it's certainly going to be far more convoluted than an abstract class.

I showed an outline of it a couple paragraphs down:

  protocol ActivityViewControlling: class {
    func retrieveText() -> String
  }
  extension ActivityViewControlling where Self: UIViewController {
    ...
  }
  class MyActivityViewController: UIViewController, ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

`MyActivityViewController` *must* implement `retrieveText()` if it wants to conform to `ActivityViewControlling`.

You're right that this is currently somewhat convoluted (and someone else mentioned that the extension members may not override the inherited ones, which is obviously another problem). But I think protocols are actually pretty close to what you want already, and we're better off extending them to cover abstract class use cases than extending classes.

···

--
Brent Royal-Gordon
Architechies

Hi Alex,

I like your idea and was having similar thoughts when I drafted my proposal.

Ultimately, I left it out of the draft because I'm noticing that the smaller and simpler the proposals are, the more likely they are to get a positive response.

If the proposal for abstract classes is accepted, I would definitely support (and would probably help write) a follow-on proposal adding this idea.

Evan

···

On Feb 25, 2016, at 10:21 AM, Alex Hoppen <alex@ateamer.de> wrote:

I would like to see declarations of abstract associatedtype incorporated into the proposal.

I had a case some days ago where the combination abstract class + abstract associatedtype was exactly what I needed: I wanted to create an abstract subclass of NSOperation to parse NSData into some type that conforms to JSONDecodable.

The proposed syntax for this example would look like the following:

abstract class JsonParseOperation: NSOperation {
  abstract associatedtype DestinationType: JSONDecodable
}

Technically associatedtype (without the abstract keyword) would be sufficient since typealias was not renamed to associatedtype in classes (at least I think so), but I believe abstract provides a nice visual clue that this type has to be instantiated by subclasses and that the class has to be abstract.

– Alex

On 24 Feb 2016, at 23:36, Evan Maloney via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

After Thorsten reminded me yesterday that I wanted this feature too, I spent some time writing up proposal to allow declaration of abstract functions and properties on Swift classes:

https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c

This is the first draft; all comments welcome!

Thanks,
E.

---

Allow declaration of abstract functions and properties on classes

Proposal: SE-NNNN
Author: Evan Maloney <https://github.com/emaloney>
Status: Draft
Review manager: TBD
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#introduction>Introduction

Swift protocols are useful for the declaring interfaces that must be provided by conforming entities (in other words: structs, classes and enums). Adopters of a given protocol are free to provide any implementation they wish, so long as it supplies the necessary interfaces.

Separating interface from implementation is widely considered to be a best practice in software design, and the Swift protocol is designed for this use-case.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-problem>The Problem

Unfortunately, the protocol does not cover all of the cases where a developer might want to specify an interface to be implemented by another entity.

For example, consider the class, which allows the creation of an inheritance hierarchy. Often, a class in a hierarchy exists merely to provide a common implementation to subclasses. Such classes aren't ever intended to be instantiated directly; only subclasses will be instantiated.

To illustrate the point, imagine a view controller class that:

Places an animating UIActivityIndicatorView onscreen
Performs some operation to retrieve some text
Puts the text in a UITextView and places it onscreen
Hides the UIActivityIndicatorView
Now imagine you had many cases in your application where you could benefit from such a view controller, and each case differed only in the operation required to retrieve the text (represented by Step 2 above).

Ideally, you would be able to achieve this by declaring the interface for a function without needing to specify an implementation, the same way you would with a protocol:

func retrieveText() -> String
In other languages, such as C++, this concept exists in the form of an abstract class. However, Swift does not support this, so developers are forced to provide useless implementations such as:

func retrieveText() -> String
{
    fatalError("Subclasses must implement retrieveText()")
}
The idea here is that subclasses should always provide a retrieveText() implementation, and therefore the call to fatalError() should never be hit.

This has a few significant downsides:

It forces the developer to write code that should never be executed under normal conditions. This seems like a waste.

Because a default implementation is provided--the one that calls fatalError()--the compiler has no way of knowing that the subclasses are supposed to provide an implementation, too.

If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#proposed-solution>Proposed Solution

The proposed solution involves adding support for abstract classes to Swift.

This would entail:

Allowing functions and properties to be declared abstract. An abstract function or property declares the interface without specifying the implementation.

Allowing abstract classes to be defined by partially unimplemented protocol conformances. If a class declares conformance to a protocol without providing an implementation for each of that protocol's properties and functions, it is an abstract class.

Requiring classes to be explicitly declared as abstract if it has one or more unimplemented functions or properties.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#detailed-design>Detailed Design

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-functions>Abstract functions

Functions can be declared abstract using the abstract keyword, which must appear before the func keyword in the declaration. Otherwise, the notation is identical to how the function would be declared if it were to appear in a protocol:

public abstract func retrieveText() -> String
As long as the abstract keyword appears before the func, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public func retrieveText() -> String
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-properties>Abstract properties

Abstract property declarations are identical to what would be found in a protocol, but are prefixed with the abstractkeyword, which must appear first:

abstract var fileName: String { get }
abstract var favoriteColor: UIColor { get set }
As is typical with protocol declarations, var is always used and not let.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-protocol-conformance>Abstract protocol conformance

A class can be made abstract by declaring conformance to a protocol that it does not implement fully.

For example, say you had a protocol Vehicle:

protocol Vehicle
{
    var name: String { get }
    var color: UIColor { get }
    var numberOfWheels: Int { get }
    var isParked: Bool { get set }

    func driveTo(destination: Location) throws
}
In your code, you're able to factor out everything except the driveTo() function, the implementation of which is vehicle-specific. The common code goes into a BaseVehicle class:

abstract class BaseVehicle: Vehicle
{
    let name: String
    let color: UIColor
    let numberOfWheels: Int
    var isParked: Bool

    init(name: String, color: UIColor, numberOfWheels: Int, isParked: Bool = true)
    {
        self.name = name
        self.color = color
        self.numberOfWheels = numberOfWheels
        self.isParked = isParked
    }
}
The BaseVehicle class partially conforms to the Vehicle protocol: the name, color, numberOfWheels and isParked properties are provided, but the driveTo() function remains unimplemented.

As a result, BaseVehicle is an abstract class and must be declared as such.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-abstract-class-declaration>The abstract class declaration

A class must be declared as abstract if any of the following are true:

If the class declares one or more abstract functions or properties
If the class declares conformance to a protocol but does not supply implementations for every one of the functions and properties declared in that protocol.
If the class inherits from an abstract class and does not supply an implementation for every one of the unimplemented functions or properties.
Classes are marked as abstract by placing the abstract keyword before the class keyword at the top of the class declaration, eg.:

public abstract class MyAbstractClass
{
    // ...code...
}
As long as the abstract keyword appears before the class, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.

The following declaration is equivalent to the one above:

abstract public class MyAbstractClass
{
    // ...code...
}
<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#instantiation>Instantiation

Because an abstract class is not a complete implementation, the compiler will not allow instantiation of abstract classes.

Attempting to instantiate an abstract class will result in a compiler error.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#impact-on-existing-code>Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

<https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#citations>Citations

This idea has been discussed in the following swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution> mailing list threads:

[Review] SE-0030 Property Behaviors <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160222/010876.html>

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

How can the compiler enforce that the developer supplies a delegate?

By making the delegate/closure property non-optional, the compiler can ensure that there is always a valid delegate/closure set. The property would have to be initialized, so presumably the initializer would have to take the delegate/closure as a parameter.

That way would work if you forced every initializer in the class to accept a parameter representing each and every function you want to declare abstract. And it just gets messier from there, since if you wanted to do the same for get/set abstract properties, you'd need to model both the getter and setter separately, so for those you have 2 parameters.

If you have 2 abstract functions and 3 abstract get/set properties you want to model, you've just added 8 new parameters that you must pass to each one of your class's initializers.

(If you're willing to accept a little less compile-time safety to get more convenience, the property could be either an implicitly unwrapped optional or be marked @deferred using the upcoming behaviors feature. That would, for instance, make it possible to set this up entirely in Interface Builder without making a subclass just to override the initializer.)

Once you go with an implicitly unwrapped optional, you don't get less compile-time safety, you get none. All bugs are surfaced at runtime.

It's one of the reasons we don't use Interface Builder with our Swift codebase. IB is an end-run around many of the compile-time safety features that make Swift great.

Show me how to do that in a way that enforces the developer supply an implementation. I don't see how it can be done, and if it can, it's certainly going to be far more convoluted than an abstract class.

I showed an outline of it a couple paragraphs down:

Sorry, you did; my response was a bit imprecise because I failed to describe the exact scenario I was talking about.

  protocol ActivityViewControlling: class {
    func retrieveText() -> String
  }
  extension ActivityViewControlling where Self: UIViewController {
    ...
  }
  class MyActivityViewController: UIViewController, ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

Obviously, by explicitly stating conformance to a protocol, you must provide an implementation of whatever is declared in that protocol. As long as you remember to explicitly declare conformance to that protocol. What I'm looking for adds safety by removing the requirement that the developer remember to explicitly declare conformance to a protocol and then supply an implementation.

Now that I think about it, what I'm talking about is pretty much the same behavior as the 'required' keyword with classes, but for things that aren't initializers.

What I want is:

- By virtue of a class being an abstract class of type Foo, you (or a superclass that descends from Foo) must supply an implementation of the abstract interface

Protocol-based solutions give you:

- By virtue of something being a class, if you explicitly conform to protocol Foo you must supply declarations for that protocol's interface

The other problem with the protocol-based solution is that it leads to conceptual leakage. The protocol you need to declare lives outside the class(es) that need it.

The ActivityViewControlling protocol is now available universally, even though it's interface is only relevant to a certain portion the class hierarchy.

You're right that this is currently somewhat convoluted (and someone else mentioned that the extension members may not override the inherited ones, which is obviously another problem). But I think protocols are actually pretty close to what you want already, and we're better off extending them to cover abstract class use cases than extending classes.

All the gymnastics above could be solved with a simple keyword.

Don't get me wrong, I love protocols. When I saw Dave Abraham's Protocol-Oriented Programming talk, it was an eye-opening experience, like when I finally understood C++ after coming from C.

For anyone who hasn't watched it, here it is:

https://developer.apple.com/videos/play/wwdc2015/408/

But the fact is, classes exist in Swift, too. And some of the solutions classes require might be orthogonal from protocols, which solve a different problem.

In this particular case, I think trying to use protocols for something that's inherently tied to class inheritance leads to more convoluted, more confusing, and less clear code. It's a square peg/round hole problem.

I think the fundamental question is: do we see classes as first-class citizens in Swift? Or were classes merely added to placate existing Objective-C developers and/or ensure interoperability with legacy code?

If classes are first-class citizens, then I think a class-based solution to a class-only use-case is warranted.

···

On Feb 25, 2016, at 4:03 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

You're right that this is currently somewhat convoluted (and someone else mentioned that the extension members may not override the inherited ones, which is obviously another problem). But I think protocols are actually pretty close to what you want already, and we're better off extending them to cover abstract class use cases than extending classes.

I am curious to see a way that protocols can override existing methods, since the protocols are always at the top of the inheritance chain and putting them anywhere else makes them abstract classes by definition.

– Alex

That way would work if you forced every initializer in the class to accept a parameter representing each and every function you want to declare abstract. And it just gets messier from there, since if you wanted to do the same for get/set abstract properties, you'd need to model both the getter and setter separately, so for those you have 2 parameters.

If you have 2 abstract functions and 3 abstract get/set properties you want to model, you've just added 8 new parameters that you must pass to each one of your class's initializers.

Of course, which is why if you had eight override points, you would put them in one delegate protocol and pass in a single delegate instead. Your example had one override point, so it would be reasonable to use one closure, but even two override points would tilt the scales heavily towards using a delegate, and eight would be right out.

It's one of the reasons we don't use Interface Builder with our Swift codebase. IB is an end-run around many of the compile-time safety features that make Swift great.

If you're not using IB, then there's no reason not to use a custom initializer that takes the delegate as a parameter, and you therefore would not need to worry about the delegate being uninitialized.

Now that I think about it, what I'm talking about is pretty much the same behavior as the 'required' keyword with classes, but for things that aren't initializers.

The `required` keyword is something quite different—it states that all subtypes, at any degree of separation, must include this member. It only makes sense for initializers, because all other members are implicitly inherited and therefore subtypes always include them.

In other words, *all* members in Swift are required except for initializers without the `required` keyword, so the `required` keyword doesn't really make any sense here.

The other problem with the protocol-based solution is that it leads to conceptual leakage. The protocol you need to declare lives outside the class(es) that need it.

The ActivityViewControlling protocol is now available universally, even though it's interface is only relevant to a certain portion the class hierarchy.

I'm not sure I understand what you mean, but I *think* you're complaining that you can apply ActivityViewControlling to something that's not a UIViewController. I do agree that's a problem, which is why I suggested we should allow protocols to require a particular superclass:

  protocol ActivityViewControlling: UIViewController {
    func retrieveText() -> String
  }
  extension ActivityViewControlling {
    ...
  }
  class MyActivityViewController: ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

The requirement would not constrain conforming types quite as tightly as an abstract class would—it would allow you to conform, say, a UITableViewController subclass to ActivityViewControlling. However, there are almost certainly cases where that would be a benefit, not a detriment, so that might be a net win even if it's not quite as good for this particular use case.

···

--
Brent Royal-Gordon
Architechies

Well not exactly, if you want the same behaviors in subclasses of UIViewController and UITableViewController :
- with protocols + extensions, you write in once and apply it to each of your subclasses
- with abstract classes you have to write 2 abstract classes, one for direct UIViewController subclasses, one for UITableViewController subclasses

···

Le 26 févr. 2016 à 12:07, Alex Hoppen via swift-evolution <swift-evolution@swift.org> a écrit :

You're right that this is currently somewhat convoluted (and someone else mentioned that the extension members may not override the inherited ones, which is obviously another problem). But I think protocols are actually pretty close to what you want already, and we're better off extending them to cover abstract class use cases than extending classes.

I am curious to see a way that protocols can override existing methods, since the protocols are always at the top of the inheritance chain and putting them anywhere else makes them abstract classes by definition.

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

Well not exactly, if you want the same behaviors in subclasses of UIViewController and UITableViewController :
- with protocols + extensions, you write in once and apply it to each of your subclasses
- with abstract classes you have to write 2 abstract classes, one for direct UIViewController subclasses, one for UITableViewController subclasses

That's a problem with class hierarchies in general, not with abstract classes.

You can use the same argument to call for the removal of classes from Swift, which is why I think the fundamental question is, are classes intended to be first-class citizens in Swift?

A quick remark:

Although it is definitely a *work-around*, you can at present use the following technique:

protocol ActivityViewControlling {

  // the name doesn’t have to be this long, but you may want to avoid
  // accidental naming collisions:
  func viewControllerForActivityViewControlling() -> UIViewController

  func retrieveText() -> String
   
}

extension ActivityViewControlling where Self:UIViewController {

  func viewControllerForActivityViewControlling() -> UIViewController {
   return self
  }

}

…as a clunky but functional replacement for the capability to declare `protocol ActivityViewControlling: UIViewController`.

I’d still like the ability to constrain protocols in that way, but the above makes it harder to “accidentally, uselessly conform”.

···

On Feb 25, 2016, at 7:48 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

That way would work if you forced every initializer in the class to accept a parameter representing each and every function you want to declare abstract. And it just gets messier from there, since if you wanted to do the same for get/set abstract properties, you'd need to model both the getter and setter separately, so for those you have 2 parameters.

If you have 2 abstract functions and 3 abstract get/set properties you want to model, you've just added 8 new parameters that you must pass to each one of your class's initializers.

Of course, which is why if you had eight override points, you would put them in one delegate protocol and pass in a single delegate instead. Your example had one override point, so it would be reasonable to use one closure, but even two override points would tilt the scales heavily towards using a delegate, and eight would be right out.

It's one of the reasons we don't use Interface Builder with our Swift codebase. IB is an end-run around many of the compile-time safety features that make Swift great.

If you're not using IB, then there's no reason not to use a custom initializer that takes the delegate as a parameter, and you therefore would not need to worry about the delegate being uninitialized.

Now that I think about it, what I'm talking about is pretty much the same behavior as the 'required' keyword with classes, but for things that aren't initializers.

The `required` keyword is something quite different—it states that all subtypes, at any degree of separation, must include this member. It only makes sense for initializers, because all other members are implicitly inherited and therefore subtypes always include them.

In other words, *all* members in Swift are required except for initializers without the `required` keyword, so the `required` keyword doesn't really make any sense here.

The other problem with the protocol-based solution is that it leads to conceptual leakage. The protocol you need to declare lives outside the class(es) that need it.

The ActivityViewControlling protocol is now available universally, even though it's interface is only relevant to a certain portion the class hierarchy.

I'm not sure I understand what you mean, but I *think* you're complaining that you can apply ActivityViewControlling to something that's not a UIViewController. I do agree that's a problem, which is why I suggested we should allow protocols to require a particular superclass:

  protocol ActivityViewControlling: UIViewController {
    func retrieveText() -> String
  }
  extension ActivityViewControlling {
    ...
  }
  class MyActivityViewController: ActivityViewControlling {
    func retrieveText() -> String { ... }
  }

The requirement would not constrain conforming types quite as tightly as an abstract class would—it would allow you to conform, say, a UITableViewController subclass to ActivityViewControlling. However, there are almost certainly cases where that would be a benefit, not a detriment, so that might be a net win even if it's not quite as good for this particular use case.

--
Brent Royal-Gordon
Architechies

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