r-lib / R6

Encapsulated object-oriented programming for R
https://R6.r-lib.org
Other
404 stars 56 forks source link

Get class definition of R6 instance? #144

Closed epatters closed 6 years ago

epatters commented 6 years ago

Is it possible to retrieve the class definition (object of class "R6ClassGenerator") of an R6 instance? I can't figure out how. For comparison, it is possible with R5 instances. It would be useful for introspection/reflection purposes.

wch commented 6 years ago

In general, no. But you could create a method that returns the class R6ClassGenerator object, or perhaps in the initializer add it as a an attribute to self.

ju-rgen commented 4 years ago

How can the class added in the initializer? At this point of time the Class seems not to be defined - when I used code like class <- in the initialize function, this field was empty after creation of an object.

A general method to get the class for an instance would be really helpful for advanced programming.

wch commented 4 years ago

@ju-rgen If you're looking for the name of the class, you can use class(self) in the initialize method.

If you want the class generator object, it's not directly available from an instance of the class. The idea for R6 objects is that they're lightweight and can stand alone. If an R6 object (an instance) holds a reference to the generator object, then:

gaborcsardi commented 4 years ago

Nevertheless, if you really want to do this, you can create a factory function, that creates the class, and then an object from it, that refers to the class. E.g. something like this:

factory <- function(...) {
  Class <- R6::R6Class(
    public = list(
      initialize = function() private$class <- Class,
      get_class = function() private$class
    ),
    private = list(class = NULL)
  )

  Class$new(...)
}

o1 <- factory()
o1$get_class()

You'll probably want to cache the class generator. Also, if you want to provide the class as well, e.g. to be able to inherit from it, then it'll get a bit more difficult I guess.

wch commented 4 years ago

@gaborcsardi thanks for the example. A slight tweak to it, though: in your version, the generator is created every time factory() is called, which means that each instance will have its own generator, which uses more memory than necessary. They can share the same generator object if you do it this way:


factory <- local({
  Class <- R6::R6Class(
    public = list(
      initialize = function() private$class <- Class,
      get_class = function() private$class
    ),
    private = list(class = NULL)
  )

  Class$new
})

o1 <- factory()
o1$get_class()

Or, if you don't like the factory part and prefer for the user to call Class$new(), you can return a generator object which is wrapped in a local():

Class <- local({
  MyClass <- R6::R6Class(
    public = list(
      initialize = function() private$class <- MyClass,
      get_class = function() private$class
    ),
    private = list(class = NULL)
  )

  MyClass
})

o1 <- Class$new()
o1$get_class()
gaborcsardi commented 4 years ago

Yes, that's what I meant by "caching the class generator". Thanks!