Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
How Actors Work Internally in Swift (swiftrocks.com)
123 points by andrekandre on Aug 13, 2021 | hide | past | favorite | 49 comments


It seems to me there's some real cognitive dissonance in the fact that Swift is promoted as a systems programming language, but its facilities are insufficient to implement its own runtime. I like the language well enough, but there's a lot of "almost-but-not-quite" frustrations about it in that domain.

Bit of a tangent, I suppose, but I was disappointed to see that the actor implementation continues enlarging the C++ core of the language. I also think that combines with the fact that the project is, in reality, controlled by Apple and not truly open, to create an extraordinarily high bar for contribution.

I think the very narrow needle it's trying to thread between its base case as a GUI applications environment and other types of programs may end up dooming it. You can't please all the people all the time...


> The protocol states that all actors must also be Sendable, which is another new important piece of the concurrency proposal. This protocol has no actual code, and its purpose is to "mark" which types are safe to use in a concurrent environment.

It looks like Swift is taking pointers from Rust. Specifically Rust's send/sync traits. There's even other keywords like unsafe in Swifts new `UnsafeSendable` for Sendable types that bypass compile time checks.


Unsurprising, since the creator of rust, Graydon Hoare now works for apple on swift[0] in "a non-leadership position".

[0] https://old.reddit.com/r/rust/comments/7qels2/i_wonder_why_g...


I believe that Graydon left Apple a while ago, but following compiler Twitter, it’s pretty clear that the rust folks and Swift folks are usually in good terms.


You don't by chance know a list of handles to follow to get started following "compiler Twitter"?


I would recommend @johnregehr, @shafikyaghmour and @trav_downs if you're new to the area. If you're particularly interested in rust, @myrrlyn, @ManishEarth and @steveklabnik are great follows. @jfbastien is a compiler dad-joker extraordinaire and @jckarter is a master shitposter (with a side of old Macs) if you're in need of compiler-oriented comic relief.

In particular, John tends to get involved in a lot of interesting conversations, so he's great to find more people to follow.


I believe a few apple employees also contribute to Rust right? Seems to be quite a bit of crossover.


> taking pointers from Rust

Surely you meant borrowing.


Swift and Rust have a very similar feel to them, and I love both of them. There is no doubt that there is a lot of cross pollination between the two. And at least one person that was involved in creating Rust now works on developing the Swift language.


Can you comment on compile time for both in your experience? Larger Swift and SwiftUI projects are bring my (new) mac to a grinding halt and I’m finding myself working around things I know will be difficult for the compiler.


I'm using both a lot. Rust has better compile times than Swift. It is especially not prone to these "one expression takes 60 seconds" scenarios. Although I feel that things have improved considerably with Xcode 13 beta. On the Rust side, there's an (almost-done) alternative compiler backend called Cranelift that brings 30-50% faster compile times for debug builds. This improves the iteration time considerably.

https://news.ycombinator.com/item?id=25130528


The concept of unsafe keywords in systems programming languages goes back all the way to 1960.

Rust might have brought many interesting concepts into mainstream, however unsafe wasn't one of them.


That combined with marking types as Sendable points strongly at Rust influence. Then when you know that literal Graydon Hoare is working at Apple on Swift, the Rust influence is undeniable.


The Unsafe API has existed since Swift 1 which predates Graydon joining, and `UnsafeSendable` is just following the language naming conventions.


Perhaps this is exposing a gap in my knowledge of programming language history, but I'm not aware of other languages that mark types as threadsafe with an otherwise empty Send(able) implementation.


Sendable can also be equated to serializable once you are going cross-process. In that sense you see usage in systems like CORBA and RMI, and so systems that broadly aim to make things sendable like Erlang.


See Concurrent Haskell as possible set of ideas how typeclasses can be used to constrain the set of operations and sequences a type can perform.


