I’m writing a coroutine builder that is basically
async
,
produce
, and
actor
all in one. Internally the coroutine can send on one channel, receive on another (of a different type), and return a value. Externally the builder returns an object that has a
SendChannel<O>
, a
ReceiveChannel<I>
, and a
Deferred<R>
.
The implementation looks something like:
fun <I, O, R> CoroutineScope.foo(block: suspend FooScope<I, O>.() -> R): Foo<I,O,R> {
val inputs = Channel<I>()
val outputs = Channel<O>()
val result = async {
val scope = object : FooScope<I, O>,
ReceiveChannel<I> by inputs,
SendChannel<O> by outputs {}
return@async scope.block()
}
result.invokeOnCompletion { cause -> inputs.cancel(cause) }
result.invokeOnCompletion { cause -> outputs.cancel(cause) }
inputs.invokeOnClose { cause -> result.cancel(cause) }
outputs.invokeOnClose { cause -> result.cancel(cause) }
return object : Foo<I, O, R> {
override val inputs get() = inputs
override val outputs get() = outputs
override val result() = result
}
}
This definitely isn’t ideal – everything has to be manually wired up to cancel everything else, so I would love to find a simpler way to write it.