Swift Actors: A practical example, part 1
trycombine.com
trycombine.com
Many of the Rx based architectures (The Composable Architecture in Swift, React, Cycle.js, etc) that have appeared – all inspired by Elm (itself inspired by Erlang/OTP?) – seem to lean on a kind of pseudo actor model where messages are passed into a kind of 'actor', its state is modified, side-effects are initiated and performed asynchronously, and subscribers are updated in turn.
With the Rx based architectures at least this created a lot of boiler plate and the need to create a separate enum just to pass messages into the monad with a big switch statement to route them all.
Now, if I understand correctly, this actor model would facilitate a very similar workflow but without an additional dependency and far less boilerplate.
Instead of passing messages into the 'actor' via a messages stream – you can just call methods on the actor. Then, an actor can manage a 'state' property internally and simply forward changes to that state to via a publisher (PassthroughSubject/BehaviorSubject).
That would be pretty nice!
No, Elm is just another implementation, not the invention.
See the following for Actors:
Actors are something different.
Not really. You didn't mention Redux and you explicitly mentioned React, hence my clarification.
It's an amazing, rich language with the only drawback of the compiler being shamefully slow (though it's probably not that bad on Linux, where there are no legacy ObjC frameworks). Other than that, I love the idea of having an ARC-based modern type-safe language on the backend, possibly with the new concurrency model being at the core of the network framework.
For example, how could Swift Actors implement the following:
TrainController ≡
moving := False // train is initially not moving
isOpen := False // door is initially closed
open ↦ moving ?? // Received request to open door, there are two cases for moving:
False ↠ // Case moving=False, then
isOpen := True; // then set isOpen to True and then
DoorController.open // send door an open request in hole of the region
True ↠ Throw TrainMoving[] // Case moving=True, then throw TrainMoving
close ↦ Hole DoorController.close ; // Received request to close door, so send door a close request
// in a hole of the region and after door reports that it is closed,
isOpen := False // set isOpen to False
move ↦ isOpen ?? // Received request to move train, there are two cases for isOpen:
False ↠ // Case isOpen=False, then
moving := True; // set moving to True and then
EngineController.move // send engine a move request in a hole of the region
True ↠ Throw DoorOpen[ ] // Case isOpen=True, then throw DoorOpen
stop ↦ Hole EngineController.stop ; // Received request to stop train, so send engine a stop
// request in a hole of the region and after engine reports that it is stopped,
moving := False // set moving to FalseSpecifically, actors would alleviate the the mental overhead of constant dispatch queue management littering iOS codebases. Certain UIKit classes would gain @UIActor and suddenly you get a compiler error if you try to modify a view from a background thread. I can’t seem to find any references to that in the current proposal.
Regardless, I’m excited for actors to land in the next release. I’m just concerned adoption will be similar to Swift 3’s do/try/catch syntax, which didn’t take off until UIKit converted all fallible synchronous methods from `(Data?, Error?)` to `throws`.
But i don't know if it will be the most performant implementation as it will depends of how much state copy between threads its involved (as this is the common bottleneck in performance on a multi-threaded implementation).
So a more classic implementation might perform a little better, but i don't know if this will also stand true in Swift as the compiler might use tricks behind the curtains to optimize things giving the people behind this also control the Swift compiler.
By contrast, i see a lot of talk about actor "isolation", inheritance, reentrancy, etc... Which i don't see mentionned often in erlang / OTP style actor systems...
But maybe there's an extra complicating factor of TSan causing significant performance penalties making it impractical for it to be on in release builds?
> The answer is: “it depends”. It depends how your or others' code uses Person. If two pieces of code, running concurrently, update Person.name at the same time - your app will crash
Damn, I honestly forget what it's like to program with threads in languages that let this happen.
In C# you might have to use the following and worry about the private member access yourself.
await Task.Run(()=>SyncMethod())I think if you mutate that string from multiple threads — like by appending to it — you would get crashes pretty easily.
I’m really not sure if you would see crashes if you simply access and assign to the name property (rather than mutating the value it is pointing to). I have not seen guarantees on this. Maybe I missed it, but if not, even if it appears to work today, it may not tomorrow.
"One Actor is no Actor."
The fact that "there are no variable races in an Actor" is a side-effect of Actor concurrency.