Get the name of object in R when writing print method

I'd like to be able to have a print method reference the name of the object, but I'm just not sure if this is possible?

Looking at How to get the name of an object in R and then subsequently

meta programming - want to refer to users name for object in print method - #2 by nirgrahamuk (which is my own question :sweat_smile: )

This seems to work outside of a print S3 method. Which is on one hand, fascinating, on the other hand, a real shame.

Looking at r - Getting the object name for S3 print method failing - Stack Overflow it seems like this just isn't possible? I feel like I've seen some magic code in the tidyverse that does this however. But I can't recall it at the moment (and it also just might not exist).

Here's a reprex that hopefully demonstrates the issue:

my_info <- function(x, env = environment()){
  obj_name <- deparse(substitute(x, env))
  cli::cli_alert_info("Access column names with {obj_name}$<var>")
  cli::cli_alert_info("e.g., {obj_name}${names(x)[1]}")
}

my_print <- function(x){
  cli::cli_h1("My Object")
  cli::cli_text("My dims are {dim(x)}")
  my_info(x, environment())
}

my_info(airquality)
#> β„Ή Access column names with airquality$<var>
#> β„Ή e.g., airquality$Ozone

my_print(airquality)
#> 
#> ── My Object ───────────────────────────────────────────────────────────────────
#> My dims are 153 and 6
#> β„Ή Access column names with airquality$<var>
#> β„Ή e.g., airquality$Ozone

But when using the print method, this doesn’t work, it just prints β€œx”

print.my_class <- function(x, ...){
    cli::cli_h1("My Object")
    cli::cli_text("My dims are {dim(x)}")
    my_info(x, environment())
}

class(airquality) <- c("my_class", class(airquality))

airquality
#> 
#> ── My Object ───────────────────────────────────────────────────────────────────
#> My dims are 153 and 6
#> β„Ή Access column names with x$<var>
#> β„Ή e.g., x$Ozone

Created on 2024-05-16 with reprex v2.1.0

Session info
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.4.0 (2024-04-24)
#>  os       macOS Sonoma 14.3.1
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       Australia/Brisbane
#>  date     2024-05-16
#>  pandoc   3.1.13 @ /opt/homebrew/bin/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package     * version date (UTC) lib source
#>  cli           3.6.2   2023-12-11 [1] CRAN (R 4.4.0)
#>  digest        0.6.35  2024-03-11 [1] CRAN (R 4.4.0)
#>  evaluate      0.23    2023-11-01 [1] CRAN (R 4.4.0)
#>  fastmap       1.1.1   2023-02-24 [1] CRAN (R 4.4.0)
#>  fs            1.6.4   2024-04-25 [1] CRAN (R 4.4.0)
#>  glue          1.7.0   2024-01-09 [1] CRAN (R 4.4.0)
#>  htmltools     0.5.8.1 2024-04-04 [1] CRAN (R 4.4.0)
#>  knitr         1.46    2024-04-06 [1] CRAN (R 4.4.0)
#>  lifecycle     1.0.4   2023-11-07 [1] CRAN (R 4.4.0)
#>  magrittr      2.0.3   2022-03-30 [1] CRAN (R 4.4.0)
#>  purrr         1.0.2   2023-08-10 [1] CRAN (R 4.4.0)
#>  R.cache       0.16.0  2022-07-21 [1] CRAN (R 4.4.0)
#>  R.methodsS3   1.8.2   2022-06-13 [1] CRAN (R 4.4.0)
#>  R.oo          1.26.0  2024-01-24 [1] CRAN (R 4.4.0)
#>  R.utils       2.12.3  2023-11-18 [1] CRAN (R 4.4.0)
#>  reprex        2.1.0   2024-01-11 [1] CRAN (R 4.4.0)
#>  rlang         1.1.3   2024-01-10 [1] CRAN (R 4.4.0)
#>  rmarkdown     2.26    2024-03-05 [1] CRAN (R 4.4.0)
#>  rstudioapi    0.16.0  2024-03-24 [1] CRAN (R 4.4.0)
#>  sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.4.0)
#>  styler        1.10.3  2024-04-07 [1] CRAN (R 4.4.0)
#>  vctrs         0.6.5   2023-12-01 [1] CRAN (R 4.4.0)
#>  withr         3.0.0   2024-01-16 [1] CRAN (R 4.4.0)
#>  xfun          0.43    2024-03-25 [1] CRAN (R 4.4.0)
#>  yaml          2.3.8   2023-12-11 [1] CRAN (R 4.4.0)
#> 
#>  [1] /Users/nick/Library/R/arm64/4.4/library
#>  [2] /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────

Is there a way to get the my_print to work with a print S3 method?

1 Like

What an interesting challenge! :sweat_smile: I guess the second case doesn't work because the object has been passed through one function more so env isn't the right one?

2 Likes

This also seems related: Using column name as function argument within a S3 methods

2 Likes

In general this is not possible, because an object may have any number of names, including zero. (E.g. think of an anonymous function, that has zero names.)

With non-standard evaluation you can work out the expression that was used to call your function, but that might not be a name, it can be any expression. It is also more difficult if your function is an S3 method, and you probably need to do a lot of introspection to do it, and implementing that in a robust way also seems very challenging.

Another big challenge is that the name you end up finding downstream may be meaningless for the end user, if it is a name coming from some internal downstream code.

You probably already know the articles under the 'Conditions' menu of the rlang pkgdown page.

3 Likes

Check out the implementation of get_name_in_parent() in the {assertive.base} package. I think it does exactly what you need, although there's possibly a less convoluted way of achieving the same result.

Thanks @maelle and @Gabor , really appreciate you!

With non-standard evaluation you can work out the expression that was used to call your function, but that might not be a name, it can be any expression. It is also more difficult if your function is an S3 method, and you probably need to do a lot of introspection to do it, and implementing that in a robust way also seems very challenging.

This almost sounds like a challenge that would make me want to write an R package to solve this. But it also sounds very prickly and hard.

It sounds like the best solution here might just be to include a generic name of the object.

I'll keep picking away at this, perhaps trying out the suggestion from @DaniMori (Thanks mate!).

1 Like

it occurred to me that if this is meant to be bespoke behaviour of a certain custom S3 type; you could attach an attribute when building an object of that type, such that it would have canonical name. Then the method could look for the attribute ?


as_my_class <- function(x, env = environment()){
  obj_name <- deparse(substitute(x, env))
  class(x) <- c("my_class", class(x))
  attr(x,"obj_name") <- obj_name
  x
}

print.my_class <- function(x, ...){
  cli::cli_h1("My Object")
  cli::cli_text("My dims are {dim(x)}")
  obj_name <- attr(x,"obj_name",exact=TRUE)
  cli::cli_alert_info("Access column names with {obj_name}$<var>")
  cli::cli_alert_info("e.g., {obj_name}${names(x)[1]}")
}

airquality <- as_my_class(airquality)

airquality
── My Object ────────────────────────────────────────────────────────────
My dims are 153 and 6
β„Ή Access column names with airquality$<var>
β„Ή e.g., airquality$Ozone
1 Like