# \[Pitch #6\] Actors

**URL:** <https://forums.swift.org/t/pitch-6-actors/45519>\
**Category:** Pitches\
**Created:** [March 10, 2021, 6:43am UTC](https://forums.swift.org/t/pitch-6-actors/45519 "2021-03-10T06:43:45Z")\
**Posts on this page:** 1\
**Showing post:** 32

<div class="post-metadata">

**Author:** ![abbottmg](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/abbottmg/32/15646_2.png) [@abbottmg](https://forums.swift.org/u/abbottmg)\
**Post date:** [March 12, 2021, 10:46am UTC](https://forums.swift.org/t/pitch-6-actors/45519/32 "2021-03-12T10:46:28Z")

</div>

> [@SwiftUI](#):
>
> Turn back to the example:
> 
> > [@abbottmg](#):
> >
> > ````swift
> > > extension A {
> > sync func h() async -> R
> > }
> > _ = A().h() // from caller site, sync-func rule first
> > 
> > func m(v: sync A) async -> R { v.method(); /* sync-param rule */ }
> > _ = await m(A()); // async-body rule last
> > > ```
> > 
> > ````
> 
> any problems?

Yes, I believe so

```
_ = A().h() // not ok: must be awaited because suffix-async

```

This is the antonym problem I was talking about: the `prefix sync` misleads the API consumer into believing they don’t need to `await` the call even though the `suffix async` still requires it.

To state my priors: I assume “an outsider can call this in-sync” means the opposite of “must be called async -\> must be `await`ed”. The `prefix sync` cannot make this promise as long as it can share a signature with the `suffix async` because the prefix only considers one _reason_ there may be a suspension point but the suffix flags the function as being an _asynchronous context_ in which any number of suspension points (including 0) may occur for any reason. Calls to this "must occur within the operand of an `await` expression [(async/await: await expressions)](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#await-expressions)"—no matter what the other portions of the signature claim.

In other words, `suffix async` is not so much `awaitable body` as it is `call mustAwait`. This is parallel to `throws` representing `call mustTry` instead of `tryable body`. In this context, `prefix sync` is like trying to spell `rethrows` as `noThrow func f(() throws -> R) throws -> R`. One side of that signature is lying to me but I don't "know" which until the compiler finishes static analysis.

This conceptualization is why I personally doubt there's much of anything to "unify" into `sync`, which itself is just the special case `async<Never>` (to borrow from typed `throws`). The more I think about it, the more I want to treat Isolation (really, Message) as just a subtype of suspension point, alongside Subtask, Yield, Continuation, etc. Specifically, Message points can be elided statically, conditional on the static context of each call. A function that is `async<Message>`, that is one whose only suspension points are of type `Message`, might be able to determine it can merge or even strip all the suspensions in the call once it sees the call site and knows whether the executor hosting the call also hosts the actor(s) referenced. Therefore both `suffix async` and (the implict) `suffix sync` are incomplete descriptions of the signature, because either could be wrong.

As I hinted above, this seems like a job for a spelling similar to `rethrows` and [`reasync`](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#reasync).

```swift
actor A {
  // synchronous within A's **executor** , async across it
  // can message other actors w/o await if they statically share execs
  // can also handle x-exec msgs, so `await self.f` still common?
  // for this reason I might recommend `message` as an alternative
  // except that makes less sense as a parameter modifier
  func f() isolated -> R
  // always synchronous (nonisolated)
  func g() -> R
  // awaits a "type-erased" set of suspension points
  func h() async -> R
  // asserts body doesn't need to await B (and so awaits A)
  // but call may be sync if caller.exec == a.exec == b.exec
  func i(o: isolated B) isolated -> R
  // as above but some other task type is being awaited
  func j(o: isolated B) async -> R
}

// as f
func k(a: A) isolated -> R
// as g
func l(a :A) -> R
// body will need to await both
// but call may be sync if caller.exec == a.exec == b.exec
func m(a: A, b: B) isolated -> R
// as m, but assert that body doesn't need to await A
func n(a: isolated A, b: B) isolated -> R
// as n, but assert body treats A and B as same
func o(a: isolated A, b: isolated B) isolated -> R

```

From a previous thread, it sounds like this kind of executor analysis may be hard. This probably would result in more `await` operators than the current isolation pitch _but_ that may not be a bad thing, as mentioned upthread:

> [@Chris\_Lattner3](#):
>
> the actor model throughout academia has strong benefits from forcing all cross-actor references to be async through the mailbox. One example where this comes up is with distributed actors

In fact, maybe asserting a non-self actor as `isolated` is an anti-pattern in a distributed world, unless we can ship the whole body over the wire?

---

_[View the full topic](https://forums.swift.org/t/pitch-6-actors/45519)._
