As unexpected coincidence, I've been going few days ago through old threads on concurrency discussion, and there was a nice way to think about this: actor isolation is orthogonal to access control. So you don't fix isolation with access levels, and you don't fix access levels with isolation. This may seem as contradictory to disallowing async setters on actors, but if you think about it – it makes a lot of sense. So actors don't allow such mutations not because this something you would be able to address with access control, but to enforce state isolation.
Transaction argument, while is important (that's number one post in thread you linked!), isn't the main player here IMO. If we go back to original proposal and review thread, as well as actors model itself, the core idea of actors is to be only holders and protectors of the state. So making async setters now violate this isolation, making isolation a bit blurry concept in general. Again, actor shouldn't be just used as some asynchronous locks, they are much more than that.
Finally, let's consider example from the proposal:
extension BankAccount {
enum BankError: Error {
case insufficientFunds
}
func transfer(amount: Double, to other: BankAccount) throws {
if amount > balance {
throw BankError.insufficientFunds
}
print("Transferring \(amount) from \(accountNumber) to \(other.accountNumber)")
balance = balance - amount
other.balance = other.balance + amount // error: actor-isolated property 'balance' can only be referenced on 'self'
}
}
Now, let's imagine we allow async
setters on actors. How it will look like from usage point? Something like
await other.balance = await other.balance + amount
? That's clearly now what we want.
What if we could eliminate second await
to get
await other.balance = other.balance + amount
Is it one transaction to actor? Or will it first read balance (1st suspension point), then update it (2nd suspension point)? That's unclear from the code, and source of potential errors.
Actor require a bit of shift into using them, so you don't write Java-like setters at all. While they look in Swift like classes from OOP, they aren't them at all.