But that's pretty different, since normal code in that context uses immutable data structures and the channels, etc. are language features. You then don't have to tack something like Send(able) onto arbitrary types because they're immutable anyway. The whole point of Send(able) is thread safety in the face of generally mutable state in contrast to something like Concurrent Haskell.


The idea is already there, the rest are details.

It is like arguing Rust invented affine types while ignoring the ideas in Cyclone and linear typing, even if they don't map 1:1 to what Rust has.


Affine types are a version of linear types.

Concurrent Haskell doesn't use typeclasses to constrain what data can be passed between threads. All data can be in Concurrent Haskell because all data is immutable anyway. It's a fundamentally different concept, not a slightly different subset like affine/linear types.


Is he still working there? It doesn’t really look like he is.


Graydon has been off doing other things for a while now. Off the top of my head, I think he left in 2019.

I miss working with him, but there are lots of folks involved with Swift who think really deeply about this stuff. No complex feature is the work of any one person in isolation.


My sense is that was a recent change, a feature as integral to internal design f the language would be baking for while, and you would expect his influence to outlast his presence.


Unsafe monads were already a thing in Haskell.

Sendable is just yet another monadic type, or a ML functor.


There is really no way in which Rust's Send or Swift's Sendable form a monad or a functor in any of the senses of those terms.


That doesn't change the fact that Haskell and ML did it first on their type systems, regardless how pedantic we want to discuss type theory.


The things you are saying are just not true. Standard ML and OCaml are in fact single-threaded languages, and while Haskell has concurrency and parallelism support it does not have anything that looks particularly like a Send or Sendable trait.


Concurrent ML:

http://cml.cs.uchicago.edu/pages/sync-var.html

As for the rest I am on the go to type a proper example.


Yes, Concurrent ML exists. It also, however, does not have any feature analogous to Rust's Send trait or Swift's Sendable protocol.


Of course it doesn't have a type class Sendable, that is not the point, rather the type theory behind it, and what those languages allow for, regardless of what they have in 2021 on their standard library.


While there is no exact match between them, the ``Send`` trait which can be thought of as preventing you from sharing an object between two threads actually has two monadic implementation in haskell. The ``IORef`` and ``STRef`` monads serve exactly the same purpose. This utilizes the Rank2Types extenstion. I invite you to read the Lazy Functional State Threads paper[1] by Simon Peyton Jones published in 1994.

The purpose of the ST Monad is to prevent you form sharing the object inside it wrapped by an STRef with another thread of execution.

IORef utilizes the uniqueness of the ``IO`` monad and its properties guaranteed by the haskell runtime i.e. only one thread can use it at any given time to prevent more than one thread from having a reference to the object inside the IORef from multiple.

[1]:https://www.microsoft.com/en-us/research/wp-content/uploads/...


Using empty interfaces (protocols) as markers is a longstanding practice in Java [0], although nowadays it is considered an antipattern and annotations are preferred.

[0] https://en.wikipedia.org/wiki/Marker_interface_pattern


> There's even other keywords like unsafe in Swifts new `UnsafeSendable` for Sendable types that bypass compile time checks.

Swift has used the “Unsafe” prefix for a very long time to indicate things that break its safety model, see for example the UnsafePointer APIs.


Swift actors seem to differentiate from other actor implementations (see Erlang) which have a decoupled message queue. Calling into a Swift actor may be non-blocking, but the result must still be await'd and from the actor's perspective all calls are sequential and synchronous.

By this token, actors seem to be the language-level implementation of a class where all methods are asynchronous, and also being protected by the same lock.

The novel feature seems to be cooperative scheduling amongst actors.


I spent a bit of time watching the sessions after this years WWDC. I’m primarily an Elixir developer but have wanted to try Swift for a while, so the introduction of Actors felt like a good point to jump in.

My enduring impression after learning Swift Actors is that a LOT of additional complexity is introduced because of reference types. The data inside an Elixir process is just that: data, but the possibility of the data in a Swift Actor being a reference to a class suddenly makes everything complicated.

