Skip to content
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

Error when making generator a method of an R6 class #30

Closed
dfalbel opened this issue Nov 16, 2020 · 5 comments
Closed

Error when making generator a method of an R6 class #30

dfalbel opened this issue Nov 16, 2020 · 5 comments

Comments

@dfalbel
Copy link
Contributor

dfalbel commented Nov 16, 2020

Not sure if you want to support that, but the following does not work:

g <- R6::R6Class(
  "generator",
  public = list(
    x = coro::generator(function() {
      for (i in 1:10)
        yield(i)
    })
  )
)

instance <- g$new()
instance$x
#> <generator>
#> Error in env_get(fn_env(x), "fn", inherit = TRUE): argument "default" is missing, with no default
instance$x()
#> Error in dots_split(..., .n_unnamed = 0:1): object '_parent' not found

R6 changes the function environments to the class env. It's probably also removing important information from the generator function.

@lionel-
Copy link
Member

lionel- commented Nov 26, 2020

Maybe there should be a way of preventing R6 from treating a function as a method? For example functions wrapped in I(). And maybe functions with a custom class should automatically get that treatment?

Relatedly, maybe R6Class() should issue a warning when methods don't inherit from the current environment? Changing their environments in the background seems a bit unsafe.

What do you think @wch?

@lionel-
Copy link
Member

lionel- commented Nov 26, 2020

In the meantime you can wrap the generators like this @dfalbel

R6::R6Class(
  "generator",
  private = list(
    generators = list(x = coro::generator(function() {
      for (i in 1:10)
        yield(i)
    }))
  ),
  public = list(
    x = function() private$generators$x()
  )
)

@dfalbel
Copy link
Contributor Author

dfalbel commented Nov 27, 2020

Thanks! That works great for me!

@wch
Copy link
Member

wch commented Dec 1, 2020

With R6Class, all functions that are part of public or private are assumed to be methods, and their environment is modified when the class is instantiated.

R6 does have the notion of members function that are not methods, though. They must be assigned either in initialize() or after the object is created. So you could so something like this:

num_generator <- coro::generator(function() {
  for (i in 1:10)
    yield(i)
})

Generator <- R6::R6Class(
  "Generator",
  public = list(
    initialize = function() {
      self$x = num_generator
    },
    x = NULL
  )
)

(You could also call coro::generator(function() ....) inside of initialize(), but that would create a new factory for each instance, which is less efficient.)

The difference between methods and non-method functions is recognized when the object is cloned; in the cloned object, methods will have their environment changed (so they can find the correct self and private), but non-method functions will not have their environment changed.

@lionel-, the idea of using I() to mark non-method functions is interesting. Want to file an issue on R6 so it can be discussed there? Or maybe the current behavior just needs to be better documented.

@lionel-
Copy link
Member

lionel- commented Dec 2, 2020

Thanks for your input @wch!
I've filed an issue for I() at r-lib/R6#222.

Closing this since it's not a coro issue.

@lionel- lionel- closed this as completed Dec 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants