Working through 'Arithmetic' in vignette("s3-vector", "vctrs")
to create my own class, I've hit some undesirable behaviour and I'm struggling to see how to address it. My issue is that arithmetic with my class and a base-R class actually works when I'd prefer it to throw an error. This behaviour manifests with the <vctrs_meter>
class defined in the vignette, so I've used this class as a reprex below.
library(vctrs)
#> Warning: package 'vctrs' was built under R version 4.2.3
# -- Define a <vctrs_meter> class as per vignette ---------------------------------------------------------
meter <- function(x) {
x <- vec_cast(x, double())
new_meter(x)
}
new_meter <- function(x) {
stopifnot(is.double(x))
new_vctr(x, class = "vctrs_meter")
}
format.vctrs_meter <- function(x, ...) {
paste0(format(vec_data(x)), " m")
}
vec_arith.vctrs_meter <- function(op, x, y, ...) {
UseMethod("vec_arith.vctrs_meter", y)
}
vec_arith.vctrs_meter.default <- function(op, x, y, ...) {
stop_incompatible_op(op, x, y)
}
vec_arith.vctrs_meter.vctrs_meter <- function(op, x, y, ...) {
switch(
op,
"+" = ,
"-" = new_meter(vec_arith_base(op, x, y)),
"/" = vec_arith_base(op, x, y),
stop_incompatible_op(op, x, y)
)
}
# -- Expected behaviour with built-in types ---------------------------------------------------------------
# This works as expected
meter(10) + meter(1)
#> <vctrs_meter[1]>
#> [1] 11 m
# This doesn't work, also as expected
meter(10) + 1L
#> Error in `vec_arith()`:
#> ! <vctrs_meter> + <integer> is not permitted
#> Backtrace:
#> ▆
#> 1. └─vctrs:::`+.vctrs_vctr`(meter(10), 1L)
#> 2. ├─vctrs::vec_arith("+", e1, e2)
#> 3. ├─global vec_arith.vctrs_meter("+", e1, e2)
#> 4. └─global vec_arith.vctrs_meter.default("+", e1, e2)
#> 5. └─vctrs::stop_incompatible_op(op, x, y)
#> 6. └─vctrs:::stop_incompatible(...)
#> 7. └─vctrs:::stop_vctrs(...)
#> 8. └─rlang::abort(message, class = c(class, "vctrs_error"), ..., call = call)
# -- Unexpected behaviour with base S3 classes ------------------------------------------------------------
# These all work with warnings, whereas I'd expect an error
meter(1) + factor("hi")
#> Warning: Incompatible methods ("+.vctrs_vctr", "Ops.factor") for "+"
#> <vctrs_meter[1]>
#> [1] 2 m
meter(1) + Sys.Date()
#> Warning: Incompatible methods ("+.vctrs_vctr", "+.Date") for "+"
#> <vctrs_meter[1]>
#> [1] 19537 m
meter(1) + as.POSIXct(Sys.Date())
#> Warning: Incompatible methods ("+.vctrs_vctr", "+.POSIXt") for "+"
#> <vctrs_meter[1]>
#> [1] 1687910401 m
Created on 2023-06-28 with reprex v2.0.2
Ideally I'd like arithmetic with base S3 classes to fail in the same way as for built-in types as demonstrated in the reprex.
NB, I've created an issue on GitHub for this, as I think this behaviour is surprising enough to merit a change to the vignette.