Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot use S4 and box together: name '.cacheOnAssign' not found in 'env' #284

Open
dereckmezquita opened this issue Jun 21, 2022 · 4 comments

Comments

@dereckmezquita
Copy link
Sponsor

Error description

My set up and minimal example

I have this set up:

  • SomeProject/
    • bin/
      • someMinimalScript.R
        • utils/
          • R/
            • someMinimalModule.R

Contents of someMinimalScript.R:

#!/usr/bin/env Rscript

box::use(./utils/R/someMinimalModule[ someClass ])

This is importing a module who's contents are someMinimalModule.R:

#' @export
someClass <- setClass(
    "someClass",
    slots = list(
        example_slot = "character"
    ),
    prototype = list(
        example_slot = character()
    )
)

#' @export
setMethod("show", "someClass", \(object) {
    cat("\n")
    cat("Example slot:", object@example_slot, "\n")
    cat("\n")
})

This is a set of tools which is designed to be used from the system command line (CLI tools). This is achieved by exporting the path to the tools in a .bashrc:

export PATH="/Users/someUser/someProject/bin:$PATH"

We need to also do this in the project: chmod 755 ./bin/*.

My errors

Once all of this is set up, I am getting this error when calling this tool from the command line:

someMinimalScript.R
Error in box::use(./utils/R/someMinimalModule[someClass]) : 
  could not find function "setClass"
(inside “setClass("someClass", slots = list(example_slot = "character"), ”
“    prototype = list(example_slot = character()))”)
Calls: <Anonymous> ... tryCatchList -> tryCatchOne -> <Anonymous> -> rethrow -> throw
Execution halted

First question, why am I getting this above error? It makes no sense, the methods package is part of R or it's included in base. I solved this by appending methods:: to my function calls:

#' @export
someClass <- methods::setClass(
    "someClass",
    slots = list(
        example_slot = "character"
    ),
    prototype = list(
        example_slot = character()
    )
)

#' @export
methods::setMethod("show", "someClass", \(object) {
    cat("\n")
    cat("Example slot:", object@example_slot, "\n")
    cat("\n")
})

But now I get this error; I would appreciate any insight anyone could extend to get this resolved:

someMinimalScript.R
Error in box::use(./utils/R/someMinimalModule[someClass]) : 
  name '.cacheOnAssign' not found in 'env'
(inside “env$.cacheOnAssign”)
Calls: <Anonymous> ... tryCatchList -> tryCatchOne -> <Anonymous> -> rethrow -> throw
Execution halted

R version

sessionInfo()
R version 4.2.0 (2022-04-22)
Platform: aarch64-apple-darwin21.3.0 (64-bit)
Running under: macOS Monterey 12.2.1

Matrix products: default
BLAS:   /opt/homebrew/Cellar/openblas/0.3.20/lib/libopenblasp-r0.3.20.dylib
LAPACK: /opt/homebrew/Cellar/r/4.2.0/lib/R/lib/libRlapack.dylib

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_4.2.0    data.table_1.14.2

‘box’ version

‘1.1.2’

@dereckmezquita
Copy link
Sponsor Author

dereckmezquita commented Jun 22, 2022

Note this error also occurs when trying to run code interactively. It seems that box does not play nice with S4.

Module:

#' @export
someClass <- methods::setClass(
    "someClass",
    slots = list(
        example_slot = "character"
    ),
    prototype = list(
        example_slot = character()
    )
)

#' @export
methods::setMethod("show", "someClass", \(object) {
    cat("\n")
    cat("Example slot:", object@example_slot, "\n")
    cat("\n")
})
box::use(./utils/R/someModule)
Error in box::use(./utils/R/someModule) : 
  name '.cacheOnAssign' not found in 'env'
(inside “env$.cacheOnAssign”)

@dereckmezquita dereckmezquita changed the title Cannot use S4 and box in executable R script: name '.cacheOnAssign' not found in 'env' Cannot use S4 and box together: name '.cacheOnAssign' not found in 'env' Jun 22, 2022
@dereckmezquita
Copy link
Sponsor Author

dereckmezquita commented Jun 22, 2022

This error also occurs when creating new methods in S4:

SomeMethod.R

methods::setGeneric("is.empty", \(x, ...) standardGeneric("is.empty"))

methods::setMethod("is.empty", "data.frame", \(x, ...) {
    return(identical(dim(x), c(0, 0)))
})
box::use(./utils/R/someMethod)
Error in box::use(./utils/R/helpers) : 
  name '.cacheOnAssign' not found in 'env'
(inside “env$.cacheOnAssign”)
Calls: <Anonymous> ... tryCatchList -> tryCatchOne -> <Anonymous> -> rethrow -> throw
Execution halted

Unfortunately after a lot of testing it seems that the whole of the S4 system does not work with box. This is greatly unfortunate since S4 is a great system and one of the standards in OOP for R.

@klmr
Copy link
Owner

klmr commented Jul 1, 2022

Hi,

In the order from easiest to hardest:

1.

First question, why am I getting this above error? It makes no sense, the methods package is part of R or it's included in base. I solved this by appending methods:: to my function calls:

That’s normal; first off, ‘methods’ isn’t included in ‘base’, and it isn’t by default loaded/attached in R except in interactive sessions. So this isn’t specific to ‘box’ (you’d have the same error in any script run non-interactively). But in addition, ‘box’ modules don’t attach any packages (except ‘base’), even in interactive sessions — see this FAQ.

Your fix works but the “preferred” solution is to use box::use() instead of :::

box::use(methods)

#' @export
someClass <- methods$setClass(
…

2.

But now I get this error; …

Error in box::use(./utils/R/someMinimalModule[someClass]) : 
  name '.cacheOnAssign' not found in 'env'

This can be fixed by adding .cacheOnAssign <- FALSE to your module source code. However, …

3.

Unfortunately S4 does not, and (I believe) fundamentally cannot ever work with ‘box’. The reason for that is that S4 explicitly only supports defining classes and methods in packages or the R global environment, and nothing else. See also #95. In particular, S4 internally tries to load the code that defines an S4 class as a package (i.e. via require), even if the code is actually a module. You can’t tell it not to do this, since that logic is hard-coded.

Maybe (!) it is possible to work around this limitation and cobble together some half-assed support for S4 in ‘box’ but unfortunately it isn’t even obvious whether that’s possible at all, and the documentation of S4 is notoriously poor, as noted in Advanced R:

S4 is a complex system and it can be challenging to use effectively in practice. This wouldn’t be such a problem if S4 documentation wasn’t scattered through R documentation, books, and websites. S4 needs a book length treatment, but that book does not (yet) exist.

Worse, what little S4 documentation exists is riddled with errors, both in the prose (which makes some sentences unintelligible) and in the actual example code!

My general recommendation (regardless of ‘box’!), echoing Advanced R, is to avoid S4 as much as possible. I’d go even further than Advanced R: don’t use S4 at all. Use S3 or R6, as appropriate, or build your own object system where neither of these works well. After all, R allows doing this, and it’s e.g. what ‘ggplot2’ did. S4 has many good ideas but its implementation is rather problematic.

@dereckmezquita
Copy link
Sponsor Author

Thank you very much for all that @klmr I really appreciate it.

It's very unfortunate as I really am a fan of S4. I have experience with R6 and S3 as well so for now I think I will move over to those systems.

As I understood getting this really fixed would require making changes to how S4 works right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants