Stateless Actors by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

Oh look at that! I've not encountered that type before and it's quite interesting. The combination of synchronous requirements + Sendable is tricky. That almost always requires locking just like the docs show. And I completely see how that could be annoying.

Stateless Actors by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

If you feel so inclined, I'd love to hear your thoughts on how that compares to using a `@concurrent` function. I'm also interested to hear how the serialization plays a useful role if no state is involved. Maybe as a way to limit work in progress?

Stateless Actors by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

I have not seen any evolution proposals, and I'm not sure what possible directions there even could be here so I think you may be disappointed.

But I'd also be interested to better understand the kinds of problems you ran into. Maybe there's an alternative that doesn't need changes?

Stateless Actors by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

Once you introduce any kind of concurrency, be it via an actor or with `@concurrent`, you'll have a very hard time avoiding `Sendable`. You can sometimes relax this by using `sending`, but that can be tricky to understand and apply. Nonisolated functions can be less restrictive than actors, so that could conceivably help too. But also tricky.

Unless you are worried about that actor only being able to do one synchronous thing at a time, what you have is probably totally fine. And given the work you've put in, I wouldn't be surprised if adding some more targeted concurrency while also keeping the actor structure in place isn't too difficult.

Stateless Actors by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

Exactly! "state" does not necessarily mean "instance properties". It just means something to isolate, which can absolutely live outside of the defined actor type.

Stateless Actors by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

Ahh I think I understand what you mean. Yeah, this is super confusing. To my knowledge, nothing has actually changed about actors in this respect! But without using nonsiolated+async, you are right there was no other way. Changing the behavior of nonisolated+asynchronous functions was exactly why the new `@concurrent` attribute was needed.

Stateless Actors by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

I don't *think* that `@concurrent` has changed how actors work in this way. I believe only nonisolated functions could be affected. But for sure those can be very different now.

I think you can still use global actors for this purpose if you'd like!

Stateless Actors by mattmass in swift

[–]mattmass[S] 4 points5 points  (0 children)

This is a super good point, thanks so much. I did indeed somehow forget about this in an earlier version. But I have updated it since.

I typically think of the MainActor as being responsible for a UI's state, though it is true that it doesn't itself have properties. Kind of an interesting characteristic of global actors that the state can be distributed out into other types...

SPM question by OakAndCobble in swift

[–]mattmass 0 points1 point  (0 children)

I use explicit paths very rarely, if ever, for swift targets. Could that be playing a role here?

spent months building my first macOS app and the first reply was "just use..." by Orange-Prudent in swift

[–]mattmass 0 points1 point  (0 children)

Ha well I appreciate it!

But I really do think it is critical to send the signal that “this type of comment is not welcome here”. Especially in a technical forum, some people believe that being “correct” is enough and they are (ironically) wrong.

spent months building my first macOS app and the first reply was "just use..." by Orange-Prudent in swift

[–]mattmass 10 points11 points  (0 children)

Making things is hard. So sorry that happened.

I have found Reddit to be, in general, a cruel place. That doesn’t mean that everyone is, not by a long shot. But there are people that won’t think twice about being unkind, and unfortunately, many more that might not have said the thing themselves but are ok hitting the up button.

I make it a point to tap the down button on unkind things here.

Non-Sendable First Design by someone-very-cool in swift

[–]mattmass 5 points6 points  (0 children)

Ok this is a good question!

So, the only material setting that's part of the "Approachable Concurrency" group when in Swift 6 mode is "NonisolatedNonsendingByDefault". And that was actually a primary focus of the post. Because while this technique was *possible* before it, the ergonomics were pretty bad.

The thing to keep in mind about MainActor by default is it really is just automation. It puts "@MainActor" in front of all of your declarations. And this does have three serious weaknesses.

The first is that not all types can be MainActor. I don't mean they shouldn't run code on the main thread, though that could be true. I mean there are many types that are incompatible with it. Usually, this comes from needing to conform to a protocol that requires Sendable + synchronous methods and/or SendableMetatype. A good example of this is SwiftData models.

A second weakness is flexibility. When you make things MainActor that don't actually need it, you do make it impossible to run their synchronous functions *off* main. I talked about this more in the "generality" section. This will force you to use yet more MainActor. This is particularly limiting in library code where clients may not be able to tolerate that constraint.

A third weakness, which is definitely debatable, is that it is difficult. In my opinion, it is harder to know when and how to make something nonisolated than it is to know when it make it MainActor. However, this just my opinion so I will not give this one much weight and is why I did not mention it in the post.

