-
Notifications
You must be signed in to change notification settings - Fork 86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-blocking handling of results #163
Conversation
Just to be clear about what this package lets you do: library(promises)
plan(multiprocess)
f <- future({ ... })
# traditional API
then(f, function(value) {
# Save it in the global environment
result <<- value
message("Your job is complete")
})
# promise-pipe API
f %...>% head() %...>% View() This entire script would execute very quickly regardless of how long the future expr takes to execute, leaving you back at the R prompt. Whenever the future finishes executing, then you would see the |
Thanks Joe. I'm pretty sure I understand your objectives and suggestions here and from a first look at it I believe I agree with this plan/strategy. I'll try to do give some good thoughts in the next coming days to make sure I understand the ins-and-outs and what implications etc it'll have. Then we can vchat. |
In order for the S3 method library("promise")
library("future") the future package would have to Import If so, then this comes down to the usual "I wanna avoid heavy dependency" problem. Yes, I would prefer avoiding to force additional directly dependencies on code/users using futures (i.e. promise + R6, Rcpp, later, rlang, magrittr, and BH, totaling 14 The only obvious solution I see is adding a dependency on a new very lightweight This corresponds to my ideas on having a lightweight What do you think? What can be done? |
Hi Henrik, thanks for taking a look!
I admire and relate to your goal of keeping dependencies minimal, and I also try to keep native code out of most packages. Thanks for the reminder. Some thoughts, in case
|
Fingers type GiB's automatically these days - too much of those at UCSF (or actually TiB's and soon PiB's). I would certainly be happy with a Error: package or namespace load failed for 'future`:
object 'as.promise' not found whilst loading namespace 'future' at install time (or at load time if we install with It does indeed work if I just use:
in However, that would require #' @importFrom promises as.promise
#' @importFrom future future as.promise.Future
#' @export
call_me <- function(x, ...) {
f <- future(x)
as.promise(f)
} it will fail with: Error in UseMethod("as.promise") :
no applicable method for 'as.promise' applied to an object of class "c('SequentialFuture', 'UniprocessFuture', 'Future', 'environment')" (This is illustrated with mockup It might be a good-enough solution, but at the same time I'm not sure if it's a "hack" not to declare an S3 method and if that might break in a future version of R. I need to dive back into the R internals for namespaces and S3 dispatching, which I haven't done in a very long time, in order fully understand what is legal, possible, and dangerous. AFAIK, the just-export-and-dont-declare the S3 method has been around for a long time and before namespaces were introduced, so I think there's lots of code / CRAN packages that would break if it stopped being supported. A completely different route would be to add a package #' @importFrom promises as.promise
#' @importFrom future resolved value
#' @export
as.promise.Future <- function(x, ...) {
[...]
} (This is illustrated with mockup Brain is getting mushy - too long working day. I'll try to think more about this. |
Hi again. I've been looking more into this one in different ways. Adding a lightweight, no-dependency, standalone future.api package would be an option that could be added to I've also looked back into the few ideas I had above, e.g. a future.promises package that would depend on both future and promises, which means user would do |
I'm happy to have promises suggest future, at least for the time being. We can always switch to something else later if it becomes necessary, without (too) much pain. |
That's great to hear. I'll try to follow your use cases (promises alone and shiny) closely. Feel free to shoot me issues and other feature requests (preferable less tricky than this one). Also, if you've got package tests that I can run (revdepcheck but also standalone), please let me know. The other day I had some troubles testing promises in batch mode, which I assume is because I someone needs to give control back to the event loop. |
Really, the only thing I don't like about the integration with future right now is that I have to poll the future for completion (using |
You mean without polling? A mechanism where at the very end of the future evaluation the "worker" pushes a notification? If so, that has to go in under the Optional/Extended Future API since not all possible future workers will be capable of that/such channels of communication. |
(Please DO NOT merge, for discussion only. It's possible the promises package will be renamed in the next few days.)
I have two goals with this PR, one of which benefits
future
users and one of which benefitspromises
/Shiny users.future
users to be able to be notified when their jobs are complete, without blocking or having the user manually remember to go poll for results. This would allow e.g. a message to be popped up to show that a large background job is done. Currently this can be accomplished using thelater
package to implement a polling loop, but in the future it could be made part of the Extended API so some future backends could implement using e.g. background threads or file descriptors.promises
(especially with Shiny) be able to use Future seamlessly as a promise. Until now I have accomplished this by implementingas.promise.Future
in thepromises
package, but this feels backwards to me, like having DBI bundling the RMySQL implementation.I was originally going to send you separate PRs, with the first one just implementing goal 1 without foisting any dependency on
promises
on you, in case you don't want to take a position on promises. This would look like anonResolved(future, callback)
function. But as I set to implementing this function, I realized that it's a disservice to you and your users to implement something that is just like a promise, but worse.then(future, callback)
is no harder to learn or type thanonResolved(future, callback)
, and it provides many additional benefits like well-structured error handling, composing together multiple tasks in serial or parallel, the fact that it is a first-class object (and thus be reused, passed into functions, returned from functions), and more.So now I am advocating for a stronger position than before, that you consider taking a PR like this one, and let goal 2 subsume goal 1, since it automatically provides a better version of it. I would like to spend some time with you talking more about promises, and after that if you prefer to have an arms-length relationship with them rather than taking a direct dependency, then I'd be happy to implement another PR that provides
onResolved
and not actual promises.