gh123man / Async-Channels

Channels for Swift concurrency
MIT License
103 stars 6 forks source link

Would it be possible to add buildEither to SelectCollector? #8

Closed Kuniwak closed 3 months ago

Kuniwak commented 4 months ago

Hello,

I would like to discuss the possibility of accepting a PR to add the buildEither method to SelectCollector.

The use case I am about to explain is quite specialized, and I acknowledge that almost no one else using this library, except for me, may ever need this functionality.

I am a developer trying to create a CSP library in Swift similar to JCSP. JCSP is a library that implements some CSP functionalities in Java. One of the features of CSP is external choice, which allows the selection of channels that can either send or receive messages. I plan to use select for selecting channels that can send or receive messages. However, in implementing this external choice, using Async-Channel gives too much flexibility, making verification difficult. Therefore, I plan to publish the following code with restrictions to reduce this flexibility:

public indirect enum Process<Ev: Hashable, Value> {
    // ...
    case externalChoice([ChannelID: ChannelHandler<Ev, Value>])
    // ...
}

public enum ChannelHandler<Ev: Hashable, Value> {
    case receive((Value) -> Process<Ev, Value>)
    case send(Value, Process<Ev, Value>)
}

public func interpret<Ev: Hashable, Value>(process: Process<Ev, Value>, onContext context: Context<Ev, Value>) async throws {
    switch process {
    // ...
    case .externalChoice(let events, let channels):
        let channels = channels.map { (channel: context.channelMap[$0.key]!, handler: $0.value) }
        var nextProcess: Process<Ev, Value>? = nil
        await select {
            any(channels) { entry in
                let channel = entry.channel
                let handler = entry.handler

                switch handler { // This line causes an error because SelectCollector does not have buildEither
                case .receive(let fn):
                    receive(channel) { value in
                        nextProcess = fn(value!)
                    }
                case .send(let value, let process):
                    send(value, to: channel) {
                        nextProcess = process
                    }
                }
            }
            try await interpret(process: nextProcess!, onContext: context)
        }

This code encounters an error because there is a switch statement inside the select, and SelectCollector does not have the buildEither method. Therefore, would it be possible to add buildEither to SelectCollector?

If you are open to this addition, I will prepare a PR. I would appreciate your feedback on this matter.

gh123man commented 4 months ago

Are there any possible drawbacks to implementing buildEither (changes/breakage to the existing API)? From a surface look I can't see any - so with that in mind I would be in favor of adding support for this. I am happy to review and test the PR.

Kuniwak commented 4 months ago

Are there any possible drawbacks to implementing buildEither (changes/breakage to the existing API)? From a surface look, I can't see any

I agree. Based on my reading of SE-0289, adding the buildEither method seems to be a purely additive change because it only increases the syntax available within the resultBuilder. The SelectHandler generated by the existing syntax remains unchanged. Therefore, I believe the behavior of select statements that did not previously cause compilation errors will not be affected by the addition of the buildEither method.

Kuniwak commented 4 months ago

I sent a pull request about it at #10.