r-lib / R6

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

Can super be made a top-level element of R6 objects? #101

Closed richierocks closed 7 years ago

richierocks commented 7 years ago

If I have an R6 class

thing_factory <- R6Class(
  "Thing",
  public = list(
    do_something = function() {
      print("the parent do_something method")
    }
  )
)

and another class that inherits from it, overriding functionality

child_thing_factory <- R6Class(
  "ChildThing",
  inherit = thing_factory,
  public = list(
    do_something = function() {
      print("the child do_something method")
    }
  )
)

then I can call the parent method using

a_child_thing <- child_thing_factory$new()
a_child_thing$.__enclos_env__$super$do_something()

It would be more natural to write

a_child_thing$super$do_something()

I don't have a good feel for how much effort it would be to make super a top-level element of inherited classes, or what it would break. Does this change seem feasible?

wch commented 7 years ago

I think it could be done, but then it would differ from self and private. The original intent was objects would be encapsulated so that super would not be visible from the outside. (Note that __enclos_env__ is only there because it's necessary for cloning (IIRC) -- it's not intended to be used by application code.) Also, both public and private members from the superclass are put in super, so doing this would expose them.

What's the use case you have in mind?

gaborcsardi commented 7 years ago

@richierocks If you subclass needs to provide access to super methods in its API, then I think it is better to do it explicitly.

richierocks commented 7 years ago

I don't have a specific use-case in mind right now. I just noticed that it was a thing you can do, but only in a slightly weird way. And I have vague flashbacks to when I was programming C# that it was occasionally useful to make an object pretend it is its super-class.

@gaborcsardi

If you subclass needs to provide access to super methods in its API, then I think it is better to do it explicitly.

I guess you mean explicit conversion to the parent object? This sound like a good solution, but currently as doesn't seem to work properly for R6 classes. In the example from the original post:

as(a_child_thing, "Thing")
## Error in as(a_child_thing, "Thing") : 
##   internal problem in as(): “ChildThing” is(object, "Thing") is TRUE, but the metadata asserts that the 'is' relation is FALSE

If that worked OK, to call the super method I could do

as(a_child_thing, "Thing")$do_something()

I'd be happy with that as a solution.

gaborcsardi commented 7 years ago

No, that's not what I mean. What I mean is that if A inherits from B and B's API includes a call to some (shadowed) method of A, then B should offer a method that does it. I believe this is possible with R6.

wch commented 7 years ago

You could add a method that simply returns the super object, like this:

library(R6)
A <- R6Class("A",
  public = list(
    f = function() "Thing A"
  )
)

B <- R6Class("B",
  inherit = A,
  public = list(
    f = function() "Thing B",
    super_ = function() super
  )
)

b <- B$new()
b$super_()$f()
# [1] "Thing A"

You could also do away with the parens if you made it an active binding.

Note that the super object is not exactly an R6 object. It is similar, but there are some differences. For example, it doesn't have a class attribute. It also combines all the public, private, and active members.