reactive
reactive copied to clipboard
add withRunner method to EventStream to switch event flow context
it could be used to execute events on thread pools, actors, etc.
For example can be used to switch running context to UI thread in android
Perhaps we should use scala.concurrent.ExecutionContext?
Maybe the Runner
type should simply be replaced by something like Runnable => Unit
. That would simplify the type signature so that it doesn't need to be an inner type. It would also fit pretty well with the signature of many existing constructs like ThreadPool
and ExecutionContext
events.withRunner{ ExecutionContext.global execute _ }
Maybe we just need an async
method that takes an implicit
ExecutionContext.
Anyone know how prepare
is meant to be used?
On Wed, Jul 10, 2013 at 4:29 PM, Dylan Halperin [email protected]:
Maybe the Runner type should simply be replaced by something like Runnable => Unit. That would simplify the type signature so that it doesn't need to be an inner type. It would also fit pretty well with the signature of many existing constructs like ThreadPool and ExecutionContext
events.withRunner{ ExecutionContext.global execute _ }
— Reply to this email directly or view it on GitHubhttps://github.com/nafg/reactive/pull/74#issuecomment-20770744 .
In that case wouldn't async
be essentially the same as the current nonblocking
method?
The value I see in using withRunner(runner: Runnable=>Unit)
is that it could fit into non-scala things as well. For example, Java's SWT Display
class has void asyncExec(Runnable runnable)
and void syncExec(Runnable runnable)
which cause a task to be run on the UI thread, which is sometimes mandatory. I imagine @Yarikx has similar motivations w.r.t. Android.
How about this:
def nonblocking(implicit ec: ExecutionContext): EventStream[T]
def withRunner(runner: Runnable => Unit): EventStream[T]
@dylemma see my comment on the google group.
Here's the interface of ExecutionContext (docs removed):
trait ExecutionContext {
def execute(runnable: Runnable): Unit
def reportFailure(t: Throwable): Unit
def prepare(): ExecutionContext = this
}
If you change execute
to apply
(and make ExecutionContext extend Function1), essentially ExecutionContext is a Runnable => Unit, with some extra methods (one mandatory). We could even make a converter:
implicit def fromFunc(f: Runnable=>Unit) = new ExecutionContext {
def execute(r: Runnable) = f(r)
def reportFailure(t: Throwable) = t.dumpStack // etc.
}
So you can easily write an ExecutionContext for SWT a/syncExec, or the AWT event thread, or anything.
Fair enough. Bonus points if that implicit doesn't need to be explicitly imported. I'm not sure, but maybe if it's defined in EventStream it may work without an import...
On the other hand, if you're writing a function literal, in order to trigger an implicit conversion you have to write the types explicitly, but if you call the conversion explicitly the types can be inferred. So it's not really buying anything.
1a. implicit val nowRunner: ExecutionContext = {r: Runnable => r.run() }; val es2 = es.async 1b. val es2 = es.async((: Runnable).run()) 2a. implicit val nowRunner = fromFunc(.run()); val es2 = es.async 2b. val es2 = es.async(fromFunc{r => r.run() })
I'm thinking to address this by using an ADT isomorphic to how RxJava handles success, failure, and END with separate execution paths. Basically foreach(A => Unit) is short for something like observe(Event[A] => Unit) where type Event = Success[A] | Failure | End.