[HN Gopher] Approachable Swift Concurrency
___________________________________________________________________
Approachable Swift Concurrency
Author : wrxd
Score : 145 points
Date : 2025-12-30 13:01 UTC (9 hours ago)
(HTM) web link (fuckingapproachableswiftconcurrency.com)
(TXT) w3m dump (fuckingapproachableswiftconcurrency.com)
| seanalltogether wrote:
| One of the things that really took me a long time to map in my
| head correctly is that in theory async/await should NOT be the
| same as spinning up a new thread (across most languages). It's
| just suspending that closure on the current thread and coming
| back around to it on the next loop of that existing thread. It
| makes certain data reads and writes safe in a way that
| multithreading doesn't. However, as noted in article, it is
| possible to eject a task onto a different thread and then deal
| with data access across those boundaries. But that is an
| enhancement to the model, not the default.
| jen20 wrote:
| EDIT: Seems like newer versions of Xcode change the Swift
| language defaults here, but that is just the IDE, not the
| language (and Swift Package Manager does not appear to do the
| same!)
|
| I'd argue the default is that work _does_ move across system
| threads, and single-threaded async/await is the uncommon case.
|
| Whether async "tasks" move across system threads is a property
| of the executor - by default C#, Swift and Go (though without
| the explicit syntax) all have work-stealing executors that _do_
| move work between threads.
|
| In Rust, you typically are more explicit about that choice,
| since you construct the executor in your "own" [1] code and can
| make certain optimizations such as not making futures Send if
| you build a single threaded one, again depending on the
| constraints of the executor.
|
| You can see this in action in Swift with this kind of program:
| import Foundation for i in 1...100 {
| Task { let originalThread = Thread.current
| try? await Task.sleep(for: Duration.seconds(1)) if
| Thread.current != originalThread { print("Task
| \(i) moved from \(originalThread) to \(Thread.current)")
| } } } RunLoop.main.run()
|
| Note to run it as-is you have to use a version of Swift < 6.0,
| which has prevented Thread.current being exposed in
| asynchronous context.
|
| [1]: I'm counting the output of a macro here as your "own"
| code.
| MORPHOICES wrote:
| How do you actually learn concurrency without fooling yourself?
|
| Every time I think I "get" concurrency, a real bug proves
| otherwise.
|
| What finally helped wasn't more theory, but forcing myself to
| answer basic questions:
|
| What can run at the same time here?
|
| What must be ordered?
|
| What happens if this suspends at the worst moment?
|
| A rough framework I use now:
|
| First understand the shape of execution (what overlaps)
|
| Then define ownership (who's allowed to touch what)
|
| Only then worry about syntax or tools
|
| Still feels fragile.
|
| How do you know when your mental model is actually correct? Do
| you rely on tests, diagrams, or just scars over time?
| mrkeen wrote:
| Share xor mutate, that's really all there is
| ragnese wrote:
| Talk about trivializing complexity...
|
| The idea that making things immutable somehow fixes
| concurrency issues always made me chuckle.
|
| I remember reading and watching Rich Hickey talking about
| Clojure's persistent objects and thinking: Okay, that's
| great- another thread can't change the data that my thread
| has because I'll just be using the old copy and they'll have
| a new, different copy. But now my two threads are working
| with different versions of reality... that's STILL a logic
| bug in many cases.
|
| That's not to say it doesn't help at all, but it's EXTREMELY
| far from "share xor mutate" solving all concurrency
| issues/complexity. Sometimes data needs to be synchronized
| between different actors. There's no avoiding that. Sometimes
| devs don't notice it because they use a SQL database as the
| centralized synchronizer, but the complexity is still there
| once you start seeing the effect of your DB's transaction
| level (e.g., repeatable_read vs read_committed, etc).
| mrkeen wrote:
| It's not that shared-xor-mutate magically solves
| everything, it's that shared-and-mutate magically breaks
| everything.
|
| Same thing with goto and pointers. Goto kills structured
| programming and pointers kill memory safety. We're doing
| fine without both.
|
| Use transactions when you want to synchronise between
| threads. If your language doesn't have transactions, it
| probably can't because it already handed out shared
| mutation, and now it's too late to put the genie in the
| bottle.
|
| > This, we realized, is just part and parcel of an
| optimistic TM system that does in-place writes.
|
| [1] https://joeduffyblog.com/2010/01/03/a-brief-
| retrospective-on...
| ModernMech wrote:
| +5 insightful. Programming language design is all about
| having the right nexus of features. Having all the
| features or the wrong mix of features is actually an
| anti-feature.
|
| In our present context, _most_ mainstream languages have
| already handed out shared mutation. To my eye, this is
| the main reason so many languages have issues with
| writing asynch /parallel/distributed programs. It's also
| why Rust has an easier time of it, they _didn 't_ just
| hand out shared mutation. And also why Erlang has the
| best time of it, they built the language _around_ no
| shared mutation.
| jesuslop wrote:
| Heisembugs aren't just technical debt but project killer time
| bombs so one must better have a perfect thread design in head
| that works first attempt, else is hell on earth. I can be safe
| in a bubble world with whole process scope individual threads
| or from a thread pool (so strong guarantees of joining every
| created thread) and having share-nothing threads communicating
| only by prosumer sync-queues that bring a clear information-
| flow picture. One can have a message pump in one thread, as GUI
| apps do. That is just a particular case of the prosumer channel
| idea before. Avoid busy waits, wait on complex event conditions
| by blocking calls to select() on handler-sets or
| WaitForMultipleObjects(). Exceptions are per thread, but is
| good to have a polite mechanism to make desired ones to be
| potentially process-fatal, and fail earliest. This won't cover
| all needs but is a field-tested start.
| tetha wrote:
| I've written, tested and debugged low-level java concurrency
| code involving atomics, the memory safety model and other nasty
| things. All the way down to considerations if data races are a
| problem or just redundant work and similar things. Also
| implementing coroutines in some complang-stuff in uniersity.
|
| This level is rocket science. If you can't tell why it is
| right, you fail. Such a failure, which was just a singular
| missing synchronized block, is the _worst_ 3-6 month debugging
| horror I've ever faced. Singular data corruptions once a week
| on a system pushing millions and trillions of player
| interactions in that time frame.
|
| We first designed with many smart people just being adverse and
| trying to break it. Then one guy implemented, and 5-6 really
| talented java devs reviewed entirely destructively, and then
| all of us started to work with hardware to write testing setups
| to break the thing. If there was doubt, it was wrong.
|
| We then put that queue, which sequentialized for a singular
| partition (aka user account) but parallelized across as many
| partitions as possible live and it just worked. It just worked.
|
| We did similar work on a caching trie later on with the same
| group of people. But during these two projects I very much
| realized: This kind of work just isn't feasible with the
| majority of developers. Out of hundreds of devs, I know 4-5 who
| can think this way.
|
| Thus, most code should be structured by lower-level frameworks
| in a way such that it is not concurrent on data. Once you're
| concurrent on singular pieces of data, the complexity explodes
| so much. Just don't be concurrent, unless it's trivial
| concurrency.
| xenihn wrote:
| I'm interested in knowing more details about this if you
| happen to have a post written up somewhere!
| halfmatthalfcat wrote:
| I loved the idea of Swift adopting actors however the
| implementation seems shoehorned. I wanted something more like
| Akka or QP/C++...
| jen20 wrote:
| > I wanted something more like Akka
|
| https://github.com/apple/swift-distributed-actors is more like
| Akka, but with better guarantees from the underlying platform
| because of the first-class nature of actors.
| troupo wrote:
| > the implementation seems shoehorned.
|
| Because it's extremely hard to retrofit actors (or, really, any
| type of concurrency and/or parallelism) onto a language not
| explicitly designed to support it from scratch.
| Someone wrote:
| I feel the reverse. I can see one can claim Swift has
| everything but the kitchen sink, but its actors, to me, don't
| look shoehorned in.
|
| Reading https://docs.swift.org/swift-book/documentation/the-
| swift-pr..., their first example is: actor
| TemperatureLogger { let label: String var
| measurements: [Int] private(set) var max: Int
| init(label: String, measurement: Int) {
| self.label = label self.measurements =
| [measurement] self.max = measurement }
| }
|
| Here, the 'actor' keyword provides a strong hint that this
| defines an actor. The code to call an actor in Swift also is
| clean, and clearly signals "this is an async call" by using
| _await_ : await logger.max
|
| I know Akka is a library, and one cannot expect all library
| code to look as nice as code that has actual support from the
| language, but the simplest Akka example seems to be something
| like this (from https://doc.akka.io/libraries/akka-
| core/current/typed/actors...): object
| HelloWorld { final case class Greet(whom: String,
| replyTo: ActorRef[Greeted]) final case class
| Greeted(whom: String, from: ActorRef[Greet]) def
| apply(): Behavior[Greet] = Behaviors.receive { (context,
| message) => context.log.info("Hello {}!",
| message.whom) message.replyTo ! Greeted(message.whom,
| context.self) Behaviors.same } }
|
| I have no idea how naive readers of that would easily infer
| that's an actor. I also would not have much idea about how to
| use this (and I _do_ have experience writing scala; that is not
| the blocker).
|
| And that gets worse when you look at Akka http
| (https://doc.akka.io/libraries/akka-http/current/index.html). I
| have debugged code using it, but still find it hard to figure
| out where it has suspension points.
|
| You may claim that's because Akka http isn't good code, but I
| think the point still stands that Akka allows writing code that
| doesn't make it obvious what is an actor.
| ModernMech wrote:
| This is my feeling as well. It feels to me that based on the
| current product, Swift had two different designers: one
| designer who felt swift needed to be a replacement for
| Objective C and therefore needed to feel like a spiritual
| successor to that language, which meant it had to be
| fundamentally OOP, imperative, and familiar to iOS devs; and
| another designer who wanted it to be a modern functional,
| concurrent language for writing dynamic user interfaces with an
| advanced type checker, static analysis, and reactive updates
| for dynamic variables.
|
| The end result is a language that brings the worst of both
| worlds while not really bringing the benefits. An example I
| will give is SwiftUI, which I absolutely hate. You'd think this
| thing would be polished, because it's built by Apple for use on
| Apple devices, so they've designed the full stack from editor
| to language to OS to hardware. Yet when writing SwiftUI code,
| it's very common for the compiler to keel over and complain it
| can't infer the types of the system, and components which are
| ostensibly "reactive" are plagued by stale data issues.
|
| Seeing that Chris Lattner has moved on from Swift to work on
| his own language, I'm left to wonder how much of this situation
| will actually improve. My feeling on Swift at this point is
| it's not clear what it's supposed to be. It's _the_ language
| for the Apple ecosystem, but they also want it to be a general
| purpose thing as well. My feeling is it 's never _not_ going to
| be explicitly tied to and limited by Apple, so it 's never
| really going to take off as a general purpose programming
| language even if they eventually solve the design challenges.
| steve1977 wrote:
| The thing I often ask or mention in discussions about SwiftUI
| is, if SwiftUI is so good and easy to use and made for cross-
| platform, why did take Apple themselves for example so long
| to port their Journal app to macOS? This is a trivial
| application, something you'd have found in a beginner
| programming book as an example project 10 or 20 years ago.
|
| I get all the points about Swift and SwiftUI in theory, I
| just don't see the results in practice. Also or especially
| with Apple's first party applications.
| astrange wrote:
| Journal has a lot of extra features where it autogenerates
| suggestions based on what you've done lately.
| cosmic_cheese wrote:
| It's an unpopular opinion, but my belief is that trying to go
| all-in on one paradigm is the actual mistake. There's several
| types of awkwardness that arise when a UI library is strictly
| declarative, for example.
|
| On Apple platforms, I've had a lot of success in a hybrid
| model where the "bones" of the app are imperative
| AppKit/UIKit and declarative SwiftUI is used where it's a
| good fit, which gives you the benefits of both wherever
| they're needed and as well as an escape hatch for otherwise
| unavoidable contortions. Swift's nature as something of a
| hodgepodge enables this.
| whalesalad wrote:
| Any sufficiently complicated concurrent program in another
| language contains an ad hoc informally-specified bug-ridden
| slow implementation of half of Erlang.
|
| - Robert Virding
| scottmf wrote:
| Concurrency issues aside, I've been working on a greenfield iOS
| project recently and I've really been enjoying much of Swift's
| syntax.
|
| I've also been experimenting with Go on a separate project and
| keep running into the opposite feeling -- a lot of relatively
| common code (fetching/decoding) seems to look so visually messy.
|
| E.g., I find this Swift example from the article to be very
| clean: func fetchUser(id: Int) async throws ->
| User { let url = URL(string:
| "https://api.example.com/users/\(id)")! let (data, _)
| = try await URLSession.shared.data(from: url) return
| try JSONDecoder().decode(User.self, from: data) }
|
| And in Go (roughly similar semantics) func
| fetchUser(ctx context.Context, client *http.Client, id int)
| (User, error) { req, err :=
| http.NewRequestWithContext( ctx,
| http.MethodGet,
| fmt.Sprintf("https://api.example.com/users/%d", id),
| nil, ) if err != nil {
| return User{}, err } resp, err
| := client.Do(req) if err != nil {
| return User{}, err } defer
| resp.Body.Close() var u User if
| err := json.NewDecoder(resp.Body).Decode(&u); err != nil {
| return User{}, err } return u, nil
| }
|
| I understand _why_ it 's more verbose (a lot of things are more
| explicit by design), but it's still hard not to prefer the
| cleaner Swift example. The success path is just three
| straightforward lines in Swift. While the verbosity of Go
| effectively buries the key steps in the surrounding boilerplate.
|
| This isn't to pick on Go or say Swift is a better language in
| practice -- and certainly not in the same domains -- but I do
| wish there were a strongly typed, compiled language with the
| maturity/performance of e.g. Go/Rust and a syntax a bit closer to
| Swift (or at least closer to how Swift feels in simple demos, or
| the honeymoon phase)
| tidwall wrote:
| Or this. func fetchUser(id int) (user User,
| err error) { resp, err :=
| http.Get(fmt.Sprintf("https://api.example.com/users/%d", id))
| if err != nil { return user, err }
| defer resp.Body.Close() return user,
| json.NewDecoder(resp.Body).Decode(&user) }
| jtbaker wrote:
| I'm conflicted about the implicit named returns using this
| pattern in go. It's definitely tidier but I feel like the
| control flow is harder to follow: "I never defined `user` how
| can I return it?".
|
| Also those variables are returned even if you don't
| explicitly return them, which feels a little unintuitive.
| ragnese wrote:
| I haven't written any Go in many years (way before
| generics), but I'm _shocked_ that something so implicit and
| magical is now valid Go syntax.
|
| I didn't look up this syntax or its rules, so I'm just
| reading the code totally naively. Am I to understand that
| the `user` variable in the final return statement is not
| really being treated as a value, but as a reference?
| Because the second part of the return
| (json.NewDecoder(resp.Body).Decode(&user)) sure looks like
| it's going to change the value of `user`. My brain wants to
| think it's "too late" to set `user` to anything by then,
| because the value was already read out (because I'm
| assuming the tuple is being constructed by evaluating its
| arguments left-to-right, like I thought Go's spec enforced
| for function arg evaluation). I would think that the
| returned value would be: `(nil, return-value-of-Decode-
| call)`.
|
| I'm obviously wrong, of course, but whereas I always found
| Go code to at least be fairly simple--albeit tedious--to
| read, I find this to be very unintuitive and fairly
| "magical" for Go's typical design sensibilities.
|
| No real point, here. Just felt so surprised that I couldn't
| resist saying so...
| jtbaker wrote:
| yeah, not really an expert but my understanding is that
| naming the return struct automatically allocates the
| object and places it into the scope.
|
| I think that for the user example it works because the
| NewDecoder is operating on the same memory allocation in
| the struct.
|
| I like the idea of having named returns, since it's
| common to return many items as a tuple in go functions,
| and think it's clearer to have those named than leaving
| it to the user, especially if it's returning many of the
| same primitive type like ints/floats:
|
| ``` type IItem interface { Inventory(id int) (price
| float64, quantity int, err error) } ```
|
| compared to
|
| ``` type IItem interface { Inventory(id int) (float64,
| int, error) } ```
|
| but feel like the memory allocation and control flow
| implications make it hard to reason about at a glance for
| non-trivial functions.
| Someone wrote:
| > My brain wants to think it's "too late" to set `user`
| to anything by then, because the value was already read
| out
|
| It doesn't set `user`, it returns the User passed to the
| function.
|
| Computing the second return value modifies that value.
|
| Looks weird indeed, but conceptually, both values get
| computed before they are returned.
| hocuspocus wrote:
| Not defending Go's braindead error handling, but you'll note
| that Swift is doubly coloring the function here (async throws).
| tarentel wrote:
| What is the problem with that though? I honestly wish they
| moved the async key word to the front `async func ...` but
| given the relative newness of all of this I've yet to see
| anyone get confused by this. The compiler also ensures
| everything is used correctly anyway.
| hocuspocus wrote:
| The problem is that that the Swift function signature is
| telling you that someone else needs dealing with async
| suspension and exception handling, clearly not the same
| semantics.
| tarentel wrote:
| In a sense it is telling someone else that yes, but more
| importantly, it is telling the compiler. I am not sure
| what the alternative is here, is this not common in other
| languages? I know Java does this at least. In Python it
| is hidden and you have to know to catch the exception.
| I'm not sure how that is better as it can be easily
| forgotten or ignored. There may be another alternative
| I'm not aware of?
| saghm wrote:
| And sometimes you even have to use the return value the way
| the function annoates as well instead of just pretending it's
| a string or whatever when it's not! Having the language tell
| me how to use things is so frustrating.
| neonsunset wrote:
| C# :) async Task<User> FetchUser(int id,
| HttpClient http, CancellationToken token) {
| var addr = $"https://api.example.com/users/{id}";
| var user = await http.GetFromJsonAsync<User>(addr, token);
| return user ?? throw new Exception("User not found"); }
| tarentel wrote:
| As someone who has been coding production Swift since 1.0 the
| Go example is a lot more what Swift in practice will look like.
| I suppose there are advantages to being able to only show the
| important parts.
|
| The first line won't crash but in practice it is fairly rare
| where you'd implicitly unwrap something like that. URLs might
| be the only case where it is somewhat safe. But a more fair
| example would be something like func
| fetchUser(id: Int) async throws -> User { guard let
| url = URL(string: "https://api.example.com/users/\(id)") else {
| throw MyError.invalidURL } //
| you'll pretty much never see data(url: ...) in real life
| let request = URLRequest(url: url) // configure
| request let (data, response) = try await
| URLSession.shared.data(for: request) guard let
| httpResponse = response as? HTTPURLResponse,
| 200..<300 ~= httpResponse.statusCode else { throw
| MyError.invalidResponseCode } //
| possibly other things you'd want to check
| return try JSONDecoder().decode(User.self, from: data)
| }
|
| I don't code in Go so I don't know how production ready that
| code is. What I posted has a lot of issues with it as well but
| it is much closer to what would need to be done as a start. The
| Swift example is hiding a lot of the error checking that Go
| forces you to do to some extent.
| Jtsummers wrote:
| I'm not familiar with Swift's libraries, but what's the point
| of making this two lines instead of one: let
| request = URLRequest(url: url) let (data, response) =
| try await URLSession.shared.data(for: request) //
| vs let (data, response) = try await
| URLSession.shared.data(from: url)
|
| That aside, your Swift version is still about half the size
| of the Go version with similar levels of error handling.
| tarentel wrote:
| The first one you can configure and it is the default way
| you'd see this done in real life. You can add headers,
| change the request type, etc. Likely, if you were making an
| actual app the request configuration would be much longer
| than 1 line I used. I was mostly trying to show that the
| Swift example was hiding a lot of things.
|
| The second one is for downloading directly from a URL and
| I've never seen it used outside of examples in blog posts
| on the internet.
| scottmf wrote:
| Thanks. What could possibly cause an invalid URL in this
| example though?
| Invictus0 wrote:
| @dang I think it's important that "fucking" remains in the title
| JKCalhoun wrote:
| (It certainly makes it easier to find the topic some time after
| when going back to search for it on HN.)
| mojuba wrote:
| It's a good article but I think you need to start explaining
| structured concurrency from the very core of it: why it exists in
| the first place.
|
| The design goal of structured concurrency is to have a safe way
| of using all available CPU cores on the device/computer. Modern
| mobile phones can have 4, 6, even 8 cores. If you don't get a
| decent grasp of how concurrency works and how to use it properly,
| your app code will be limited to 1 or 1.5 cores at most which is
| not a crime but a shame really.
|
| That's where it all starts. You want to execute things in
| parallel but also want to ensure data integrity. If the compiler
| doesn't like something, it means a design flaw and/or
| misconception of structured concurrency, not "oh I forgot
| @MainActor".
|
| Swift 6.2 is quite decent at its job already, I should say the
| transition from 5 to 6 was maybe a bit rushed and wasn't very
| smooth. But I'm happy with where Swift is today, it's an amazing,
| very concise and expressive language that allows you to be as
| minimalist as you like, and a pretty elegant concurrency paradigm
| as a big bonus.
|
| I wish it was better known outside of the Apple ecosystem because
| it fully deserves to be a loved, general purpose mainstream
| language alongside Python and others.
| ragnese wrote:
| > It's a good article but I think you need to start explaining
| structured concurrency from the very core of it: why it exists
| in the first place.
|
| I disagree. Not every single article or essay needs to start
| from kindergarten and walk us up through quantum theory. It's
| okay to set a minimum required background and write to that.
|
| As a seasoned dev, every time I have to dive into a new
| language or framework, I'll often want to read about styles and
| best practices that the community is coalescing around. I
| _promise_ there is no shortage at all of articles about Swift
| concurrency aimed at junior devs for whom their iOS app is the
| very first real programming project they 've ever done.
|
| I'm not saying that level of article/essay shouldn't exist. I'm
| just saying there's more than enough. I almost NEVER find
| articles that are targeting the "I'm a newbie to this
| language/framework, but not to programming" audience.
| astrange wrote:
| > The design goal of structured concurrency is to have a safe
| way of using all available CPU cores on the device/computer.
|
| That's parallelism. Concurrency is mostly about hiding latency
| from I/O operations like network tasks.
| isodev wrote:
| I really don't know why Apple decided to substitute terms like
| "actor" and "task" with their own custom semantics. Was the goal
| to make it so complicated that devs would run out of spoons if
| they try to learn other languages?
|
| And after all this "fucking approachable swift concurrency", at
| the end of the day, one still ends up with a program that can
| deadlock (because of resources waiting for each other) or exhaust
| available threads and deadlock.
|
| Also, the overload of keywords and language syntax around this
| feature is mind blowing... and keywords change meaning depending
| on compiler flags so you can never know what a code snippet
| really does unless it's part of a project. None of the safeties
| promised by Swift 6 are worth the burnout that would come with
| trying to keep all this crap in one's mind.
| hn-acct wrote:
| Do people actually believe that there are too many keywords?
| I've never met a dev irl that says this but I see it
| regurgitated on every post about Swift. Most of the new
| keywords are for library writers and not iOS devs.
|
| Preventing deadlock wasn't a goal of concurrency. Like all
| options - there are trade offs. You can still used gcd.
| isodev wrote:
| > Do people actually believe that there are too many
| keywords?
|
| Yes they do. Just imagine seeing the following in a single
| file/function: Sendable, @unchecked Sendable, @Sendable,
| sending, and nonsending, @conccurent, async, @escaping, weak,
| Task, MainActor.
|
| For comparison, Rust has 59 keywords in total. Swift has 203
| (?!), Elixir has 15, Go has 25, Python has 38.
|
| > You can still used gcd.
|
| Not if you want to use anything of concurrency, because
| they're not made to work together.
| saagarjha wrote:
| Task and MainActor are types.
| isodev wrote:
| So?
| jen20 wrote:
| So... they aren't keywords.
|
| Swift does indeed have a lot of keywords [1], but neither
| Task or MainActor are among them.
|
| [1]: https://github.com/swiftlang/swift-
| syntax/blob/main/CodeGene...
| isodev wrote:
| I never said they're keywords. Y'all way too focused on
| defending Apple at all cost.
| dagmx wrote:
| If you're including types, you'd hit the many hundreds if
| not thousands in most languages.
|
| It dilutes any point you were trying to make if you don't
| actually delineate between what's a keyword and a type.
| dagmx wrote:
| Most of your listed examples aren't keywords though.
| They're built in types or macro decorators.
| travisgriggs wrote:
| > Instead of callbacks, you write code that looks sequential [but
| isn't]
|
| (bracketed statement added by me to make the implied explicit)
|
| This sums up my (personal, I guess) beef with coroutines in
| general. I have dabbled with them since different experiments
| were tried in C many moons ago.
|
| I find that programming can be hard. Computers are very pedantic
| about how they get things done. And it pays for me to be explicit
| and intentional about how computation happens. The illusory
| nature of async/await coroutines that makes it seem as if code
| continues procedurally demos well for simple cases, but often
| grows difficult to reason about (for me).
| jesuslop wrote:
| That is the price you pay. If you refuse to pay you are left to
| express a potentially complex state machine in terms of a flat
| state-transition table, so you have a huge python cases
| statement saying on event x do this and on event y do that.
| That obscures evident state-chart sequentiality, alternatives
| or loops (the stuff visible in the good old flow-charts) that
| otherwise could be mapped in their natural language constructs.
| But yes, is not honest flow. Is a tradeoff.
| ChrisMarshallNY wrote:
| This looks like it's well-written and approachable. I'll need to
| spend more time reviewing it, but, at first scan, it looks like
| it's nicely done.
| dang wrote:
| (We don't have a problem with profanity in general but in this
| case I think it's distracting so I've de-fuckinged the title
| above. It's still in the sitename for those who care.)
| sebastianconcpt wrote:
| But how would you do what in Rust's Tokio is a `spawn_blocking`
| in Swift?
| cyberax wrote:
| Does it do any refcounting optimizations?
|
| Are there any reference counting optimizations like biased
| counting? One big problem with Python multithreading is that
| atomic RCs are expensive, so you often don't get as much
| performance from multiple threads as you expect.
|
| But in Swift it's possible to avoid atomics in most cases, I
| think?
| tombert wrote:
| I don't know a ton about Swift, but it does feel like for a lot
| of apps (especially outside of the gaming and video encoding
| world), you can almost treat CPU power as infinite and
| exclusively focus on reducing latency.
|
| Obviously I'm not saying you throw out big O notation or stop
| benchmarking, but it does seem like eliminating an extra network
| call from your pipeline is likely to have a much higher ROI than
| nearly any amount of CPU optimization has; people forget how
| unbelievably slow the network actually is compared to CPU cache
| and even system memory. I think the advent of these async-first
| frameworks and languages like Node.js and Vert.x and Tokio is
| sort of the industry acknowledgement of this.
|
| We all learn all these fun CPU optimization tricks in school, and
| it's all for not because anything we do in CPU land is probably
| going to be undone by a lazy engineer making superfluous calls to
| postgres.
| postsantum wrote:
| I worked on a resource-intensive android app for some years and
| it had a good perfomannce boost after implementing
| parallelization. But mostly for old shitty devices
|
| On latest phones it's barely noticiable
___________________________________________________________________
(page generated 2025-12-30 23:00 UTC)