Custom print methods for arrays and atomic vectors

I would like to define a different print method for arrays, but I'm afraid I'm not understanding something about S3 dispach. My custom print method is called if I call print(x) explicitly, but is not called if I just type x at the console. However, if I define a custom S3 class, then the appropriate print method is called.

A similar thing happens if I try to define a method for print.numeric

Here is a minimal example:

print.array <- function(x, ...) cat("Hi!\n")

x <- array(1:8, c(2,2,2) )

print(x) # the print method defined above is called
# Hi!
x        # the print method defined above is NOT called

Does anyone have any insights into what is happening? What function is actually doing the printing when just x is evaluated at the console?

Hmm…so, this actually works inside of a reprex. It also works from the console if I use print(x) (otherwise it prints the array.

print.array <- function(x, ...) cat("Hi!\n")

x <- array(1:8, c(2,2,2) )

print.array(x)
#> Hi!
x
#> Hi!

Created on 2018-02-19 by the reprex package (v0.2.0).

Are you not using curly braces for the function intentionally? I've never set up a print method for my own purposes, but it seems from this answer on StackOverflow (below), that they're using the standard format.

foo <- function(arg) {
...
}

Your assumption that the overridden print function should work in the console is correct but you seem to have picked a case where it doesn't.

I don't have an answer as to why the console ignores the print.array function. It may well be that the console is not treating the array class properly.

I can duplicate the behavior you are seeing where the bare x in the console runs the default print, not the print.array, function.

In general you should submit code examples in the form of a reprex... here is some info about them.

However the example you are showing works in differently in a reprex than it does when typed into the console.

Here is a reprex of what you are trying to do and you will see that it is behaving as expected (in the reprex). It also shows making your own class as an alternative to using an "array" class which can be used for a workaround for the issue you are seeing, i.e. where the bare 'x' will use your custom print function when typed into the console.

x <- array(1:8, c(2,2,2) )
# x class is array
class(x)
#> [1] "array"
# this uses the base print for arrays
x
#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    1    3
#> [2,]    2    4
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]    5    7
#> [2,]    6    8
# however there is no print.array
.S3methods(class="array")
#> [1] anyDuplicated as.data.frame as.raster     duplicated    unique       
#> see '?methods' for accessing help and source code
# this adds the print method for arrays
print.array <- function(x, ...) cat("Hi array")
# and you can see that it has been added
.S3methods(class="array")
#> [1] anyDuplicated as.data.frame as.raster     duplicated    print        
#> [6] unique       
#> see '?methods' for accessing help and source code
# and, in this reprex, a bare x behaves as expected
# but this is different from it's behavior if x
# is typed directly into the console.
x
#> Hi array
# However I can "add" an array class to x
class(x) <- c("array", class(x))
# this the following works not only in this reprex
# it will also work as expected if typed into the console
x
#> Hi array
# however making the function print.array will affect
# all arrays which might not be what you want to do.
#
# an alternative would be to add your own class
# name.
x <- array(1:8, c(2,2,2) )
class(x) <- c("myclass", class(x))
# make a print function for your class
print.myclass <- function(x, ...) cat("hello my class")
# and this will work not only in a reprex but also 
# from the console
x
#> hello my class

Created on 2018-02-19 by the reprex package (v0.2.0).

1 Like

So, after a little digging, it seems that R level print() is never called if the object being printed does not have a class attribute. If the object is not S4 and doesn't have a class attribute, then printing is all handled at the C level, hence why print.array() is not called at the console, (but is called in contexts that call print() at the R level, like reprex() or source(print.eval = TRUE)

This is described in the last paragraph of this section:

and the actual source code that handles the printing is here and here

@mara, I am omitting the curly braces intentionally.
For single statement functions, I think it leads to more readable code. There is also a very marginal performance improvement by omitting the {. Thats because { is another function call, which has it's own overhead.

library(microbenchmark)

wo_braces <- function(x) x
w_braces  <- function(x) { x }

ret <- microbenchmark(
  wo_braces(NULL),
  w_braces(NULL),
  times = 1e7
)

ret
#> Unit: nanoseconds
#>             expr min  lq     mean median  uq      max neval cld
#>  wo_braces(NULL) 128 149 183.2108    155 202   295824 1e+07   a
#>   w_braces(NULL) 128 148 203.5789    155 202 63680957 1e+07   a
autoplot.microbenchmark(ret)
#> Loading required namespace: ggplot2

Created on 2018-03-10 by the reprex package (v0.2.0).

Thank you both for replying!