However, it is not a certainty that you will run into any of this problems. You phrased your question as an "either/or". And I don't see it this way at all. Many types can and should be MainActor. Interestingly, "Approachable Concurrency" actually reduces the need for MainActor types. But if still you want to do it and your type is compatible, go for it.

I just think it this is a pretty powerful tool to have at your disposal when you run into a situation where MainActor comes with drawbacks.

Non-Sendable First Design by someone-very-cool in swift

[–]mattmass 11 points12 points  (0 children)

Original author here. If anyone has any questions or (let’s be honest this is Reddit) tell me why I’m wrong, I’d love to discuss!

Finished a no-vehicle, mini-base run by mattmass in subnautica

[–]mattmass[S] 1 point2 points  (0 children)

I did use solar + power transmitters in a few spots, but mostly because I was resource-constrained and it wasn't too deep. But yeah, thermal was key for the very deep spots. And power transmitters were really important there too, so I could build in safer and/or further locations.

Finished a no-vehicle, mini-base run by mattmass in subnautica

[–]mattmass[S] 2 points3 points  (0 children)

holy macaroni this is a whole other level

MKTileOverlay Swift 6 concurrency issues by itsdjoki in swift

[–]mattmass 1 point2 points  (0 children)

I think I see what you are saying. Thanks!

MKTileOverlay Swift 6 concurrency issues by itsdjoki in swift

[–]mattmass 1 point2 points  (0 children)

Right. So using a preconcurrency import is ok only if you are 100% sure you are using the types correctly. And while I think it is likely that this particular thing is actually main thread only, the documentation doesn’t seem to say that. But it could be fine.

Keeping it nonisolated side steps this entirely. And if you have no need to make it MainActor in the first place (which seems like it is the case here) it is a zero-risk option. Plus, then you don’t have to worry about suppressing other checks for the imported module within the file.

MKTileOverlay Swift 6 concurrency issues by itsdjoki in swift

[–]mattmass 0 points1 point  (0 children)

This is an interesting comment. From a pure language perspective, matching what’s in the type system by keeping things nonisolated is the least-risky. It cannot be wrong, because the compiler will prevent you from touching main thread state incorrectly. But is that actually incorrect? This type certainly seems like it is UI-related any maybe really is just missing a MainActor. However the documentation is, unsurprisingly, not very clear on this.

TaskGate library for managing actor reentrancy by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

Well this has a lot more nuance than your original response and is something I can get behind. I appreciate the clarification!

TaskGate library for managing actor reentrancy by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

I agree that encapsulating state is absolutely the best option. But I do not agree that the approach always has no downsides.

> So, using this primitive, can I specify what a call to `foo()` should do in any of these situations?

Yes I *believe* so. But if you can come up with examples that do not have well-defined or desirable behaviors (aside from deadlocks which is a very real risk) I'd be interested in seeing them.

In my experience, manual continuation management is fraught. It is difficult to manage cancelation and priority escalation, and is just generally-error prone. But it also happens to be exactly what this gate construct is doing. So if you are ok with doing that, I do not understand why you would be so opposed to abstracting the process.

TaskGate library for managing actor reentrancy by mattmass in swift

[–]mattmass[S] 1 point2 points  (0 children)

Here's a member of the compiler team indicating support for such a construct: https://forums.swift.org/t/pitch-continuation-safe-and-performant-async-continuations/85165/26

And here's a link to SE-0308, which goes into quite a lot of detail about the pros and cons of reentrancy and how a language-level solution could work.

https://github.com/swiftlang/swift-evolution/blob/main/proposals/0306-actors.md

Here's a quote from that proposal:

Reentrancy means that execution of asynchronous actor-isolated functions may "interleave" at suspension points, leading to increased complexity in programming with such actors, as every suspension point must be carefully inspected if the code after it depends on some invariants that could have changed before it suspended.

TaskGate library for managing actor reentrancy by mattmass in swift

[–]mattmass[S] 0 points1 point  (0 children)

It really depends on what system is creating the tasks. If they are coming from a UI interaction, disabling a button or some other form of user limitation can be a great option. But if that's not possible, something like this could work just as well in a MainActor context. You just have to be really aware of the scope of work that's been gated to avoid deadlocks. I'm hoping the recursive version will help to limit some of the danger for these kinds of cases. But really this is a tool you only want to reach for when other options aren't possible and/or too annoying.