16 ms·
Swift Evolution: Actors
- legulere 6y agoawaits in actor-isolated functions being reentrant seems like a huge opportunity for bugs, because it is not obvious at all: https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md#interleaving-execution-with-reentrant-actors https://github.com/apple/swift-evolution/blob/main/proposals...
- DelightOne 6y agoCurrently active review: https://forums.swift.org/t/se-0306-actors/45734 https://forums.swift.org/t/se-0306-actors/45734 Review by Chris Lattner: https://forums.swift.org/t/se-0306-actors/45734/4 https://forums.swift.org/t/se-0306-actors/45734/4
- gigatexal 6y agoChris's review is really impressive. Anyone doing code review should take note.
- macintux 6y agoI agree: well-written, well-presented, well-considered. (Of course I'm heavily biased against inheritance, but I think it's a very good review regardless.)
- gigatexal 6y agoTechnical aspects aside I really enjoyed the empathy he showed, spending considerable time showering love to the author on what he liked about the proposal and how well it was written and how overall he’s excited and then skillfully transitioned into the critique of the proposal and not the person.
- qaq 6y agoI really like how Swift is shaping up. They keep making choices that imho give you good balance of ergonomics and performance.
- blacktriangle 6y agoI can't help but feel the opposite, as if Swift is a dog chasing cars. Function builders are a really awkward unnecessary feature that was implemented so that SwiftUI could have a decent looking DSL. Some types showed that the whole protocol oriented programming model breaks down in some serious ways. And now they are looking at doing actors as a whole new built-in functionality rather than just a concurrency library. It looks like the Swift language is just too rigid and unable to evolve in useful ways without the blessing of the language developers themselves.
- afavour 6y agoI really enjoy working with Swift but after looking at this Actor proposal I think I agree with you. It seems like you'd always want to use actors? Unless you specifically don't want things to be concurrency safe. It feels like something the language should have had from the start or should have as an external library.
- ogre_codes 6y ago> It seems like you'd always want to use actors? My impression is that when working with Swift your first tool of choice should be a value type, so a strict or an enum. Actors would only be desirable when you need class-like behaviors and need concurrency. Maybe I’m missing something, but I suspect many of us will never use actors directly at all. I certainly don’t see any parts of my code which demand this kind of structure.
- viktorcode 6y agoYou'll want actors if you have shared mutable state. But the most common usage of concurrent programming will be covered with simple async / await calls.
- 6y ago
- williesleg 6y agoI'm going to archive this thread and sell it as an NFT
- Nican 6y agoOh! Actors is one of my favorite paradigms of lately. I have been reading plenty about Akka as well. When I start looking into the subject further, I start falling into a rabbit hole of how Actors are sometimes treated as a database, that is hard to query, and provides no transaction-ability across actors. There is some argument that Actors could be reimplemented using Postgres' "SELECT ... FOR UPDATE", since you can lock the row for the duration of the transaction. Anyone else have experience managing large amounts of data inside of Actors have a say about this?
- halfmatthalfcat 6y agoI use Akka extensively and have not heard (and would never) use Actors in that capacity.
- dmux 6y agoYou have to walk a fine line with Akka IMHO. Having inherited and supported an Akka Cluster for 4+ years, I'd strongly recommend that you evaluate whether you really need an Actor based system or if a plain-old set of services talking to each other via a message-broker would suffice. The particular system my team inherited gave us nothing but issues: cluster coordination issues, so quorum wasn't met and things wouldn't start cleanly, network partitioning issues so nodes would randomly be considered dead, actors "becoming" (the term used IIRC) other types of actors based on messages received... the whole thing was just.. too ephemeral and organic for our tastes. I used to get excited thinking about systems like that, but I've since grown more conservative in my technology / architectural preferences (i.e. choose boring technology). We actually ended up replacing it with exactly what I suggested: a plain-old set of services talking via a message-broker. It's stupid simple and we've all slept better. Edit: I will say that the use of actors constrained to a single service to handle concurrency is probably way more supportable than in a clustered mode.
- fearthetelomere 6y agoWith something like Elixir/Erlang, the distributed system is quite robust and reliable from my experience. A bit rigid and somewhat difficult to configure for custom topologies, but dependable overall. >a plain-old set of services talking via a message-broker That said, I think you're absolutely right with distributed Akka. I'm very hesitant to fully embrace it, and we use simple service-level APIs to communicate between nodes. I understand the developers have made a lot of progress on the functionality of remote Akka over the years, but it's just not as tried and true as I would like. Using Aeron for message transport for example, is something that may be the best tool available, but is really hard to sell to my org when simple services are more approachable and maintainable. >actors "becoming" (the term used IIRC) other types of actors based on messages received Yeah... I didn't understand why my org was using the Classic Akka instead of the new and improved Typed Akka for a long time. But cool-looking things like this just aren't worth it sometimes. Especially when Classic Akka "just works".
- wrren 6y agoOver a long enough timeline, all languages converge on the Erlang feature set!
- skohan 6y agoErlang has an incredibly compelling value proposition! The problem is that the tooling and ecosystem are not really there. I tried to dip my toe into it maybe a year ago, and the error messages have not held up according to modern standards.
- fredrikholm 6y agoHave you tried Elixir? It's a thin language wrapped over Erlang, filling in the holes of the areas you mentioned, and was designed for exactly that purpose (programmer ergonomics).
- skohan 6y agoNo I haven't but that sounds like a great concept for a project
- the_duke 6y agoI love actors, but baking them into the language like this seems like the wrong approach to me. The language should provide all the necessary abstractions to create an isolated context. Actors can then "just" be a default abstraction provided by the standard library. Side note: has the Swift cross platform support improved? I was very much looking forward to using Swift as a general purpose language, but Apple didn't seem very interested in making it a true first class cross platform language. I believe IBM also dropped their "Swift for the server" efforts a while ago due to similar concerns.
- EdwinLarkin 6y agoYeah.I would say the Swift ecosystem is too detached from different platforms than iOS/macOS. It's a nice language but useless if you want to use it as a generic purpose tool.
- wealthyyy 6y agoJony Ive - Introducing Swift Actors .......
- Grustaf 6y agoThen it's lucky that 1 billion people use those OS:es. The wealthiest 1 billion at that...
- skohan 6y agoI think "useless" is way too strong here. Swift doesn't have the same community support outside of Apple platforms as something like Rust and Go, but it's a very capable general purpose language with plenty of platform-agnostic packages available if you look for them. Also the C interop is great, so you have access to the last 50 years of prior art. I think the argument is vastly overstated that Swift is not useful without Apple frameworks. It's similar to C# - you have the language, and then you have Windows APIs. Nobody would argue that C# is "useless" outside of Windows.
- pjmlp 6y ago
- alexashka 6y agoThis seems to be a way to tackle concurrency, without addressing distributed programming. Why? As far as I can tell, both Erlang and Akka (the two cited examples that implement actors) do both concurrent and distributed systems.
- ex3ndr 6y agoBecause distributed part is library not language feature?
- pjmlp 6y agoErlang, Axum, Agent Tcl, Active Oberon are just a few examples where it is a language feature.
- fearthetelomere 6y agoActors aren't just about local concurrency, and if a language is attempting to bake them in, they should leave some room for the distributed case in my opinion. One of the biggest benefits of using actors is that whether an actor exists on the same machine or across the network can be abstracted away from you. You're just sending messages, so you can send a message over the wire and it would be as simple to the developer as sending it locally. Not considering this use case would make it a very limiting language feature.
- jchb 6y ago> You're just sending messages, so you can send a message over the wire and it would be as simple to the developer as sending it locally If you are expecting a reply, or some side-effect in another system, as a result of the message you sent (and usually you would expect that, otherwise you wouldn't send the message in the first place) then it's not that simple. If the actor is in the same OS process on the same machine, then message delivery is reliable, and you know you'll either get a reply OR a signal that the other actor died. If, on the other hand, the actor is on another machine across the network then the semantics are different. You cannot always differentiate between the remote actor dying and an intermittent network connection error. So you need to take that into account in your protocol design - for example by making operations idempotent. I've many times seen Erlang code where the developers didn't make this distinction - because the message passing operation looks the same, remote or not - and as a result the system is not resilient to network failures.
- mrkeen 6y ago> we can implement a correct version of transfer(amount:to:) // Safe: this operation is the only one that has access to the actor's isolated // state right now, and there have not been any suspension points between // the place where we checked for sufficient funds and here. balance = balance - amount // Safe: the deposit operation is placed in the `other` actor's mailbox; when // that actor retrieves the operation from its mailbox to execute it, the // other account's balance will get updated. await other.deposit(amount: amount) Am I correct in thinking that the system temporarily destroys money here (and hopes to recreate it if the receiver doesn't crash?) If I have a closed system of actors constantly transferring money among themselves, and I poll to see the total amount of money in the system, will it fluctuate?
- liuliu 6y agoI think that you are correct. There is a paragraph on actor's reentrancy: https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md#actor-reentrancy https://github.com/apple/swift-evolution/blob/main/proposals... which points out that you can definitely get balance before await, therefore, have fluctuated total.
- rkalla 6y agoIn the code above it certainly looks like that - is this not idiomatic swift-finance code though?
- syrrim 6y agoThey say that the `deposit` call is placed in `other`s mailbox. As long as the mailbox is preserved between restarts, then there is no danger of the money disappearing. The money will temporarily be unavailable - that is, in neither account's balance. But the money is still somewhere, in the mailbox.
- gpderetta 6y agoA crash between updating balance and putting the message in the mailbox can still result in an inconsistent state.
- blt 6y agoThe idea of the "mailbox" reminds me of Dispatcher.BeginInvoke in C# on Windows. It is very powerful, but can also lead to messy code where many threads are mutating shared state and failing to fully reason about all the other mutations that can happen. Systems with explicit communication, i.e. based on queues, encourage the programmer to limit the number of possible ways in which worker threads can interact with the thread that manages mutable state. They also require something like a big switch statement where all possible interactions are listed in one place. It seems like in actor-based code, all the possible interactions with the mutable state could be spread across the code base. On the other hand, these hand-written message loops and switch statements are essentially the same code that the compiler would generate for an actor's mailbox processing loop. One could argue that writing them by hand is a waste of effort. Programmers simply need to use discipline. (Similar to virtual dispatch in C++, for example.) I am curious about the opinions of programmers more familiar with concurrent architectures. Does the actor model make it easy to write concurrent spaghetti code?
- fearthetelomere 6y ago>Does the actor model make it easy to write concurrent spaghetti code? I think you could write concurrent spaghetti code in any environment just as easily, to be honest. I also think it really depends on which actor-model implementation you're using. Each offers their own unique experience, as I've found that there's a noticeable difference in the Scala+Akka approach vs Elixir+OTP, for example. The actor model is just one way of reasoning about concurrency. Your mailbox serializes/linearizes your interactions so you don't necessarily need locks. Because you get that for free, you can write "single-threaded" code to handle each message. Messages can be sent across a network, so now you can concurrently interact with remote actors too. The simplicity of the model stops there. Concurrent code gets complicated very quickly by nature. If you architect your application to have messages causing ripple effects in your system, your application behavior is going to be very difficult to reason about. But that's nothing new, either. Instead of sending messages, concurrent function calls could produce the same issue. Is reasoning about message flows and behaviors hard? Yes, but that's just a byproduct of concurrency. Like you said, "programmers simply need to use discipline", but everyone will disagree on what discipline looks like. Each actor model implementation will have their "best practices" to mitigate the complexity of concurrent interactions, as will each organization using said implementation for their project. At my work, the way we use Scala+Akka was very structured and not at all the way I expected, having been used to Elixir+OTP. As I think the issues of the actor model are actually issues with reasoning about concurrency in general, I would pick actors over coroutines just for the up-front structure and simplicity it provides.
- pixel_tracing 6y agoI really don’t like that we have the overhead of a new keyword called actor
- kyberias 6y agoWhat kind of overhead do you think a keyword could bring?
- ardit33 6y agoit is called language verbosity... over time it ads up and creates a difficult language to work with, and hard for beginners to start with
- skohan 6y agoI actually think one of Swift's strengths is that it takes progressive disclosure of complexity seriously. It's possible to write a hello world which is no more complex than the python version, and gradually learn about the more esoteric concepts as you need them. By contrast, a language like Rust is far more parsimonious with introducing keywords, but I think I encountered 10 new concepts in the first month I was working with the language, all of which I had to understand at some level in order to progress with my project.
- xiaodai 6y agoWill it go the way of AD in Swift or Tensorflow for Swift. Maybe not coanit might fit in the Swift ecosystem
- danappelxx 6y agoFrom what I can tell UIKit and friends will adopt this actors eventually via bridging (just like they adopted Swift 3's error handling).
- zepto 6y agoNo, because that was a fork, and this is mainline development.
- waffletower 6y agoInvestment in STM (Software Transactional Memory) through implementation of supporting data structures, could be a more effective investment for this problem space.
- fiddlerwoaroof 6y agoClojure’s STM is basically unused: from what I can tell, STM turns out to be better in theory than in practice.
- lilactown 6y agoThe problem IMHO is that there aren't a lot of problems that are particularly suited to STM. I can't think of many instances where I would use STM over CAS (e.g. an atom in Clojure); and the times I have needed something akin to STM (e.g. building an incremental dataflow engine with transactions) I wanted more control over the runtime behavior than Clojure's STM provided. Plenty of business systems need transactional guarantees and either durability or distribution (or both), which makes an external store like Postgres or Redis a ton more attractive than STM within a single process.
- breatheoften 6y agoSome manner of pluggable STM protocol to define a standard programming model on top of conflict detection would be pretty nice tho imo. Features like postgres's serialized transaction isolation -- which as a non expert I think of as basically database implemented STM -- are really flexible -- but most ecosystems programming models don't really expose this capability in a full proof and natural way. You have to work to too hard to take full advantage of the capability ...
- astrange 6y agoI don't think image computing is a good idea and so I don't think complicated memory updating abstractions like this are necessary. The other problem is that Intel tried to add STM to their hardware, but it has so many bugs that it's been disabled in multiple CPU generations. I only know one program that uses it and it's a PS3 emulator, not a database.
- layoutIfNeeded 6y agoAnother thing that’s about to be bolted onto Swift at the language level (instead of making it a library). Sad!
- slavapestov 6y agoYou'll need to explain how the static isolation checking could be feasibly implemented in a library.
- layoutIfNeeded 6y agoThis is not gonna end well: https://tclementdev.com/posts/what_went_wrong_with_the_libdispatch.html https://tclementdev.com/posts/what_went_wrong_with_the_libdi...
- bsaul 6y agoNo idea why you got downvoted. The fact that the first implementation of the actor system is most certainly going to be on top of libdispatch makes it a very interesting property. Libdispatch is really not known for being able to spawn thousands of "light thread". And so there is indeed a potential pitfall in making asynchronous + concurrent code easier. However, after having read a lot of the swift proposal relative to concurrency, i am still unsure if the end result of production code is going to be lots of actors running in parallel, or just lots of async calls multiplexed on a few OS threads.
- astrange 6y agoYou can easily have thousands of tasks (blocks) in dispatch, and thousands of queues as long as they're targeted to the same base queue. The issue with dispatch is that it wasn't communicated enough that you want to have a few base queues and then a tree of other serial queues on top of that, so people end up running too many CPU threads at once, on a device that often has less than one core free at a time.
- astrange 6y agoThis post is a somewhat inaccurate rewording of official advice from the dispatch team at Apple, but that's one of the teams working on Swift concurrency so, like, it'll be fine.
- viktorcode 6y agolidbdispatch is a concrete implementation of a scheduler on OS threads. Actors (and new async model in Swift) are abstractions that are independent of underlying concurrency implementation. So the presumption that the new asynchronous model will inescapable have threads overhead is wrong.
- toddh 6y agoOnce you have actors it helps to have a priority mechanism, latency guarantees, a scheduling abstraction so work can be partitioned, some sort of boost mechanism to prevent starvation, a timer abstraction, and a state machine abstraction to carry out work flows.
- astrange 6y agoSwift concurrency has that in other reviews. Although not "guarantees" since it doesn't define a realtime system.
- javajosh 6y agoI find it hard to accept that a real actor model can be implemented at compile time!
- machiaweliczny 6y agoWhar do you mean by that?
- jayd16 6y agoAt first I assumed this would lock all touched state across all touched actors such that you get the same kind of cross table-row locking guarantees a SQL call would give you ...but it seems like its still an open question in the RFC?
- dwaite 6y agoactor state is meant to be read and modified only by the actor itself, which operates off of its own message mailbox (e.g. event queue). I would expect actors to operate on their own state and on immutable data, and things such as transactional data coordination to be separate. That said, I don't have nearly the experience with actors as many on HN - are you thinking of a particular actor-based system w.r.t touched state?
- 29athrowaway 6y agoI thought the proper way to implement something like this was: - fixed point instead of floating point numbers for currency. - have a journal with transactions such as withdrawals and deposits, plus a "checkpoint" at one point in time where you store the computed balance. e.g.: your balance is = starting balance for today + today withdrawals + today deposits
- travisgriggs 6y agoI like Actors. I have played with them in both Pony and Elixir. They were always an obvious next step from Smalltalk style everything-is-a-fricking-message oriented programming/thinking. Is it just me, or is Swift getting really complex? As I read through Lattners evaluation, I found myself wondering "hadn't heard of that @Thing yet. I wonder how many there are that I don't know about." Wile I was impressed about some of the nuance that goes into the combinatorial complexity of the many design patterns Swift wants to capture under its broad umbrella to make it a language for everyone, I kind of have to accept that I will probably never have the bandwidth to acquire the High Priest level of knowledge to really get the thing. Which means the odds of ending up with unexpected and surprising behavior actually rise. I feel more and more this way about Kotlin, Rust, and even Python these days. Reading/writing code in these increasingly multi-paradigm ecosystems is starting to feel like reading epic poetry (e.g. Dante or Beowulf), but where different sections are written in different languages, with different prose and rhyming styles, some pieces a mix of two left for the reader to figure out. I used to stress about getting these systems enough to code idiomatically in them, to use them "right." But more and more, I'm finding myself developing personal dietary menus in each, where I pick a nutritious subset that I can wrangle into some sort of expressive consistency, and just write code in that subset, idiomatic-fling-of-the-season be damned.
- dwaite 6y ago> Is it just me, or is Swift getting really complex? As I read through Lattners evaluation, I found myself wondering "hadn't heard of that @Thing yet. I wonder how many there are that I don't know about." Probably a fair few, although I wouldn't blame you for not knowing about something like @Sendable - since that is closely associated with the actor proposal.
- skohan 6y agoMy experience with Swift was that there was a section of the language API that I used heavily, then I had features I would use every now and then, and others I would use almost never (like keypaths). I think one of the advantages of a multi-paradigm language is that you have the freedom to carve out a sub-language which is appropriate for a given problem or for your personal taste. That said, I think Swift does sometimes feel like it's missing a metaprogramming system. Like property wrappers and function builders could probably be generated by a sufficiently powerful macro system, but instead they exist as one-off features with significant complexity.
- sai_c 6y agoEvery time I read about new Swift features, I'm reminded of this paragraph from Aaron Hillegass's book "Cocoa Programming for Mac OS X". I don't know why. Once upon a time, there was a company called Taligent, which was created by IBM and Apple to develop a set of tools and libraries like Cocoa. About the time Taligent reached the peak of its mindshare, I met one of its engineers at a trade show. I asked him to create a simple application for me: A window would appear with a button, and when the button was clicked, the words "Hello, World!" would appear in a text field. The engineer created a project and started subclassing madly: subclassing the window and the button and the event handler. Then he started generating code: dozens of lines to get the button and the text field onto the window. After 45 minutes, I had to leave. The app still did not work. That day, I knew that the company was doomed. A couple of years later,Taligent quietly closed its doors forever.
- pindab0ter 6y agoIt’s a funny excerpt, but I don’t see how it’s relevant to this. It seems to me that the whole point of introducing actors is to reduce complexity.
- sai_c 6y agoAs I said, I really can't put my finger on it, but maybe it is this (tongue in cheek). Taligent: The whole point of adding all those abstractions is to reduce complexity.
- mpweiher 6y agoOne of the funny things about our industry is that when our smartest really give their best to do The Right Thing™, we get Taligent and Swift. And when people do everything "wrong", we get amazing things like Smalltalk and Cocoa. Hmm...
- djxfade 6y agoWhat's wrong with Swift? It's a massively popular language
- sriku 6y ago(some thoughts) The "actor properties can only be accessed via self." constraint seems a very elegant and simple static expression that is understandable in a program. Love it. On top of that, the rest of the proposal appears to stick to "message passing is method invocation" way of thinking about objects and therefore syntactically both get mixed up. I have a hunch that the language would be better off being explicit about message passing and keep it separate from "invoke an async method". (The proposal requires automatic conversion to message passing when it sees an async method invocation on an actor.) The "access immutable properties directly" facility should reduce a lot of boilerplate that would otherwise crop up, but actor usage itself should stay away from styles that require such cross-actor access (opinion).
- ivanbakel 6y ago>I have a hunch that the language would be better off being explicit about message passing and keep it separate from "invoke an async method". What would be the benefit of this? (I haven't read the proposal in detail, but) If you can invoke an async method of an actor, it still has to be run in serial with the execution of messages to avoid races. Besides, in the implementation I doubt that "messages" will be anything more than delayed async methods (and the implementation notes seem to support this). Actual actor systems rarely treat messages as first-class serializable entities - unless they hope to execute across multiple machines.
- sriku 6y agoYou're right on both counts. The mechanism of implementation will be very close .. which is what is in the proposal too. Having programmed in Erlang, I feel that the notation (i.e. syntax) influences moment to moment thinking. So if there is overlapping notation between method invocation and message passing, I expect increased opportunity to code and design incorrectly, resulting in subtle bugs. Making "async message passing" syntactically explicit and obivous clarifies a lot and prevents expectations of calling other methods, facing compiler errors, or worse having to deal with specified behaviours that may not be what I had in mind. Edit: one example of this confusion in the spec is the whole part about inheritance in actors ... which looks like a terrible idea to me.