Movatterモバイル変換


[0]ホーム

URL:


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 HesterORCID iD [aut], Tomasz Kalinowski [aut], Will Landau [aut], Michael Lawrence [aut], Martin MaechlerORCID iD [aut], Luke Tierney [aut], Hadley WickhamORCID iD [aut, cre]
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 withnew_class().

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) <- value

Arguments

object

An object from a S7 class

check

IfTRUE, check thatvalue is of the correct type and runvalidate() on the object before returning.

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")y

Does this object inherit from an S7 class?

Description

Usage

S7_inherits(x, class = NULL)check_is_S7(x, class = NULL, arg = deparse(substitute(x)))

Arguments

x

An object

class

An S7 class orNULL. IfNULL, tests whetherx is anS7 object without testing for a specific class.

arg

Argument name used in error message.

Value

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_object

Standard 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:

We also include three union types to model numerics, atomics, and vectorsrespectively:

Usage

class_logicalclass_integerclass_doubleclass_complexclass_characterclass_rawclass_listclass_expressionclass_nameclass_callclass_functionclass_environmentclass_numericclass_atomicclass_vectorclass_language

Value

S7 classes wrapping around common base types and S3 classes.

Examples

class_integerclass_numericclass_factor

S7 wrappers for key S3 classes

Description

S7 bundlesS3 definitions for key S3 classes provided bythe base packages:

Usage

class_factorclass_Dateclass_POSIXctclass_POSIXltclass_POSIXtclass_data.frameclass_formula

Dispatch on any class

Description

Useclass_any to register a default method that is called when no othermethods are matched.

Usage

class_any

Examples

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_missing

Value

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:

convert() provides two default implementations:

  1. Whenfrom inherits fromto, it strips any properties thatfrompossesses thatto does not (downcasting).

  2. Whento is a subclass offrom's class, it creates a new object ofclassto, copying over existing properties fromfrom and 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 toas_class().

...

Other arguments passed to customconvert() methods. For upcasting,these can be used to override existing properties or set new ones.

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() special

Find 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 aclass(processed withas_class()) or a concreteobject. Ifgeneric usesmultiple dispatch then bothobject andclass must be a list ofclasses/objects.

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 with⁠method<-⁠.

The goal is for⁠method<-⁠ 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) <- value

Arguments

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 thegivensignature.

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.

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 aclass(processed withas_class()) or a concreteobject. Ifgeneric usesmultiple dispatch then bothobject andclass must be a list ofclasses/objects.

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. whatclass() returns). For methodregistration, you can abbreviate this to a single string, the S3 classname.

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.data(the base type whose attributes will be modified).

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 byvalidate() to check thatthe S7 object adheres to the constraints of the S3 class.

A validator is a single argument function that takes the object tovalidate and returnsNULL if the object is valid. If the object isinvalid, it returns a character vector of problems.

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:

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 callingnew_class() should always be assigned to a variable with this name,i.e.Foo <- new_class("Foo").

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, andNULL otherwise.

Note, if the class is intended for external use, the constructor should beexported. Learn more invignette("packages").

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 byas_class()) or afull property specification creatednew_property().

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 callnew_object() to create the S7 object.The first argument,.data, should be an instance of the parent class(if used). The subsequent arguments are used to set the properties.

validator

A function taking a single argument,self, the objectto validate.

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 returnNULL if the object is valid. If not, itshould return a character vector where each element describes a singleproblem, using⁠@prop_name⁠ to describe where the problem lies.

Seevalidate() for more details, examples, and how to temporarilysuppress validation when needed.

.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 callS7_dispatch() to dispatch to methods. This is usually generatedautomatically from thedispatch_args, but you may want to supply it ifyou want to add additional required arguments, omit..., or performsome standardised computation in the generic.

Thedispatch_args must be the first arguments tofun, and, if present,... must immediately follow them.

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.Seeas_class() for details.

getter

An optional function used to get the value. The functionshould takeself as its sole argument and return the value. If yousupply agetter, you are responsible for ensuring that it returnsan object of the correctclass; it will not be validated automatically.

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 takeself andvalue and return a modified object.

validator

A function taking a single argument,value, the valueto validate.

The job of a validator is to determine whether the property value is valid.It should returnNULL if the object is valid, or if it's not valid,a single string describing the problem. The message should not include thename of the property as this will be automatically appended to thebeginning of the message.

The validator will be called after theclass has been verified, soyour code can assume thatvalue has known type.

default

When an object is created and the property is not supplied,what should it default to? IfNULL, it defaults to the "empty" instanceofclass. This can also be a quoted call, which then becomes a standardfunction promise in the default constructor, evaluated at the time theobject is constructed.

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 bothnameand a list-name are supplied, the list-name will be used.

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:

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. Seeas_class() fordetails.

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

Usage

prop(object, name)prop(object, name, check = TRUE) <- valueobject@name

Arguments

object

An object from a S7 class

name

The name of the parameter as a character. Partial matchingis not performed.

check

IfTRUE, check thatvalue is of the correct type and runvalidate() on the object before returning.

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") <- 15

Property introspection

Description

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

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

IfTRUE, runvalidate() on the objectbefore returning.

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")lexington

Force 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:

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] 10

Usage

super(from, to)

Arguments

from

An S7 object to cast.

to

An S7 class specification, passed toas_class(). Must be asuperclass ofobject.

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

IfTRUE, calls validator of parent classes recursively.

properties

IfTRUE, the default, checks property types beforeexecuting the validator.

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)

[8]ページ先頭

©2009-2025 Movatter.jp