| Title: | An Object Oriented System Meant to Become a Successor to S3 andS4 |
| Version: | 0.2.1 |
| Description: | A new object oriented programming system designed to be a successor to S3 and S4. It includes formal class, generic, and method specification, and a limited form of multiple dispatch. It has been designed and implemented collaboratively by the R Consortium Object-Oriented Programming Working Group, which includes representatives from R-Core, 'Bioconductor', 'Posit'/'tidyverse', and the wider R community. |
| License: | MIT + file LICENSE |
| URL: | https://rconsortium.github.io/S7/,https://github.com/RConsortium/S7 |
| BugReports: | https://github.com/RConsortium/S7/issues |
| Depends: | R (≥ 3.5.0) |
| Imports: | utils |
| Suggests: | bench, callr, covr, knitr, methods, rmarkdown, testthat (≥3.2.0), tibble |
| VignetteBuilder: | knitr |
| Config/build/compilation-database: | true |
| Config/Needs/website: | sloop |
| Config/testthat/edition: | 3 |
| Config/testthat/parallel: | TRUE |
| Config/testthat/start-first: | external-generic |
| Encoding: | UTF-8 |
| RoxygenNote: | 7.3.3 |
| NeedsCompilation: | yes |
| Packaged: | 2025-11-14 13:45:25 UTC; hadleywickham |
| Author: | Object-Oriented Programming Working Group [cph], Davis Vaughan [aut], Jim Hester |
| Maintainer: | Hadley Wickham <hadley@posit.co> |
| Repository: | CRAN |
| Date/Publication: | 2025-11-14 19:50:02 UTC |
Register an S7 class with S4
Description
If you want to usemethod<- to register an method for an S4 generic withan S7 class, you need to callS4_register() once.
Usage
S4_register(class, env = parent.frame())Arguments
class | An S7 class created with |
env | Expert use only. Environment where S4 class will be registered. |
Value
Nothing; the function is called for its side-effect.
Examples
methods::setGeneric("S4_generic", function(x) { standardGeneric("S4_generic")})Foo <- new_class("Foo")S4_register(Foo)method(S4_generic, Foo) <- function(x) "Hello"S4_generic(Foo())Retrieve the S7 class of an object
Description
Given an S7 object, find it's class.
Usage
S7_class(object)Arguments
object | The S7 object |
Value
AnS7 class.
Examples
Foo <- new_class("Foo")S7_class(Foo())Get/set underlying "base" data
Description
When an S7 class inherits from an existing base type, it can be usefulto work with the underlying object, i.e. the S7 object stripped of classand properties.
Usage
S7_data(object)S7_data(object, check = TRUE) <- valueArguments
object | An object from a S7 class |
check | If |
value | Object used to replace the underlying data. |
Value
S7_data() returns the data stored in the base object;S7_data<-() is called for its side-effects and returnsobjectinvisibly.
Examples
Text <- new_class("Text", parent = class_character)y <- Text(c(foo = "bar"))yS7_data(y)S7_data(y) <- c("a", "b")yDoes this object inherit from an S7 class?
Description
S7_inherits()returnsTRUEorFALSE.check_is_S7()throws an error ifxisn't the specifiedclass.
Usage
S7_inherits(x, class = NULL)check_is_S7(x, class = NULL, arg = deparse(substitute(x)))Arguments
x | An object |
class | An S7 class or |
arg | Argument name used in error message. |
Value
S7_inherits()returns a singleTRUEorFALSE.check_is_S7()returns nothing; it's called for its side-effects.
Note
Starting withR 4.3.0,base::inherits() can accept an S7 class asthe second argument, supporting usage likeinherits(x, Foo).
Examples
Foo1 <- new_class("Foo1")Foo2 <- new_class("Foo2")S7_inherits(Foo1(), Foo1)check_is_S7(Foo1())check_is_S7(Foo1(), Foo1)S7_inherits(Foo1(), Foo2)try(check_is_S7(Foo1(), Foo2))if (getRversion() >= "4.3.0") inherits(Foo1(), Foo1)Base S7 class
Description
The base class from which all S7 classes eventually inherit from.
Usage
S7_object()Value
The base S7 object.
Examples
S7_objectStandard class specifications
Description
This is used as the interface between S7 and R's other OO systems, allowingyou to use S7 classes and methods with base types, informal S3 classes, andformal S4 classes.
Usage
as_class(x, arg = deparse(substitute(x)))Arguments
x | A class specification. One of the following:
|
arg | Argument name used when generating errors. |
Value
A standardised class: eitherNULL, an S7 class, an S7 union,asnew_S3_class, or a S4 class.
Examples
as_class(class_logical)as_class(new_S3_class("factor"))S7 wrappers for base types
Description
The following S7 classes represent base types allowing them to be usedwithin S7:
class_logicalclass_integerclass_doubleclass_complexclass_characterclass_rawclass_listclass_expressionclass_nameclass_callclass_functionclass_environment(can only be used for properties)
We also include three union types to model numerics, atomics, and vectorsrespectively:
class_numericis a union ofclass_integerandclass_double.class_atomicis a union ofclass_logical,class_numeric,class_complex,class_character, andclass_raw.class_vectoris a union ofclass_atomic,class_list, andclass_expression.class_languageis a union ofclass_nameandclass_call.
Usage
class_logicalclass_integerclass_doubleclass_complexclass_characterclass_rawclass_listclass_expressionclass_nameclass_callclass_functionclass_environmentclass_numericclass_atomicclass_vectorclass_languageValue
S7 classes wrapping around common base types and S3 classes.
Examples
class_integerclass_numericclass_factorS7 wrappers for key S3 classes
Description
S7 bundlesS3 definitions for key S3 classes provided bythe base packages:
class_data.framefor data frames.class_Datefor dates.class_factorfor factors.class_POSIXct,class_POSIXltandclass_POSIXtfor date-times.class_formulafor formulas.
Usage
class_factorclass_Dateclass_POSIXctclass_POSIXltclass_POSIXtclass_data.frameclass_formulaDispatch on any class
Description
Useclass_any to register a default method that is called when no othermethods are matched.
Usage
class_anyExamples
foo <- new_generic("foo", "x")method(foo, class_numeric) <- function(x) "number"method(foo, class_any) <- function(x) "fallback"foo(1)foo("x")Dispatch on a missing argument
Description
Useclass_missing to dispatch when the user has not supplied an argument,i.e. it's missing in the sense ofmissing(), not in the sense ofis.na().
Usage
class_missingValue
Sentinel objects used for special types of dispatch.
Examples
foo <- new_generic("foo", "x")method(foo, class_numeric) <- function(x) "number"method(foo, class_missing) <- function(x) "missing"method(foo, class_any) <- function(x) "fallback"foo(1)foo()foo("")Convert an object from one type to another
Description
convert(from, to) is a built-in generic for converting an object fromone type to another. It is special in three ways:
It uses double-dispatch, because conversion depends on both
fromandto.It uses non-standard dispatch because
tois a class, not an object.It doesn't use inheritance for the
toargument. To understandwhy, imagine you have written methods to objects of various types toclassParent. If you then create a newclassChildthat inherits fromclassParent, you can't expect the methods written forclassParentto work because those methods will returnclassParentobjects, notclassChildobjects.
convert() provides two default implementations:
When
frominherits fromto, it strips any properties thatfrompossesses thattodoes not (downcasting).When
tois a subclass offrom's class, it creates a new object ofclassto, copying over existing properties fromfromand initializingnew properties ofto(upcasting).
If you are converting an object solely for the purposes of accessing a methodon a superclass, you probably wantsuper() instead. See its docs for moredetails.
S3 & S4
convert() plays a similar role to the convention of definingas.foo()functions/generics in S3, and toas()/setAs() in S4.
Usage
convert(from, to, ...)Arguments
from | An S7 object to convert. |
to | An S7 class specification, passed to |
... | Other arguments passed to custom |
Value
Eitherfrom coerced to classto, or an error if the coercionis not possible.
Examples
Foo1 <- new_class("Foo1", properties = list(x = class_integer))Foo2 <- new_class("Foo2", Foo1, properties = list(y = class_double))# Downcasting: S7 provides a default implementation for coercing an object# to one of its parent classes:convert(Foo2(x = 1L, y = 2), to = Foo1)# Upcasting: S7 also provides a default implementation for coercing an object# to one of its child classes:convert(Foo1(x = 1L), to = Foo2)convert(Foo1(x = 1L), to = Foo2, y = 2.5) # Set new propertyconvert(Foo1(x = 1L), to = Foo2, x = 2L, y = 2.5) # Override existing and set new# For all other cases, you'll need to provide your own.try(convert(Foo1(x = 1L), to = class_integer))method(convert, list(Foo1, class_integer)) <- function(from, to) { from@x}convert(Foo1(x = 1L), to = class_integer)# Note that conversion does not respect inheritance so if we define a# convert method for integer to foo1method(convert, list(class_integer, Foo1)) <- function(from, to) { Foo1(x = from)}convert(1L, to = Foo1)# Converting to Foo2 will still errortry(convert(1L, to = Foo2))# This is probably not surprising because foo2 also needs some value# for `@y`, but it definitely makes dispatch for convert() specialFind a method for an S7 generic
Description
method() takes a generic and class signature and performs method dispatchto find the corresponding method implementation. This is rarely neededbecause you'll usually rely on the the generic to do dispatch for you (viaS7_dispatch()). However, this introspection is useful if you want to seethe implementation of a specific method.
Usage
method(generic, class = NULL, object = NULL)Arguments
generic | A generic function, i.e. anS7 generic,anexternal generic, anS3 generic,or anS4 generic. |
class,object | Perform introspection either with a |
Value
Either a function with classS7_method or an error if nomatching method is found.
See Also
method_explain() to explain why a specific method was picked.
Examples
# Create a generic and register some methodsbizarro <- new_generic("bizarro", "x")method(bizarro, class_numeric) <- function(x) rev(x)method(bizarro, class_factor) <- function(x) { levels(x) <- rev(levels(x)) x}# Printing the generic shows the registered methodbizarro# And you can use method() to inspect specific implementationsmethod(bizarro, class = class_integer)method(bizarro, object = 1)method(bizarro, class = class_factor)# errors if method not foundtry(method(bizarro, class = class_data.frame))try(method(bizarro, object = "x"))Register an S7 method for a generic
Description
A generic defines the interface of a function. Once you have created ageneric withnew_generic(), you provide implementations for specificsignatures by registering methods withmethod<-.
The goal is formethod<- to be the single function you need when workingwith S7 generics or S7 classes. This means that as well as registeringmethods for S7 classes on S7 generics, you can also register methods forS7 classes on S3 or S4 generics, and S3 or S4 classes on S7 generics.But this is not a general method registration function: at least one ofgeneric andsignature needs to be from S7.
Note that if you are writing a package, you must callmethods_register()in your.onLoad. This ensures that all methods are dynamically registeredwhen needed.
Usage
method(generic, signature) <- valueArguments
generic | A generic function, i.e. anS7 generic,anexternal generic, anS3 generic,or anS4 generic. |
signature | A method signature. For S7 generics that use single dispatch, this must be one of thefollowing:
For S7 generics that use multiple dispatch, this must be a list of any ofthe above types. For S3 generics, this must be a single S7 class. For S4 generics, this must either be an S7 class, or a list that includesat least one S7 class. |
value | A function that implements the generic specification for thegiven |
Value
Thegeneric, invisibly.
Examples
# Create a genericbizarro <- new_generic("bizarro", "x")# Register some methodsmethod(bizarro, class_numeric) <- function(x) rev(x)method(bizarro, new_S3_class("data.frame")) <- function(x) { x[] <- lapply(x, bizarro) rev(x)}# Using a generic calls the methods automaticallybizarro(head(mtcars))Explain method dispatch
Description
method_explain() shows all possible methods that a call to a genericmight use, which ones exist, and which one will actually be called.
Note that method dispatch uses a string representation of each class inthe class hierarchy. Each class system uses a slightly different conventionto avoid ambiguity.
S7:
pkg::classorclassS4:
S4/pkg::classorS4/classS3:
class
Usage
method_explain(generic, class = NULL, object = NULL)Arguments
generic | A generic function, i.e. anS7 generic,anexternal generic, anS3 generic,or anS4 generic. |
class,object | Perform introspection either with a |
Value
Nothing; this function is called for it's side effects.
Examples
Foo1 <- new_class("Foo1")Foo2 <- new_class("Foo2", Foo1)add <- new_generic("add", c("x", "y"))method(add, list(Foo2, Foo1)) <- function(x, y) c(2, 1)method(add, list(Foo1, Foo1)) <- function(x, y) c(1, 1)method_explain(add, list(Foo2, Foo2))Register methods in a package
Description
When using S7 in a package you should always callmethods_register() whenyour package is loaded. This ensures that methods are registered as neededwhen you implement methods for generics (S3, S4, and S7) in other packages.(This is not strictly necessary if you only register methods for genericsin your package, but it's better to include it and not need it than forgetto include it and hit weird errors.)
Usage
methods_register()Value
Nothing; called for its side-effects.
Examples
.onLoad <- function(...) { S7::methods_register()}Declare an S3 class
Description
To use an S3 class with S7, you must explicitly declare it usingnew_S3_class() because S3 lacks a formal class definition.(Unless it's an important base class already defined inbase_s3_classes.)
Usage
new_S3_class(class, constructor = NULL, validator = NULL)Arguments
class | S3 class vector (i.e. what |
constructor | An optional constructor that can be used to createobjects of the specified class. This is only needed if you wish tohave an S7 class inherit from an S3 class or to use the S3 class as aproperty without a default. It must be specified in thesame way as a S7 constructor: the first argument should be All arguments to the constructor should have default values so thatwhen the constructor is called with no arguments, it returns returnsan "empty", but valid, object. |
validator | An optional validator used by A validator is a single argument function that takes the object tovalidate and returns |
Value
An S7 definition of an S3 class, i.e. a list with classS7_S3_class.
Method dispatch, properties, and unions
There are three ways of using S3 with S7 that only require the S3 classvector:
Registering a S3 method for an S7 generic.
Restricting an S7 property to an S3 class.
Using an S3 class in an S7 union.
This is easy, and you can usually include thenew_S3_class()call inline:
method(my_generic, new_S3_class("factor")) <- function(x) "A factor"new_class("MyClass", properties = list(types = new_S3_class("factor")))new_union("character", new_S3_class("factor"))Extending an S3 class
Creating an S7 class that extends an S3 class requires more work. You'llalso need to provide a constructor for the S3 class that follows S7conventions. This means the first argument to the constructor should be.data, and it should be followed by one argument for each attribute usedby the class.
This can be awkward because base S3 classes are usually heavily wrapped for userconvenience and no low level constructor is available. For example, thefactor class is an integer vector with a character vector oflevels, butthere's no base R function that takes an integer vector of values andcharacter vector of levels, verifies that they are consistent, thencreates a factor object.
You may optionally want to also provide avalidator function which willensure thatvalidate() confirms the validity of any S7 classes that buildon this class. Unlike an S7 validator, you are responsible for validatingthe types of the attributes.
The following code shows how you might wrap the base Date class.A Date is a numeric vector with classDate that can be constructed with.Date().
S3_Date <- new_S3_class("Date", function(.data = integer()) { .Date(.data) }, function(self) { if (!is.numeric(self)) { "Underlying data must be numeric" } })Examples
# No checking, just used for dispatchDate <- new_S3_class("Date")my_generic <- new_generic("my_generic", "x")method(my_generic, Date) <- function(x) "This is a date"my_generic(Sys.Date())Define a new S7 class
Description
A class specifies the properties (data) that each of its objects willpossess. The class, and its parent, determines which method will be usedwhen an object is passed to a generic.
Learn more invignette("classes-objects")
Usage
new_class( name, parent = S7_object, package = topNamespaceName(parent.frame()), properties = list(), abstract = FALSE, constructor = NULL, validator = NULL)new_object(.parent, ...)Arguments
name | The name of the class, as a string. The result of calling |
parent | The parent class to inherit behavior from.There are three options:
|
package | Package name. This is automatically resolved if the class isdefined in a package, and Note, if the class is intended for external use, the constructor should beexported. Learn more in |
properties | A named list specifying the properties (data) thatbelong to each instance of the class. Each element of the list caneither be a type specification (processed by |
abstract | Is this an abstract class? An abstract class can not beinstantiated. |
constructor | The constructor function. In most cases, you can relyon the default constructor, which will generate a function with oneargument for each property. A custom constructor should call |
validator | A function taking a single argument, The job of a validator is to determine whether the object is valid,i.e. if the current property values form an allowed combination. Thetypes of the properties are always automatically validated so the job ofthe validator is to verify that thevalues of individual properties areok (i.e. maybe a property should have length 1, or should always bepositive), or that thecombination of values of multiple properties is ok.It is called after construction and whenever any property is set. The validator should return See |
.parent,... | Parent object and named properties used to construct theobject. |
Value
A object constructor, a function that can be used to create objectsof the given class.
Examples
# Create an class that represents a range using a numeric start and endRange <- new_class("Range", properties = list( start = class_numeric, end = class_numeric ))r <- Range(start = 10, end = 20)r# get and set properties with @r@startr@end <- 40r@end# S7 automatically ensures that properties are of the declared types:try(Range(start = "hello", end = 20))# But we might also want to use a validator to ensure that start and end# are length 1, and that start is < endRange <- new_class("Range", properties = list( start = class_numeric, end = class_numeric ), validator = function(self) { if (length(self@start) != 1) { "@start must be a single number" } else if (length(self@end) != 1) { "@end must be a single number" } else if (self@end < self@start) { "@end must be great than or equal to @start" } })try(Range(start = c(10, 15), end = 20))try(Range(start = 20, end = 10))r <- Range(start = 10, end = 20)try(r@start <- 25)Generics in other packages
Description
You need an explicit external generic when you want to provide methodsfor a generic (S3, S4, or S7) that is defined in another package, and youdon't want to take a hard dependency on that package.
The easiest way to provide methods for generics in other packages isimport the generic into yourNAMESPACE. This, however, creates a harddependency, and sometimes you want a soft dependency, only registering themethod if the package is already installed.new_external_generic() allowsyou to provide the minimal needed information about a generic so that methodscan be registered at run time, as needed, usingmethods_register().
Note that in tests, you'll need to explicitly call the generic from theexternal package withpkg::generic().
Usage
new_external_generic(package, name, dispatch_args, version = NULL)Arguments
package | Package the generic is defined in. |
name | Name of generic, as a string. |
dispatch_args | Character vector giving arguments used for dispatch. |
version | An optional version the package must meet for the method tobe registered. |
Value
An S7 external generic, i.e. a list with classS7_external_generic.
Examples
MyClass <- new_class("MyClass")your_generic <- new_external_generic("stats", "median", "x")method(your_generic, MyClass) <- function(x) "Hi!"Define a new generic
Description
A generic function uses different implementations (methods) depending onthe class of one or more arguments (thesignature). Create a new genericwithnew_generic() then usemethod<- to add methods to it.
Method dispatch is performed byS7_dispatch(), which must always beincluded in the body of the generic, but in most casesnew_generic() willgenerate this for you.
Learn more invignette("generics-methods")
Usage
new_generic(name, dispatch_args, fun = NULL)S7_dispatch()Arguments
name | The name of the generic. This should be the same as the objectthat you assign it to. |
dispatch_args | A character vector giving the names of one or morearguments used to find the method. |
fun | An optional specification of the generic, which must call The |
Value
An S7 generic, i.e. a function with classS7_generic.
Dispatch arguments
The arguments that are used to pick the method are called thedispatcharguments. In most cases, this will be one argument, in which case thegeneric is said to usesingle dispatch. If it consists of more thanone argument, it's said to usemultiple dispatch.
There are two restrictions on the dispatch arguments: they must be the firstarguments to the generic and if the generic uses..., it must occurimmediately after the dispatch arguments.
See Also
new_external_generic() to define a method for a genericin another package without taking a strong dependency on it.
Examples
# A simple generic with methods for some base types and S3 classestype_of <- new_generic("type_of", dispatch_args = "x")method(type_of, class_character) <- function(x, ...) "A character vector"method(type_of, new_S3_class("data.frame")) <- function(x, ...) "A data frame"method(type_of, class_function) <- function(x, ...) "A function"type_of(mtcars)type_of(letters)type_of(mean)# If you want to require that methods implement additional arguments,# you can use a custom function:mean2 <- new_generic("mean2", "x", function(x, ..., na.rm = FALSE) { S7_dispatch()})method(mean2, class_numeric) <- function(x, ..., na.rm = FALSE) { if (na.rm) { x <- x[!is.na(x)] } sum(x) / length(x)}# You'll be warned if you forget the argument:method(mean2, class_character) <- function(x, ...) { stop("Not supported")}Define a new property
Description
A property defines a named component of an object. Properties aretypically used to store (meta) data about an object, and are oftenlimited to a data of a specificclass.
By specifying agetter and/orsetter, you can make the property"dynamic" so that it's computed when accessed or has some non-standardbehaviour when modified. Dynamic properties are not included as an argumentto the default class constructor.
See the "Properties: Common Patterns" section invignette("class-objects")for more examples.
Usage
new_property( class = class_any, getter = NULL, setter = NULL, validator = NULL, default = NULL, name = NULL)Arguments
class | Class that the property must be an instance of.See |
getter | An optional function used to get the value. The functionshould take If a property has a getter but doesn't have a setter, it is read only. |
setter | An optional function used to set the value. The functionshould take |
validator | A function taking a single argument, The job of a validator is to determine whether the property value is valid.It should return The validator will be called after the |
default | When an object is created and the property is not supplied,what should it default to? If |
name | Property name, primarily used for error messages. Generallydon't need to set this here, as it's more convenient to supply asthe element name when defining a list of properties. If both |
Value
An S7 property, i.e. a list with classS7_property.
Examples
# Simple properties store data inside an objectPizza <- new_class("Pizza", properties = list( slices = new_property(class_numeric, default = 10)))my_pizza <- Pizza(slices = 6)my_pizza@slicesmy_pizza@slices <- 5my_pizza@slicesyour_pizza <- Pizza()your_pizza@slices# Dynamic properties can compute on demandClock <- new_class("Clock", properties = list( now = new_property(getter = function(self) Sys.time())))my_clock <- Clock()my_clock@now; Sys.sleep(1)my_clock@now# This property is read only, because there is a 'getter' but not a 'setter'try(my_clock@now <- 10)# Because the property is dynamic, it is not included as an# argument to the default constructortry(Clock(now = 10))args(Clock)Define a class union
Description
A class union represents a list of possible classes. You can create itwithnew_union(a, b, c) ora | b | c. Unions can be used in twoplaces:
To allow a property to be one of a set of classes,
new_property(class_integer | Range). The defaultdefaultvalue for theproperty will be the constructor of the first object in the union.This means if you want to create an "optional" property (i.e. one thatcan beNULLor of a specified type), you'll need to write (e.g.)NULL | class_integer.As a convenient short-hand to define methods for multiple classes.
method(foo, X | Y) <- fis short-hand formethod(foo, X) <- f; method(foo, Y) <- foo
S7 includes built-in unions for "numeric" (integer and double vectors),"atomic" (logical, numeric, complex, character, and raw vectors) and"vector" (atomic vectors, lists, and expressions).
Usage
new_union(...)Arguments
... | The classes to include in the union. See |
Value
An S7 union, i.e. a list with classS7_union.
Examples
logical_or_character <- new_union(class_logical, class_character)logical_or_character# or with shortcut syntaxlogical_or_character <- class_logical | class_characterFoo <- new_class("Foo", properties = list(x = logical_or_character))Foo(x = TRUE)Foo(x = letters[1:5])try(Foo(1:3))bar <- new_generic("bar", "x")# Use built-in unionmethod(bar, class_atomic) <- function(x) "Hi!"barbar(TRUE)bar(letters)try(bar(NULL))Get/set a property
Description
prop(x, "name")/prop@nameget the value of the a property,erroring if it the property doesn't exist.prop(x, "name") <- value/prop@name <- valueset the value ofa property.
Usage
prop(object, name)prop(object, name, check = TRUE) <- valueobject@nameArguments
object | An object from a S7 class |
name | The name of the parameter as a character. Partial matchingis not performed. |
check | If |
value | A new value for the property. The object is automaticallychecked for validity after the replacement is done. |
Value
prop() and@ return the value of the property.prop<-() and@<- are called for their side-effects and returnthe modified object, invisibly.
Examples
Horse <- new_class("Horse", properties = list( name = class_character, colour = class_character, height = class_numeric))lexington <- Horse(colour = "bay", height = 15, name = "Lex")lexington@colourprop(lexington, "colour")lexington@height <- 14prop(lexington, "height") <- 15Property introspection
Description
prop_names(x)returns the names of the propertiesprop_exists(x, "prop")returnsTRUEiifxhas propertyprop.
Usage
prop_names(object)prop_exists(object, name)Arguments
object | An object from a S7 class |
name | The name of the parameter as a character. Partial matchingis not performed. |
Value
prop_names() returns a character vector;prop_exists() returnsa singleTRUE orFALSE.
Examples
Foo <- new_class("Foo", properties = list(a = class_character, b = class_integer))f <- Foo()prop_names(f)prop_exists(f, "a")prop_exists(f, "c")Get/set multiple properties
Description
props(x)returns all properties.props(x) <- list(name1 = val1, name2 = val2)modifies an existing objectby setting multiple properties simultaneously.set_props(x, name1 = val1, name2 = val2)creates a copy of an existingobject with new values for the specified properties.
Usage
props(object, names = prop_names(object))props(object, check = TRUE) <- valueset_props(object, ..., .check = TRUE)Arguments
object | An object from a S7 class |
names | A character vector of property names to retrieve. Default is allproperties. |
check,.check | If |
value | A named list of values. The object is checked for validityonly after all replacements are performed. |
... | Name-value pairs given property to modify and new value. |
Value
A named list of property values.
Examples
Horse <- new_class("Horse", properties = list( name = class_character, colour = class_character, height = class_numeric))lexington <- Horse(colour = "bay", height = 15, name = "Lex")props(lexington)props(lexington) <- list(height = 14, name = "Lexington")lexingtonForce method dispatch to use a superclass
Description
super(from, to) causes the dispatch for the next generic to use the methodfor the superclassto instead of the actual class offrom. It's neededwhen you want to implement a method in terms of the implementation of itssuperclass.
S3 & S4
super() performs a similar role toNextMethod() in S3 ormethods::callNextMethod() in S4, but is much more explicit:
The super class that
super()will use is known when writesuper()(i.e. statically) as opposed to when the generic is called(i.e. dynamically).All arguments to the generic are explicit; they are not automaticallypassed along.
This makessuper() more verbose, but substantially easier tounderstand and reason about.
super() in S3 generics
Note that you can't usesuper() in methods for an S3 generic.For example, imagine that you have made a subclass of "integer":
MyInt <- new_class("MyInt", parent = class_integer, package = NULL)Now you go to write a custom print method:
method(print, MyInt) <- function(x, ...) { cat("<MyInt>") print(super(x, to = class_integer))}MyInt(10L)#> <MyInt>super(<MyInt>, <integer>)This doesn't work becauseprint() isn't an S7 generic so doesn'tunderstand how to interpret the special object thatsuper() produces.While you could resolve this problem withNextMethod() (because S7 isimplemented on top of S3), we instead recommend usingS7_data() to extractthe underlying base object:
method(print, MyInt) <- function(x, ...) { cat("<MyInt>") print(S7_data(x))}MyInt(10L)#> <MyInt>[1] 10Usage
super(from, to)Arguments
from | An S7 object to cast. |
to | An S7 class specification, passed to |
Value
AnS7_super object which should always be passedimmediately to a generic. It has no other special behavior.
Examples
Foo1 <- new_class("Foo1", properties = list(x = class_numeric, y = class_numeric))Foo2 <- new_class("Foo2", Foo1, properties = list(z = class_numeric))total <- new_generic("total", "x")method(total, Foo1) <- function(x) x@x + x@y# This won't work because it'll be stuck in an infinite loop:method(total, Foo2) <- function(x) total(x) + x@z# We could writemethod(total, Foo2) <- function(x) x@x + x@y + x@z# but then we'd need to remember to update it if the implementation# for total(<Foo1>) ever changed.# So instead we use `super()` to call the method for the parent class:method(total, Foo2) <- function(x) total(super(x, to = Foo1)) + x@ztotal(Foo2(1, 2, 3))# To see the difference between convert() and super() we need a# method that calls another genericbar1 <- new_generic("bar1", "x")method(bar1, Foo1) <- function(x) 1method(bar1, Foo2) <- function(x) 2bar2 <- new_generic("bar2", "x")method(bar2, Foo1) <- function(x) c(1, bar1(x))method(bar2, Foo2) <- function(x) c(2, bar1(x))obj <- Foo2(1, 2, 3)bar2(obj)# convert() affects every generic:bar2(convert(obj, to = Foo1))# super() only affects the _next_ call to a generic:bar2(super(obj, to = Foo1))Validate an S7 object
Description
validate() ensures that an S7 object is valid by calling thevalidatorprovided innew_class(). This is done automatically when constructing newobjects and when modifying properties.
valid_eventually() disables validation, modifies the object, thenrevalidates. This is useful when a sequence of operations would otherwiselead an object to be temporarily invalid, or when repeated propertymodification causes a performance bottleneck because the validator isrelatively expensive.
valid_implicitly() does the same but does not validate the object at theend. It should only be used rarely, and in performance critical code whereyou are certain a sequence of operations cannot produce an invalid object.
Usage
validate(object, recursive = TRUE, properties = TRUE)valid_eventually(object, fun)valid_implicitly(object, fun)Arguments
object | An S7 object |
recursive | If |
properties | If |
fun | A function to call on the object before validation. |
Value
Eitherobject invisibly if valid, otherwise an error.
Examples
# A range class might validate that the start is less than the endRange <- new_class("Range", properties = list(start = class_double, end = class_double), validator = function(self) { if (self@start >= self@end) "start must be smaller than end" })# You can't construct an invalid object:try(Range(1, 1))# And you can't create an invalid object with @<-r <- Range(1, 2)try(r@end <- 1)# But what if you want to move a range to the right?rightwards <- function(r, x) { r@start <- r@start + x r@end <- r@end + x r}# This function doesn't work because it creates a temporarily invalid statetry(rightwards(r, 10))# This is the perfect use case for valid_eventually():rightwards <- function(r, x) { valid_eventually(r, function(object) { object@start <- object@start + x object@end <- object@end + x object })}rightwards(r, 10)# Alternatively, you can set multiple properties at once using props<-,# which validates once at the endrightwards <- function(r, x) { props(r) <- list(start = r@start + x, end = r@end + x) r}rightwards(r, 20)