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

Default convert() methods for base classes. #472

Open
t-kalinowski opened this issue Oct 21, 2024 · 5 comments
Open

Default convert() methods for base classes. #472

t-kalinowski opened this issue Oct 21, 2024 · 5 comments

Comments

@t-kalinowski
Copy link
Member

t-kalinowski commented Oct 21, 2024

Users may expect something like this to work:

x <- 3
convert(x, class_character)

Concretely, I wonder if we should do:

method(convert, list(class_any, class_character)) <- function(from, to, ...) as.character(from)
method(convert, list(class_any, class_integer)) <- function(from, to, ...) as.integer(from)
method(convert, list(class_any, class_logical)) <- function(from, to, ...) as.logical(from)
...

One thing to consider is that individual package authors may want different behavior. E.g., a strict whole-number check when casting double to integer

method(convert, list(class_double, class_integer)) <- function(from, to, ...) {
  stopifnot("must be a whole number" = trunc(from) == from)
  as.integer(from)
}

We don't want different packages exporting different methods for this type of cast. We may want to dis-allow setting convert(, to) methods for base S3 classes for this reason.

@fithisux
Copy link

@t-kalinowski if convert method takes always 2 arguments, why do we need the list(_,_) part?

@t-kalinowski
Copy link
Member Author

The list(_,_) is the second argument to method<- and is used by the generic to perform dispatch based on the argument types. https://rconsortium.github.io/S7/reference/method-set.html

@hadley
Copy link
Member

hadley commented Oct 24, 2024

I worry that this has a bunch of unintended consequences that are hard to reason about. In general, I think it's best to be as explicit as possible with coercion, even if it means some existing functions might not work.

In general, I think it's ok to rely on the convention that you should only define methods if you own either one of the classes involved or the generic. It would be nice if we could automate that as a check, but I suspect it's going to be hard.

@lawremi
Copy link
Collaborator

lawremi commented Oct 26, 2024

I've already defined all of the base/S3 conversions in my S7-based package. Definitely useful to have.

method(convert, list(class_any, class_logical)) <-
    function(from, to, ...) as.logical(from, ...)
method(convert, list(class_any, class_integer)) <-
    function(from, to, ...) as.integer(from, ...)
method(convert, list(class_any, class_double)) <-
    function(from, to, ...) as.double(from, ...)
method(convert, list(class_any, class_complex)) <-
    function(from, to, ...) as.complex(from, ...)
method(convert, list(class_any, class_character)) <-
    function(from, to, ...) as.character(from, ...)
method(convert, list(class_any, class_raw)) <-
    function(from, to, ...) as.raw(from, ...)
method(convert, list(class_any, class_list)) <-
    function(from, to, ...) as.list(from, ...)
method(convert, list(class_any, class_expression)) <-
    function(from, to, ...) as.expression(from, ...)
method(convert, list(class_any, class_function)) <-
    function(from, to, ...) as.function(from, ...)
method(convert, list(class_any, class_environment)) <-
    function(from, to, ...) as.environment(from, ...)
method(convert, list(class_any, class_name)) <-
    function(from, to, ...) as.name(from, ...)
method(convert, list(class_any, class_call)) <-
    function(from, to, ...) as.call(from, ...)

method(convert, list(class_any, class_data.frame)) <-
    function(from, to, ...) as.data.frame(from, ...)
method(convert, list(class_any, class_Date)) <-
    function(from, to, ...) as.Date(from, ...)
method(convert, list(class_any, class_factor)) <-
    function(from, to, ...) as.factor(from, ...)
method(convert, list(class_any, class_POSIXct)) <-
    function(from, to, ...) as.POSIXct(from, ...)
method(convert, list(class_any, class_POSIXlt)) <-
    function(from, to, ...) as.POSIXlt(from, ...)
method(convert, list(class_any, class_formula)) <-
    function(from, to, ...) as.formula(from, ...)

@fithisux
Copy link

The list(_,_) is the second argument to method<- and is used by the generic to perform dispatch based on the argument types. https://rconsortium.github.io/S7/reference/method-set.html

You are right.

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

No branches or pull requests

4 participants