vctrs icon indicating copy to clipboard operation
vctrs copied to clipboard

@method required for vec_arith in R 4.0+, but not R 3.6.3?

Open dantonnoriega opened this issue 4 years ago • 1 comments

Related to #1287.

I lost a bit of time trying to debug the fact that, in R 4.0, you must use @method for vec_arith. However, in R 3.6.3, @export is sufficient.

This is not made clear in https://vctrs.r-lib.org/articles/s3-vector.html#arithmetic-1. But I'm also a newb when it comes to S3 methods; perhaps it's obvious to experience folks.


Here is my vctrs_io class.

new_io <- function(x = double()) {
  stopifnot(is.double(x))
  vctrs::new_vctr(x, class = "vctrs_io")
}

# methods::setOldClass(c("vctrs_io", "vctrs_vctr"))

#' `io` vector
#'
#' This creates a double vector that represents bandwidth (GB/s).
#'
#' @import vctrs
#' @param x A numeric vector
#' @return An S3 vector of class `io`.
#' @export
#' @examples
#' io(c(1,3,9))
io <- function(x = double()) {
  x <- vctrs::vec_cast(x, double())
  new_io(x)
}


#' bandwidth units in Gb/s
#' @export
format.vctrs_io <- function(x, ...) {
  paste0(format(vctrs::vec_data(x)), " Gb/s")
}

#' @param x an object to test
#' @export
#' @rdname io
is_io <- function(x) inherits(x, "vctrs_io")

# arith
#' @export
vec_arith.vctrs_io <- function(op, x, y, ...) {
  UseMethod("vec_arith.vctrs_io", y)
}

#' @export
vec_arith.vctrs_io.default <- function(op, x, y, ...) {
  vctrs::stop_incompatible_op(op, x, y)
}

#' @export
vec_arith.vctrs_io.vctrs_io <- function(op, x, y, ...) {
  switch(
    op,
    "+" = ,
    "-" = io(vctrs::vec_arith_base(op, x, y)),
    "/" = vctrs::vec_arith_base(op, x, y),
    vctrs::stop_incompatible_op(op, x, y)
  )
}

#' @export
vec_arith.vctrs_io.numeric <- function(op, x, y, ...) {
  switch(
    op,
    "/" = ,
    "*" = new_io(vctrs::vec_arith_base(op, x, y)),
    vctrs::stop_incompatible_op(op, x, y)
  )
}

#' @export
vec_arith.numeric.vctrs_io <- function(op, x, y, ...) {
  switch(
    op,
    "*" = new_io(vctrs::vec_arith_base(op, x, y)),
    vctrs::stop_incompatible_op(op, x, y)
  )
}

# types
#' @export
vec_ptype2.vctrs_io.vctrs_io <- function(x, y, ...) new_io()
#' @export
vec_ptype2.vctrs_io.double <- function(x, y, ...) double()
#' @export
vec_ptype2.double.vctrs_io <- function(x, y, ...) double()

# casts
#' @export
vec_cast.vctrs_io.vctrs_io <- function(x, to, ...) x
#' @export
vec_cast.vctrs_io.double <- function(x, to, ...) new_io(x)
#' @method vec_cast.double vctrs_io
#' @export
vec_cast.double.vctrs_io <- function(x, to, ...) vctrs::vec_data(x)

I throw this into a package under R/vctrs_io_class.R. Here is the NAMESPACE.

# Generated by roxygen2: do not edit by hand

S3method(format,vctrs_io)
S3method(vec_arith,numeric.vctrs_io)
S3method(vec_arith,vctrs_io.default)
S3method(vec_arith,vctrs_io.numeric)
S3method(vec_arith,vctrs_io.vctrs_io)
S3method(vec_cast,vctrs_io.double)
S3method(vec_cast,vctrs_io.vctrs_io)
S3method(vec_cast.double,vctrs_io)
S3method(vec_ptype2,double.vctrs_io)
S3method(vec_ptype2,vctrs_io.double)
S3method(vec_ptype2,vctrs_io.vctrs_io)
export(io)
export(is_io)
export(vec_arith.vctrs_io)
import(vctrs)

R 3.6.3 -- Works fine

❯ R --vanilla

R version 3.6.3 Patched (2020-04-28 r78687) -- "Holding the Windsock"
Copyright (C) 2020 The R Foundation for Statistical Computing
Platform: x86_64-apple-darwin15.6.0 (64-bit)

...

> devtools::load_all(here::here())
> sessionInfo()
R version 3.6.3 Patched (2020-04-28 r78687)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib

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

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

other attached packages:
[1] io_0.0.0.9000

loaded via a namespace (and not attached):
 [1] rstudioapi_0.13   magrittr_2.0.1    usethis_2.0.1     devtools_2.3.2
 [5] pkgload_1.2.0     here_1.0.1        R6_2.5.0          rlang_0.4.10
 [9] fastmap_1.1.0     tools_3.6.3       pkgbuild_1.2.0    sessioninfo_1.1.1
[13] cli_2.3.1         withr_2.4.1       ellipsis_0.3.1    remotes_2.2.0
[17] assertthat_0.2.1  rprojroot_2.0.2   lifecycle_1.0.0   crayon_1.4.1
[21] processx_3.4.5    purrr_0.3.4       callr_3.5.1       vctrs_0.3.7
[25] fs_1.5.0          ps_1.6.0          testthat_3.0.2    memoise_2.0.0
[29] glue_1.4.2        cachem_1.0.4      compiler_3.6.3    desc_1.3.0
[33] prettyunits_1.1.1
> new_io(c(r=1, w=2)) + new_io(c(r=1, w=4))
<vctrs_io[2]>
     r      w
2 Gb/s 6 Gb/s

R 4.0.0 -- Error

❯ R --vanilla

R version 4.0.0 (2020-04-24) -- "Arbor Day"
Copyright (C) 2020 The R Foundation for Statistical Computing
Platform: x86_64-apple-darwin17.0 (64-bit)

...

> devtools::load_all(here::here())
> sessionInfo()
R version 4.0.0 (2020-04-24)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

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

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

other attached packages:
[1] io_0.0.0.9000

loaded via a namespace (and not attached):
 [1] rstudioapi_0.11   magrittr_1.5      usethis_1.6.1     devtools_2.3.0
 [5] pkgload_1.1.0     here_0.1          R6_2.4.1          rlang_0.4.10
 [9] tools_4.0.0       pkgbuild_1.0.8    sessioninfo_1.1.1 cli_2.4.0
[13] withr_2.4.1       ellipsis_0.3.1    remotes_2.1.1     digest_0.6.25
[17] assertthat_0.2.1  rprojroot_1.3-2   crayon_1.3.4      processx_3.5.1
[21] callr_3.6.0       vctrs_0.3.7       fs_1.4.1          ps_1.6.0
[25] testthat_3.0.2    memoise_1.1.0     glue_1.4.1        compiler_4.0.0
[29] desc_1.2.0        backports_1.1.7   prettyunits_1.1.1
> new_io(c(r=1, w=2)) + new_io(c(r=1, w=4))
Error: <vctrs_io> + <vctrs_io> is not permitted
Run `rlang::last_error()` to see where the error occurred.

traceback() points towards an issue with S3 methods.

> traceback()
9: stop(fallback)
8: signal_abort(cnd)
7: abort(message, class = c(class, "vctrs_error"), ...)
6: stop_vctrs(message, class = c(class, "vctrs_error_incompatible"),
       x = x, y = y, details = details, ...)
5: stop_incompatible(x, y, op = op, details = details, ..., message = message,
       class = c(class, "vctrs_error_incompatible_op"))
4: stop_incompatible_op(op, x, y)
3: vec_arith.default("+", e1, e2)
2: vec_arith("+", e1, e2)
1: `+.vctrs_vctr`(new_io(c(r = 1, w = 2)), new_io(c(r = 1, w = 4)))

If you source directly, issue is resolved (but not conducive to package development):

> source(here::here("R/vctrs_io_class.R"))
> new_io(c(r=1, w=2)) + new_io(c(r=1, w=4))
<vctrs_io[2]>
     r      w
2 Gb/s 6 Gb/s

dantonnoriega avatar Apr 13 '21 19:04 dantonnoriega

You do currently have to supply @method for vec_arith. There was a change in R 4.0.0 related to S3 method lookup, and I'm pretty sure that is why it "works" in 3.6.3, but not in 4.0.0. The news bullet for the change is:

S3 method lookup now by default skips the elements of the search path between the global and base environments.

The eventual plan is for vec_arith to work like vec_ptype2/vec_cast where you only have to use @export.

We should probably mention that you currently have to do this in the Other Helpers section here: https://vctrs.r-lib.org/articles/s3-vector.html#other-helpers

You can use this as a guide for now: https://github.com/r-lib/clock/blob/430514ea797fec8d47ba4f26e02085f5155de8c2/R/duration.R#L751-L814

DavisVaughan avatar Apr 14 '21 12:04 DavisVaughan