Regardless of the technical achievement from a compiler perspective I found that Swift’s Actors didn’t map well to my understanding of them from Elixir/Erlang and that they weren’t a net positive for solving the kinds of problems actors are suited for. I’ll concede this is all very personal and subjective though.


My understanding is the primary role of Swift actors is to support safe global state in an async environment.

I plan to use actors enums for states in my next major app version release.

Or at least I was, until Apple decided to backdoor the iPhone in the absurd notion that it protects kids. Now I’m trying to figure out how to migrate off their platform entirely.


They seem to have done a great job of combining async with multithreading by using the actor pattern which is really smart.

They also seem to have an m-to-n scheduler (not sure if in user space or kernel), so you can have more actors than “threads”.


After having tried to follow the discussions on swift forums on concurrency and actors, i still couldn't make my mind on whether their design was "fancy" smart (aka : something subtle and brilliant, that few developpers will get their grasp on and lead to inscrutable concurrency bugs), or "stupid" smart (aka : take a complex problem and make it look simple).

Actors in systems like erlang seem to make concurrency simple, and the model seems to be easy to grasp.

On the contrary, a few code samples i've seen on swift forums made my mind twist trying to understand how the code was going to be executed..


The big difference AFAIK with Erlang (other than reference types) is that Swift's model is reentrant, while Erlang's is not.

This is why `await` is such an important keywords to litter around your code - anything can happen in between when you await and when you come back, including actor message processing. You could have another invocation against your actor instance happen in tandem.

This definitely increases the learning curve, but likely still saves time later when you are fighting various deadlock conditions.


that's exactly the part where i lost it. The code samples talking about reentrancy and its various quircks in some proposal discussions really made me wonder where the whole thing was going..

its seems to me having an async public interface with a sync internal implementation is the most straightforward architectural design for actors, but obviously they thought it was too limiting. Do you have any idea why ?


If an actor system is fully synchronous, it is much less efficient and can't be reentrant.

Actor implementations tend to be split on reentrancy. Having multiple paused in-flight invocations of the actor means that you need to have clearly understood suspension points (hence await keywords). Non-reentrant actors like in Erlang can deadlock if two actor instances are calling one another. Because of the deadlock and some inefficiency concerns, some actor systems allow you to decide reentrancy per-actor as well.

Because Swift concurrency is an upgrade on top of decades-old systems, I believe a certain amount of additional complexity was required whether it was built as reentrant or not.

It was a bit harder to find than I expected, but here is a discussion piece around the initial choice of reentrancy by default. https://github.com/ktoso/swift-evolution/commit/d55bbbd6cc1a...


Erlang does not implement the Actor model. The similarity is accidental.


I'm not sure if the author of this blog visits hacker news to read this, but your code blocks are unreadable without javascript. They're black highlighted black text.


The site loads smoothly even with JS on. If you want to fix the code blocks with JS off, try the following:

  pre {
      background-color: rgb(40, 40, 35);
      color: rgb(248, 248, 242);
  }


If you remove the wheels from a car don’t expect a smooth ride.


Very unsuitable analogy.

If you remove syntax colouring from a text, you do expect to still be able to read the text.

Why the difference? Because the wheel is an essential component of a car, while syntax colouring is more akin to the paint job of the car: you change change it, remove it, it won't affect the function of the car.

BTW, if we remove the CSS, the text becomes apparent again. So if we keep going with the same analogy, it becomes even more unsuitable: removing half of the wheels prevents the car from working, but removing all of them now restores a smooth ride. We quickly see how it turns absurd and contradicts.


"Actors is a feature that is part of Swift's Structured Concurrency, bringing a brand new format to write and deal with asynchronous cod" ,

Brand new? I thought Carl Hewitt invented Actors https://en.wikipedia.org/wiki/Actor_model


The next sentence immediately says:

> Although what Swift brings is new to the language, it's not new to tech itself.

I think it's pretty clear that the author meant "brand new to Swift"?


What I would have liked to have read is how their Swift Actors relate to or differ from the other existing models.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: