[Review] SE-0026 Abstract classes and methods

What is your evaluation of the proposal?

I am in favor of the proposal. However the “Detailed design” section could use some more clarifications:

1) Eg are abstract let’s allowed?

abstract let: String

2) what about abstract class and static methods?

3) should it be possible to import existing ObjC classes which are effectively abstract classes as true abstract classes? Eg:

- add an attribute on the ObjC side which would make it possible to mark classes like NSObject, NSResponder, NSGestureRecognizer, etc as abstract classes. This attribute would have no semantic significance in ObjC.

- those classes would come across into Swift as “abstract class”

This would make it possible to mark classes in the OS frameworks which are effectively abstract classes as such. This would be another stepping stone towards making the use of the existing Cocoa APIs more Swift-like, safer and less error-prone while preserving binary backward-compatibility to the existing ObjC implementation.

4) how will abstract classes which are marked with the @objc attribute or which derive directly or indirectly from NSObject be treated from the ObjC-side?

@objc abstract class Foo {
}

how does the ObjC side look? Would this be exported as just a regular ObjC class? Would it be marked with an attribute as mentioned in the previous bullet point?

Is the problem being addressed significant enough to warrant a change to Swift?

Yes, in fact Swift is hit harder by the lack of abstract classes compared to other languages which have support for abstract classes. The reason why abstract classes make especially sense for Swift is because of:

1) exhaustive switch-case checks for enum values

2) type-based separation of nullable and non-nullable values

It is often nontrivial and may in fact sometimes be impossible to provide a compilable and type correct null-implementation of a method that should be an abstract method but which can not be marked abstract because of the existing lack of abstract classes in Swift.

In the case of (1) a workaround may be to introduced by adding an extra enum case which only exists to enable us to provide a compile-able null-implementation. But by adding the extra case combined with the exhaustiveness check that Swift does for switch-case blocks, we end up spilling an implementation detail into the API of our class which should actually be kept private. Consider the following example class hierarchy which is used by a media app to describe different types of media:

enum MediaType {
case Photo
case LivePhoto
case Video
}

class MediaItem {

var title: String = “”
var type: MediaType = ???
}

class PhotoItem : MediaItem {


}

class VideoItem : MediaItem {


}

How are we going to implement MediaItem.type without abstract methods? We are either forced to introduce a special enum case just to make the code compilable or we are forced to return one of the existing enum cases which is conceptually wrong - the media item is an abstract, unbiased representation of a media item. It is neither a photo, live photo nor a video. If we introduce an extra enum case then every caller of the MediaItem.type property which does a switch-case over the return value will have to handle this extra case.

Let’s revisit the example from the proposal and lets change the property’s type to NSURL to see the problem with (2):

var url : NSURL {

  assert(false,"Must be overridden”)
  return NSURL(fileURLWithPath: “/ignore/this”)
}

We are somewhat “lucky” in this case that we can return a nonsensical URL. It’s bad enough that we have to do this (write nonsensical code just to work around a missing language feature). But things would be even worse if there is no simple initializer available for the struct or object that we want to return. We may be forced to change the type of the property to an optional so that we can return nil instead of a concrete value. This however would change the semantics of the API and is not acceptable in every case.

Does this proposal fit well with the feel and direction of Swift?

Yes. Swift supports classes. Abstract classes are an established and simple way to describe a partially implemented class and no better solution has been presented on this mailing list that would improve on the concept of abstract classes while not adding additional burden, complexity and potential confusion for the language user.

One recurring suggestion has been to use protocols as a replacement for abstract classes and to extend them with eg support for state so that they can be used as a replacement for abstract classes. None of the presented protocol-based solutions have been convincing.

Abstract classes support a number of important properties:

- they can be derived from concrete classes
- they can be derived from other abstract classes
- they support inheritance
- they support interface
- they support implementation
- they support state

Protocols, as they stand today in Swift support:

- inheritance
- interface
- implementation

but they can not be derived from classes nor do they support state. Additionally, Swift protocols have a number of design problems and are already a major source of confusion for language users. Eg the discrepancy in dispatch behavior between methods defined in the protocol proper and a protocol extension has been cited numerous times as a very confusing and surprising aspect of Swift protocols. A number of other examples of problems can be found in the thread "[Completing Generics] Completing protocol extension diagnostics”.

Finally, adding state to protocols. Once we add state to protocols, we have moved protocols so close to the semantics of classes that we have effectively turned them into a second kind of class. Protocols are already confusing enough as they are today since they already cover a number of different aspects and we should not end up in a situation where protocols will be turned into a kitchen-sink that is forced to suck up everyone’s personal favorite language feature.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Compares well to Java and C++.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I’ve read the proposal and the related discussion thread. I’ve been using languages with and without abstract classes.

Regards,

Dietmar Planitzer

var url : NSURL {

assert(false,"Must be overridden”)
return NSURL(fileURLWithPath: “/ignore/this”)
}

For what it's worth, using `fatalError()` or `preconditionFailure()` will allow you to write a version of this with no return statement, because they are annotated with `@noreturn`.

···

--
Brent Royal-Gordon
Architechies

What is your evaluation of the proposal?

I am in favor of the proposal. However the “Detailed design” section could use some more clarifications:

1) Eg are abstract let’s allowed?

abstract let: String

  Yes, as they are for var.

2) what about abstract class and static methods?

  static are not part of inheritance..then there is not abstract static method.

