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

S7 with varargs #515

Open
VisruthSK opened this issue Dec 18, 2024 · 0 comments
Open

S7 with varargs #515

VisruthSK opened this issue Dec 18, 2024 · 0 comments

Comments

@VisruthSK
Copy link

When converting some rvest S3 classes to S7 I happened upon rvest_field which has an interesting S3 implementation using ...:

rvest_field <- function(type, name, value, attr, ...) {
  structure(
    list(
      type = type,
      name = name,
      value = value,
      attr = attr,
      ...
    ),
    class = "rvest_field"
  )
}

I noted that in the rvest implementation there was only one use case of the dots, and that was to pass a specific value called options so I opted to make that an optional property of the S7 rvest_field, and removed the dots in its construction. That led me to question how one would mimic the original behaviour as closely as possible using S7 though. The first thought I had was to take the dots and stick them into a property:

library(S7)
test <- new_class(
  "test",
  properties = list(
    real_prop = class_any,
    varargs = class_list
  ),
  constructor = function(real_prop, ...) {
    new_object(S7_object(), real_prop = real_prop, varargs = rlang::list2(...))
  }
)

But now you obviously have to do test(10, x=1)@varargs$x which isn't very pretty--and you also lose validation for the faux x "property" which defeats a lot of the benefits of S7 I think (but nominally solves the problem). Dealing with the first issue is easy (see below for a very rough implementation), but I don't know what an nice way of solving the latter would be. Maybe pass a list with arguments and attached properties like
temp(x = "hi", y = 2, list(x = class_character, y = new_property(class_numeric, getter = function(self) self@y, setter = NULL)))
and then parse the list on construction? And then instead of making someone deal with all that everytime they want to create a S7 object with the dots, expose a varargs class which does this and make objects which want to have variable properties inherit from it?

I also don't know how pertinent this is, but I think it would be cool to be able to create S7 objects with variable properties proper, instead of looking up arguments in an list. I'd be happy to work on this if it is useful/feasible.

temp <- new_class(
  "temp",
  properties = list(
    varargs = new_property(
      class_list,
      getter = function(self) self@varargs,
      setter = NULL
    )
  ),
  constructor = function(...) new_object(S7_object(), varargs = rlang::list2(...))
)

`@.temp` <- function(object, name) {
  tryCatch(
    S7:::`@.S7_object`(object, name),
    error = function(e) {
      property <- S7:::`@.S7_object`(object, "varargs")[[name]]
      if (is.null(property)) S7:::`@.S7_object`(object, name) # TODO: throw a better error which talks about instance (i.e. `object`) instead of class (i.e. `class(object)[1]`)
      property
    }
  )
}

t <- temp(x = 1, y = 2)
t@varargs
t@x
try(t@z)
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

1 participant