Uh oh!
There was an error while loading. Please reload this page.
Convenience props - #433
Conversation
aa50bab to
fa861feComparehadley
commented
Sep 27, 2024
Sorry, I missed this! I've been thinking about this too for elmer, whose classes have almost entirely scalar properties. I do think that this does belong in S7 because scalar values are such a common need and we don't currently offer any help. My model for thinking about this is based on the For this discussion, I think the most relevant are:
A few thoughts based on comparing our code to your properties:
Thanks for working on this and I look forward to discussing it more 😄 |
lawremi
commented
Sep 28, 2024
Thanks for your comments. They all seem reasonable to me. I don't feel that strongly about the details, as long as the convenience and code clarity remains. The only reason I messed around with default defaults is that they weren't hurting anything, but I agree it might be better to force the developer to be explicit. |
t-kalinowski
commented
Oct 21, 2024
It would also be nice to allow for convenient
A sketch: new_setter<-function(set_once=FALSE, coerce=TRUE, nullable=TRUE) {
set_once; coerceif (isTRUE(coerce)) {
coerce<-function(value) {
class<- eval(quote(S7_class(self)@properties[[name]]$class),
envir= parent.frame())
if(nullable)
for(classinclass$classes)
if(!is.null(class))
break
convert(value, class)
}
} elseif (isFALSE(coerce) || is.null(coerce)) {
coerce<-identity
} else {
stopifnot(is.function(coerce))
}
function(self, value) {
name<- attr(self, ".setting_prop", TRUE) |> tail(1) |> as.character()
if (set_once) {
if (!is.null(prop(self, name)))
stop(name, " can only be set once.")
}
if(nullable&& is.null(value))
return(self)
prop(self, name) <- coerce(value)
self
}
}
prop_string<-function(default=NULL,
allow_null=FALSE,
allow_na=FALSE,
setter=NULL) {
force(allow_null)
force(allow_na)
new_property(
class=if (allow_null) NULL|class_characterelseclass_character,
default=default,
setter=setter,
validator=function(value) {
if (allow_null&& is.null(value)) {
return()
}
if (length(value) !=1) {
paste0("must be a single string, not '", as.character(value), "'.")
} elseif (!allow_na&& is.na(value)) {
"must not be missing."
}
}
)
}used like: Foo:= new_class(
properties=list(
name= prop_string(
allow_null=FALSE,
default= quote(stop("name must be provided")),
setter= new_setter(set_once=TRUE, nullable=FALSE)
),
read_only= prop_bool(allow_null=TRUE, setter= new_setter(set_once=TRUE))
)) |
lawremi
commented
Oct 25, 2024
Interesting ideas. We might consider those behaviors the domain of the property object itself, leaving the setter to be defined as normal. Any user setter would get wrapped with the extra logic. |
t-kalinowski
commented
Oct 26, 2024
Some motivators for automatically calling
|
hadley
commented
Oct 28, 2024
I'm sceptical that we want to automatically convert, because I think automatic coercions tend to hide problems that you want to know about. I think scalar property helpers will need something custom, because if you have an integer property, I think that 2 is an acceptable value but 2.5 should throw an error. |
lawremi
commented
Jul 7, 2025
See https://github.com/lawremi/wizrd/blob/main/R/utils.R for my current syntactic sugar for defining properties. A good example of usage is https://github.com/lawremi/wizrd/blob/main/R/mcp.R. Just makes things so much cleaner and easier. |
Off topic at this point, but just to share some work I did recently, I implemented this feature over in I also toyed with implementing something similar for constructors, but didn't end up using it because I wasn't confident in how it should affect the default constructor. |
sebffischer
commented
Sep 29, 2025
@lawremi I like the syntactic sugar for defining properties a lot! E.g., it's great that I can then do library(S7)
A<- new_class("A", properties=list(...))
B<- new_class("B",
properties=list(
x1= list_of(A)
)
)Where this solutions falls short is when wanting to combine various such sugar functions (I think). B<- new_class("B",
properties=list(
x1= list_of(list_of(A))
)
) |
lawremi
commented
Sep 29, 2025
The links above to the wizrd package are a bit more chainable in that way; however, I don't think it supports the specific case you mention. |
@t-kalinowski here are the convenience property constructors that we discussed. They seem useful in my applications of S7; however, that does not necessarily mean that they belong in the base S7 package. Curious about everyone's thoughts.