3) should it be possible to import existing ObjC classes which are effectively abstract classes as true abstract classes? Eg:

  No, because we should not modify behavior of ObjC language when importing into Swift.

4) how will abstract classes which are marked with the @objc attribute or which derive directly or indirectly from NSObject be treated from the ObjC-side?

@objc abstract class Foo {
}

  I suggest that abstract is a Swift-only keyword…then @objc is not allowed to abstract class.

how does the ObjC side look? Would this be exported as just a regular ObjC class? Would it be marked with an attribute as mentioned in the previous bullet point?

  Abstract classes are not exported to ObjC.

  Regards,

David

···

Le 4 mars 2016 à 05:26, Dietmar Planitzer via swift-evolution <swift-evolution@swift.org> a écrit :

So, having considered this for a while, although I was initially in favour of it, as my background is largely in Java where I used class hierarchies a lot, some work I’ve been doing in Swift that has actually helped me to appreciate protocols and their capabilities a lot more.

I think with this in mind I’m actually now leaning more towards support for mixins plus some kind of implementing type only visibility. Mixins would be a new category of protocol that can include stored properties and overridable/extendable implementation details; my reason for preferring these is that they could be used with structs as well as classes, though I would actually prefer a different name (in fact I’d prefer to think of them as abstract types that are neither a struct or class… yet). Inheriting type only visibility is of course useful for providing partial implementations that a type can/should use, without exposing it as a callable method; I really dislike the idea of using precondition failures or other errors to indicate methods that should not be called directly, as it’s hacky at best, so I think the visibility restriction would provide a much better alternative.

Someone asked in this discussion whether classes are second-class citizens in Swift, and thinking about it some more I kind of think that they are; after a period of adjustment I now use structs for almost everything. I only use classes where a common reference is absolutely necessary, and also for nested types. Nesting and polymorphism can actually both be replaced by type-erased wrappers, though for nesting this may be more work than it’s worth if you’re trying to nest a private type, but it does mean that classes are actually a lot less important in Swift than other languages.

It seems to me that you are using classes and structs in exactly the right way. However, I don't think your judicious use of classes is necessarily an argument that "classes are second-class citizens in Swift".

Sometimes classes are the right tool for the job. Sometimes structs or enums are.

What I love about Swift is that it's often much easier to use the right tool for the right job than other languages. However, if classes are relegated to some sort of demoted status, over time, that would no longer be the case.

···

On Mar 4, 2016, at 5:42 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

Someone asked in this discussion whether classes are second-class citizens in Swift, and thinking about it some more I kind of think that they are; after a period of adjustment I now use structs for almost everything. I only use classes where a common reference is absolutely necessary, and also for nested types.

I promote protocols, but I wouldn't want to demote classes.

···

on Wed Mar 09 2016, Evan Maloney <swift-evolution@swift.org> wrote:

On Mar 4, 2016, at 5:42 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

Someone asked in this discussion whether classes are second-class
citizens in Swift, and thinking about it some more I kind of think
that they are; after a period of adjustment I now use structs for
almost everything. I only use classes where a common reference is
absolutely necessary, and also for nested types.

It seems to me that you are using classes and structs in exactly the
right way. However, I don't think your judicious use of classes is
necessarily an argument that "classes are second-class citizens in
Swift".

Sometimes classes are the right tool for the job. Sometimes structs or enums are.

What I love about Swift is that it's often much easier to use the
right tool for the right job than other languages. However, if classes
are relegated to some sort of demoted status, over time, that would no
longer be the case.

--
-Dave

+1. Swift loves OOP as well as POP (not to mention FP). They are both complementary techniques which are great at solving certain kinds of problems.

-Chris

···

On Mar 9, 2016, at 12:35 PM, Evan Maloney via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 4, 2016, at 5:42 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

Someone asked in this discussion whether classes are second-class citizens in Swift, and thinking about it some more I kind of think that they are; after a period of adjustment I now use structs for almost everything. I only use classes where a common reference is absolutely necessary, and also for nested types.

