The new Swift Distributed Actors package provides a glimpse into what the future distributed actor
language feature could look like in Swift.
Distributed actors aim to bring local actors, introduced in Swift 5.5, one step forward and extend them to model distributed computation.
We aim to simplify and push the state-of-the-art for distributed systems programming in Swift as we did with concurrent programming with local actors and Swift’s structured concurrency approach embedded in the language.
Local actors are an abstraction built on top of async
and await
to make it easier to concurrently access mutable in a safe way. Local actors provide a sort of barrier for the compiler to transparently enforce specific access rules when their methods are called from outside.
Contrary to local actors hiding away the required synchronization to concurrently access mutable state, distributed actors do not aim to hide away the existence of the network. Rather, they bring the programmer to model their functions thinking they may be remote.
Distributed actors are similar to (local) actors because they encapsulate their state with communication exclusively through asynchronous calls. The distributed aspect adds to that equation some additional isolation, type system, and runtime considerations.
In a distributed actor, only functions that are declared as distributed
can be called from outside of the actor. Such functions not only behave as async
function, but also require all of their arguments and return values to conform to the Codable
protocol, which enables them to be encoded and decoded for network communication. In addition, all distributed
functions throw
by default to deal with any network errors.
The following is an example of the current, preliminary syntax for distributed actors:
distributed actor Worker {
//-- actor state
var data: SomeData
distributed func work(item: String) -> WorkItem.Result {
// ...
}
}
The beauty of distributed actors lies in they being almost free of boilerplate code to deal with serialization and other network-level details. This is in part dealt with through the abstraction of an ActorTransport
, a library component responsible for all the networking that you feed your distributed actors with at init
time:
let someTransport: ActorTransport = ...
let worker = Worker(transport: someTransport)
The ActorTransport
protocol is defined in the Swift standard library, which does not provide any implementation of it. Instead, the user can create its own ActorTransport
-conforming component based on their use-case.
The Swift team has provided the implementation of a Cluster Transport based on SwiftNIO, a high-performance server-side networking library, to implement the cluster’s networking layer. Using this package, distributed actors can form a cluster, discover each other, and communicate with one another. Clusters also support the notion of membership to enable watching a cluster's lifecycle and have the watching actor be notified when the watched actor is deinit
ed.
As mentioned, Swift distributed actors are still a work in progress and are only available through the nightly toolchains. The syntax is not final yet and the Swift team encourages early adopters to provide their feedback to eventually contribute to the final definition through the Swift evolution process.