rtibbler-s4rdata

Saving then loading a `tibble` causes it to be not recognised by S4 methods


I have an obscure problem in R that involves the S4 class system.

First I create a tibble and save it:

df = tibble::tibble(a=1:5, b=2:6)
save(df, file="foo.Rdata")

Next, I close the R session, and start a new one (which for some reason matters).

Then, I define an S4 method that dispatches on a data.frame (which a tibble is a subclass of):

setGeneric("sumTheColumns", function(target){    
  standardGeneric("sumTheColumns")    
})    
setMethod("sumTheColumns", signature(target="data.frame"), function(target){    
    colSums(target)    
})    
     
load("foo.Rdata")    
print(df)    
print(class(df))    
print(showMethods(sumTheColumns))    
sumTheColumns(df)   

From this, R outputs:

[1] "sumTheColumns"
  a b
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
[1] "tbl_df"     "tbl"        "data.frame"
Function: sumTheColumns (package .GlobalEnv)
target="data.frame"

Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘sumTheColumns’ for signature ‘"tbl_df"’
Calls: sumTheColumns -> <Anonymous>
Execution halted

So as you can see, the tibble is being saved and loaded correct, and it retains its classes, however it no longer dispatches correctly. Interestingly, if you define and call these same methods in the first R session where we create df, these dispatch correctly.

Why is this happening, and how can I fix it?

In case this is an OS-specific bug, here is some output from sessionInfo:

> sessionInfo()
R version 4.1.1 (2021-08-10)
Platform: x86_64-conda-linux-gnu (64-bit)
Running under: Ubuntu 21.04

Solution

  • Compare the loadedNamespaces() entries in the two situations. I see

    > loadedNamespaces()
    [1] "compiler"  "graphics"  "utils"     "grDevices" "stats"     "datasets"  "methods"   "base"    
    

    in a clean new session, but after creating df, it grows to

    > loadedNamespaces()
     [1] "compiler"  "magrittr"  "ellipsis"  "graphics"  "pillar"    "glue"      "utils"    
     [8] "tibble"    "grDevices" "crayon"    "stats"     "utf8"      "fansi"     "datasets" 
    [15] "vctrs"     "methods"   "lifecycle" "pkgconfig" "rlang"     "base"    
    

    So one of those new packages fixes the issue. I'd guess it's tibble itself, and indeed I see the error in the new session, but not after I run loadNamespace("tibble") (which gets everything in the second list). So a solution is:

    To get your dataframe methods to work on tibbles, you could run

    loadNamespace("tibble")
    

    before calling them. You would want to do this if you intend to treat tibbles as tibbles in other places, e.g. to get them to print the way tibbles print.

    Alternatively, if you just want your code to work on tibbles as if they were dataframes, you could do what the tibble package does, and run

    methods::setOldClass(c("tbl_df", "tbl", "data.frame"))
    

    That won't load any packages, but it will tell the S4 system that tibbles inherit from dataframes.