It seems to me that you are using classes and structs in exactly the right way. However, I don't think your judicious use of classes is necessarily an argument that "classes are second-class citizens in Swift".

Sometimes classes are the right tool for the job. Sometimes structs or enums are.

What I love about Swift is that it's often much easier to use the right tool for the right job than other languages. However, if classes are relegated to some sort of demoted status, over time, that would no longer be the case.

Well, I may also be on the verge of changing my mind once again; I recently implemented an AnyFoo<T> type, however the boxing is a nightmare without support for abstract classes. Assuming I’m still doing it the right way, boxing a value for type erasure requires a base class FooBoxBase<T>, and a sub-class FooBox<F:Foo> (as FooBox is also of type FooBoxBase<T>, you don’t need to know what type F is within AnyFoo<T>). However, the type I’m boxing has a lot of methods, which means a huge amount of boiler-plate in FooBoxBase that just contains fatal errors, that in turn have to be overridden in FooBox.

With an abstract class this boiler-plate not only wouldn’t be necessary, but would also avoid mistakes such as my forgetting to override a method in FooBox (resulting in the fatal error super method being called instead).

Anyway, while I’m generally a convert to doing everything via protocols first, I think there are still cases where abstract is the best way, as mixins and similar can't solve the boxing problem either. Of course we could get some syntactic sugar to make boxing easier (since it’s a very useful thing to do) but there may be similar cases where it just isn’t possible to do cleanly without proper abstract support.

P.S- I reserve the right to change my mind again in future =)

···

On 9 Mar 2016, at 20:35, Evan Maloney <emaloney@gilt.com> wrote:

On Mar 4, 2016, at 5:42 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

Someone asked in this discussion whether classes are second-class citizens in Swift, and thinking about it some more I kind of think that they are; after a period of adjustment I now use structs for almost everything. I only use classes where a common reference is absolutely necessary, and also for nested types.

It seems to me that you are using classes and structs in exactly the right way. However, I don't think your judicious use of classes is necessarily an argument that "classes are second-class citizens in Swift".

Sometimes classes are the right tool for the job. Sometimes structs or enums are.

What I love about Swift is that it's often much easier to use the right tool for the right job than other languages. However, if classes are relegated to some sort of demoted status, over time, that would no longer be the case.

Swift is the Hannah Montana of programming languages.
https://www.youtube.com/watch?v=uVjRe8QXFHY <https://www.youtube.com/watch?v=uVjRe8QXFHY&gt;

-- E, a mommy

···

On Mar 9, 2016, at 8:46 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 9, 2016, at 12:35 PM, Evan Maloney via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 4, 2016, at 5:42 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

Someone asked in this discussion whether classes are second-class citizens in Swift, and thinking about it some more I kind of think that they are; after a period of adjustment I now use structs for almost everything. I only use classes where a common reference is absolutely necessary, and also for nested types.

It seems to me that you are using classes and structs in exactly the right way. However, I don't think your judicious use of classes is necessarily an argument that "classes are second-class citizens in Swift".

Sometimes classes are the right tool for the job. Sometimes structs or enums are.

What I love about Swift is that it's often much easier to use the right tool for the right job than other languages. However, if classes are relegated to some sort of demoted status, over time, that would no longer be the case.

+1. Swift loves OOP as well as POP (not to mention FP). They are both complementary techniques which are great at solving certain kinds of problems.

-Chris

At least it isn't the Miley Cyrus:

“Miley Cyrus - Wrecking Ball (Explicit Video)”

  -- Howard.

···

On 10 March 2016 at 14:50, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

On Mar 9, 2016, at 8:46 PM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

On Mar 9, 2016, at 12:35 PM, Evan Maloney via swift-evolution < > swift-evolution@swift.org> wrote:

On Mar 4, 2016, at 5:42 AM, Haravikk via swift-evolution < > swift-evolution@swift.org> wrote:

Someone asked in this discussion whether classes are second-class citizens
in Swift, and thinking about it some more I kind of think that they are;
after a period of adjustment I now use structs for almost everything. I
only use classes where a common reference is absolutely necessary, and also
for nested types.

It seems to me that you are using classes and structs in exactly the right
way. However, I don't think your judicious use of classes is necessarily an
argument that "classes are second-class citizens in Swift".

Sometimes classes are the right tool for the job. Sometimes structs or
enums are.

What I love about Swift is that it's often much easier to use the right
tool for the right job than other languages. However, if classes are
relegated to some sort of demoted status, over time, that would no longer
be the case.

+1. Swift loves OOP as well as POP (not to mention FP). They are both
complementary techniques which are great at solving certain kinds of
problems.

-Chris

Swift is the Hannah Montana of programming languages.
https://www.youtube.com/watch?v=uVjRe8QXFHY

-- E, a mommy

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