"Actors are reference types, but why classes?"

The more I think about it, the more I wonder; Can we really get actor to conform to protocols?. I mean, sure they can if we ensure proper actor isolation/independence of the requirement, but where is that useful?

Even common protocol like Codable wouldn't be usable by essentially any actor given the dynamic nature of the actor. It seems more likely that the actors will instead vent out a "screenshot" that conforms to protocols.

1 Like

If you compose two actors, they'll be considered to be isolated from each other, i.e., you won't be able to do a coordinated state change to both actors to maintain an invariant across them.

When you inherit an actor, your additional state is within the same isolation domain, so you can coordinate a state change with your super-actor-class and maintain invariants across the whole thing.

Doug

6 Likes

Exactly my point. If an actor provides an isolated foo, and we want to add bar that is coordinated with foo, we'd need to extend/inherit the actor. We definitely can "extend" an actor without inheritance, but it'll be a lot of work for something that inheritance should do naturally.

Now the question is: "how often does one need to add bar that coordinates with foo?"

1 Like

Would inheritance be an inappropriate tool in this case, though? It seems like it is conflating data with transformation. What would be the advantage of making the compiler figure out how to extend foo's state with bars if you could have define a concrete actor that conforms to Fooing and Baring?

Maybe I am lacking imagination?

1 Like

The problem with using inheritance to bring other data in to your isolation domain is that it doesn’t scale: what if there are 2 actors (usable on their own) that I wish to synchronise with? We don’t support multiple inheritance.

Right, in the current proposal it seems to me like global actors are the way to do this. I think it’s a good concept but wish it was designed in a way that fits into Swift’s type system better.

Right. The issue is with sync requirements in the protocols, and the problem of allowing and actor 'self' to escape through existentials and generic parameters without properly providing memory isolation. There is also an evolution problem since protocols can get retroactive sync requirements, exacerbating this.

This is the third major memory safety problem of the base proposal (after actor sendable and global variable semantics). The original actor proposal attempts to solve it by adding some limitations around non-actor requirements, adds attributes to methods, and adds some (weird to me) rules around lets that don't seem sound. I personally don't think this is the best path.

We haven't seen a second revision of the actors proposal yet, so I'm waiting for that. If needed, I'll write an "actors vs sync requirements" white paper to capture some of the tradeoffs and thoughts here. There are a couple of ways to solve the generality and soundness problem with different tradeoffs.

-Chris

2 Likes

Inheritance can work in some case, but you're right that it doesn't scale.

This discussion has given me an idea of how we could allow some form of composition with synchronous access for actors. But it's a bit out of scope for this thread. So I wrote a pitch: Interlocking in a Hierarchy of Actors

With regard to actors and inheritance it might be an idea to have a look at the Akka actor framework (akka.io). It is probably the most widely used (distributed) actor framework even before Erlang, I would say. It is for the JVM and written in Scala.

Scala with traits sort of supports multiple inheritance. So if the Akka people thought there is any point for actors to be able to inherit it can probably be seen from what they have done. Akka exists for years and is industry-proven.

I would take Akka with a piece of salt, though. IMHO some things with regard to actors were overdone in the same "spirit" things IMHO were overdone on Scala.

I would much prefer that you provide an example that you believe to be unsound, and I can use that as a way to teach interested folks how the actor proposal maintains soundness. That will help me understand what in the proposal didn't come across well (or if there actually is a hole in the soundness of the model). It wouldn't make a lot of sense to write a white paper based on a misunderstanding of the proposed model.

Doug

By catching up on the discussion, I see the following arguments.

1 - Actors are reference types, not classes. Therefore, they should be treated as their own thing.
2 - Swift should encourage Protocol-Oriented Programming over Object-Oriented Programming.
3 - Subclassing actors is valuable, sometimes, so Swift should allow it.

I agree with all of them and they're all reconcilable. Here are my conclusions from the arguments above.

1 - The syntax for actors should be just actor.
2 - By default, the actor syntax does not afford inheritance.
3 - One can allow subclassing of an actor with the syntax open actor.

This approach is also compatible with progressive disclosure, which I personally also value a lot.

8 Likes

Without commenting on the whole actor-topic, imho this detail is a bad idea: It takes a word that is already established in a similiar context (a good thing), but changes its meaning (not so good).

5 Likes

I see it as more appropriate than how class inheritance works today. Continuing on the topic of progressive disclosure I think it would make sense to lay out the basic Swift types in the following order

  • Value Types
    • struct
    • enum
  • Reference Types
    • final class
    • actor
  • Inheritable Reference Types
    • internal class and external open class
    • open actor

Although this order is what makes more sense, given the complexity of the types and their capabilities, I agree that the disparity between actors, closed by default, and classes, open by default, is weird. I also think it's weird that internally classes are open by default and across modules they're not. Classes, as they stand right now, are inconsistent with Swift's principles of best practices by default. I don't think we should make actors as inconsistent as classes in favor of consistency. I think we should fix classes, a topic for another thread, though.

In an ideal scenario classes should also be closed by default and then we would have the following:

  • Value Types
    • struct
    • enum
  • Reference Types
    • class
    • actor
  • Inheritable Reference Types
    • open class
    • open actor

While this would be feasible is another story, but I think we should at least consider it. I don't think actors need to suffer because of classes. We can do actors the best way and improve classes, if possible.

2 Likes

Can you please clarify how would the meaning change? Maybe I'm missing something.

open, for classes, means code outside the declaring module can subclass the class. You are proposing that open for actors would apply even to code within the same module.

Yes! I also mentioned that, IMO, this behavior should apply to classes. I don't see how the fundamental meaning is different though.

-- edit

By the way, Swift already uses open with a different fundamental meaning for enums.

That is what open would mean:

type other module same module
class public & and subclassing possible no difference
actor ??? subclassing (or maybe "subactoring? :rofl:) allowed

??? could either be more restrictions than for classes (would you have "open open actor"?), or it would take away the option for inheritance only in the same module — which, btw, I consider to be an acceptable compromise that is here to stay.

The best option for me would be to make actors and classes closed by default and use open only when referring to inheritance (for classes and actors) and leave access control to the private, public and internal keywords. If you want to allow inheritance only inside a module, for example, you could do public internal(open) actor. I haven't put enough thought on changing the current inheritance syntax for classes, but at the top of my head I can't think of any reason why the compiler wouldn't be able to automate the whole migration. Surely there will be a lot of people reminding me of other things I haven't thought of.

The Swift Evolution proposal for the design of open for classes has long ago been concluded. These alternatives have been considered and rejected. It is not going to be revisited and it’s not germane to the topic here.

The question is whether it’s appropriate to consider actors to be classes. If so, then it stands to reason that they will support subclassing, and they will do so with the same syntax as any other class.

2 Likes

I Imagined that would be the case. I still hold my position that actors are not classes and we should not make them behave the same way classes do for historical reasons.

1 Like