Skip to content

Instantly share code, notes, and snippets.

@couchdeveloper
Created February 6, 2024 12:53
Show Gist options
  • Save couchdeveloper/b465d02d585d2564a7431deee70e2d42 to your computer and use it in GitHub Desktop.
Save couchdeveloper/b465d02d585d2564a7431deee70e2d42 to your computer and use it in GitHub Desktop.
struct Effect<Context: Sendable>: Identifiable {
private var f: @Sendable (Context) async -> Void
var id: UUID
init(id: UUID, operation: @escaping @Sendable (Context) async -> Void) {
self.id = id
self.f = operation
}
func invoke(_ context: Context) async -> Void {
await f(context)
}
}
actor TestActor<State, Event> {
private var state: State
private let _send: (isolated TestActor, Event) -> Void
private var a: [UUID: Task<Void, Never>] = [:]
struct Proxy {
weak var actor: TestActor?
init(_ actor: TestActor? = nil) {
self.actor = actor
}
func removeTask(with id: UUID) async {
if var actor = actor {
await Proxy.removeTask(with: id, actor: actor)
}
}
private static func removeTask(with id: UUID, actor: isolated TestActor) {
let task = actor.a.removeValue(forKey: id)
task?.cancel()
}
}
init(
initialState: State,
transform: @escaping @Sendable (inout State, Event) -> [Effect<TestActor.Proxy>]
) {
self.state = initialState
_send = { actor, event in
actor.assertIsolated()
let effects = transform(&actor.state, event)
let proxy = Proxy(actor)
effects.forEach { effect in
let id = effect.id
actor.a[id] = Task {
actor.assertIsolated()
await effect.invoke(proxy)
await proxy.removeTask(with: id)
}
}
}
}
func send(event: Event) {
_send(self, event)
}
}
@couchdeveloper
Copy link
Author

couchdeveloper commented Feb 6, 2024

Ideally, the actor should not depend on a's type. That is, the initialisers would be in fact generic and would deduce the type of a from the type Effect. For example, the key is set to type UUID. However, an Effect might want to use any other hashable type. Or, suppose Effect does not conform to Identifiable at all. In this case, the _send(_:) closure would not use a collection at all to manage the tasks, it would not even create a task to wrap the operation.

So, making this available, it then would require, that a becomes a local variable defined in the initialisers, only when needed and which get captured as var in the _send(_:) closure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment