roopdebuggingmetaprogrammingtrace

Why does R behave differently for auto-printed objects?


I have encountered two instances where it seems that R treats obj and print(obj) differently. I would like to know more about the nature of this difference.

First instance

I can run

trace("print.default", where = print)

to trace the default print function.

And indeed, this seems to work as expected:

> print("Hello world!")
Tracing print.default("Hello world!") on entry 
[1] "Hello world!"

However, this does not work if I rely on the auto-print behavior:

> "Hello world!"
[1] "Hello world!"

Why is the tracing code not run for auto-printed objects in R? How can I change that?

Second instance

I can define a print method for NULL objects:

.S3method("print", "NULL", function(x, ...) cat("NULL object\n"))

Again, this works when calling print() explicitly:

> print(NULL)
NULL object

But not when relying on auto-printing:

> NULL
NULL

Why does R not dispatch to my custom print function? How can I make it work?

The instances 1 and 2 defy my expectations in a similar way, so I assume there may be a common cause. What is it?


Solution

  • As Nir Graham suggested in the answer on a similar question, R Internals is enlightening in this regard, in particular the section on autoprinting:

    The actual autoprinting is done by PrintValueEnv in file print.c. If the object to be printed has the S4 bit set and S4 methods dispatch is on, show is called to print the object. Otherwise, if the object bit is set (so the object has a "class" attribute), print is called to dispatch methods: for objects without a class the internal code of print.default is called.

    In other words, as "Hello world!" and NULL have no class attribute set, print() is never called. Indeed, adding the class attribute resolves the situation for the first instance:

    > structure("Hello world!", class = "any_class")
    Tracing print.default(x) on entry 
    [1] "Hello world!"
    attr(,"class")
    [1] "any_class"
    

    For the second instance, as NULL cannot have attributes, this can be demonstrated with numerics instead:

    > .S3method("print", "numeric", function(x, ...) cat("numeric object\n"))
    > print(3)
    numeric object
    > 3
    [1] 3
    > structure(3, class = "numeric")
    numeric object
    

    print.c puts it succinctly:

    /*
     *  auto-printing   ->  PrintValueEnv
     *                      -> PrintValueRec
     *                      -> call print() for objects
     *  Note that auto-printing does not call print.default.
     *  PrintValue, R_PV are similar to auto-printing.
     */
    

    Consequently, there should be no way to change the way objects without a class are auto-printed at the top-level.