r-lib / R6

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

Print methods and the environment pane for R6 instances #217

Closed mattwarkentin closed 3 years ago

mattwarkentin commented 3 years ago

Hi @wch,

I just have a couple (probably) simple questions about the R6 print method and also how to modify how an R6 object is displayed in the Environment pane in RStudio.

For the first part, if I didn't feel like designing a whole custom print method for my R6 class, is there a way to modify how a public method or field is presented?

I see that print.R6 is:

function (x, ...) 
{
    if (is.function(.subset2(x, "print"))) {
        .subset2(x, "print")(...)
    }
    else {
        cat(format(x, ...), sep = "\n")
    }
    invisible(x)
}

So for example, the default way an array is presented is:

library(R6)

R6Class(
  "foo",
  public = list(
    aaa = array(runif(1000), dim = c(10, 10, 10))
  )
)$new()
#> <foo>
#>   Public:
#>     aaa: 0.304002771619707 0.662762265419587 0.892216055653989 0. ...
#>     clone: function (deep = FALSE) 

But I would like to customize it to show the dimensions, so I thought a new S3 method for format would work, but it doesn't. Moving format.array to the global environment didn't help either.

library(R6)

R6Class(
  "foo",
  public = list(
    aaa = array(runif(1000), dim = c(10, 10, 10)),
    format.array = function(x, ...) {
      "[10 x 10 x 10]"
    }
  )
)$new()
#> <foo>
#>   Public:
#>     aaa: 0.887192418565974 0.142298782011494 0.786215221509337 0. ...
#>     clone: function (deep = FALSE) 
#>     format.array: function (x, ...)

Can this be achieved without defining a custom print method (such as by writing a new S3 method for format)?


Second, it's my understanding that RStudio uses str() S3 methods to present objects in the Environment pane. But no matter what I've tried I can't seem to get my R6 object to show up differently in the pane. Is this possible? If so, any suggestions on how to achieve this?

gaborcsardi commented 3 years ago

This is how to create a print method: https://r6.r-lib.org/articles/Introduction.html#printing-r6-objects-to-the-screen

For str you can create an "external" method I believe, i.e.

str.foo <- function(object, ...) {
  ...
}
mattwarkentin commented 3 years ago

I figured I would be able to avoid creating a print method if I wrote format methods. Shouldn't that work since format is what is called by print.R6?

gaborcsardi commented 3 years ago

That works as well:

foo <- R6::R6Class("foo",
  public = list(
    format = function(...) "foobar"
  )
)

foo$new()
#> foobar

Created on 2020-10-21 by the reprex package (v0.3.0)

mattwarkentin commented 3 years ago

Right. But I am not interested in changing how foo is printed, I was hoping to change how members of foo are printed when foo itself is printed (e.g. how an array member is printed).

gaborcsardi commented 3 years ago

I might be missing something, but can't you just create a format method for your class that formats its members differently?

mattwarkentin commented 3 years ago

For sure. That's my fallback plan. Just create a new print method for my class that formats encapsulated members differently than the default print.R6 method. I was just wondering whether I could just define new format methods for members that appear in my class and piggy-back on R6's print method. I'm probably over-engineering the solution...

mattwarkentin commented 3 years ago

Alright after looking over R6:::print.R6 some more, the issue was with my mental model. format isn't called on members of the R6 object, it is only called on the R6 object itself.

mattwarkentin commented 3 years ago

For posterity, I think the issue I was facing was beyond my control. In particular, one expects that RStudio uses str() to figure out how to display an object in the Environment pane, but it seems like RStudio has been setup to avoid calling str() on objects of type environment, such as a R6 object (see rstudio/rstudio#7147 and rstudio/rstudio#796).

So this is why I couldn't get my R6 class to show up differently in the pane. Maybe to be solved down the line.