Changelog
Source:NEWS.md
S7 1.0.0
Breaking changes
convert()now errors when upcasting to an abstract class. Use a concrete target class instead (#680, #686).convert()now restricts default downcasts to descendants of the source class. Converting between sibling classes requires an explicitconvert()method, rather than relying on a shared ancestor (#509).method<-()now leaves a temporary placeholder, rather than a copy of an external generic, in your package namespace. AddS7_on_build()at the top level ofzzz.R, after all method registrations, to remove these placeholders when the package is built (#364).new_class()changes the default constructor for subclasses of classes with custom constructors. The subclass constructor now takes..., followed by named arguments for its new properties. It forwards...to the parent instead of exposing the parent’s properties as individual arguments. Update calls to use the parent constructor’s arguments, and re-document affected constructors (#609, #317).new_class()now calls constructors of S7 parents from other packages at run time when the subclass is defined in a package, so subclasses use the installed parent constructor without needingnew_external_class(). Such subclass constructors now accept parent arguments through...(#763).new_class()now reserves property names beginning with_for internal use. Rename any properties that use this prefix (#579).new_object()andset_props()now name their first arguments_parentand_object, respectively. Update calls that name these arguments, or pass the first argument positionally (#423).
New features
Classes and properties
New
:=operator creates and names an object in one step:Foo := new_class()is equivalent toFoo <- new_class(name = "Foo"). S7’s:=takes precedence over rlang and data.table regardless of attachment order, without emitting masking messages (#658, #697).new_class()now supports S4 classes as parents, mapping S4 slots to S7 properties and registering the new class with S4 automatically. Conversely,S4_register()registers an S7 class with S4, andS4_contains()supplies a class name formethods::setClass(contains = ), exposing stored S7 properties as slots for S4 subclasses. This includes integration with S4 initialization, validity checking, and S4/internal generic registration. Seevignette("compatibility")for details (#456).new_class()now allows properties namednames,dim,dimnames,class,comment,tsp, androw.names. Property names beginning with_are reserved for internal use (#579).new_class()experimentally supportsclass_environmentas a parent, allowing S7 objects with reference semantics. Environments are modified in place, so some operations differ from those on value-typed objects, and the API may change.S7_data()andS7_data<-()error on these objects because they would otherwise remove the S7 attributes in place (#590).new_external_class()creates a delayed reference to an S7 class in another package, or a class in your own package that is not yet defined. This supports method registration for suggested packages (#573), self-referential and mutually recursive classes (#250).new_object()now accepts a named list of property values, passed as a single unnamed argument through...(#497). Its first argument is now named_parentto avoid clashes with property names (#423).new_property()accepts an optional property name argument in getters (function(self, name)) and setters (function(self, name, value)), so the same accessor can be reused for multiple properties (#552).new_S3_class()gains adefaultargument for supplying a quoted property default independently of its constructor (#755).new_S3_class()gains an optionalattributesargument declaring the complete set of attributes to preserve when constructing S7 subclasses. The bundled concrete S3 wrappers declare their attributes, sonew_object()strips foreign attributes from their subclasses (#760).set_props()now accepts a named list of property values, passed as a single unnamed argument through...(#497). Its first argument is now named_objectto avoid clashes with property names (#423).
Generics and methods
method()andmethod<-()accept a length-one list assignaturefor single-dispatch generics, matching the list-of-classes form used for multiple dispatch (#555).method<-()can register methods on S3 and S4 generics for base types, S3 classes, S7 unions,class_any, andNULL. Unions expand to one registration per class;class_anyandNULLregister as thedefaultandNULLmethods, respectively (#455).method<-()supports double-dispatch operators such as+,==, and%*%with plain S3 or S4 classes, even when neither operand is an S7 object (#544). It also supports unary+,-, and!methods (#531).method<-()acceptsNULLto unregister an existing method, e.g.method(foo, class_character) <- NULL(#613).super()now works with S3 and S4 objects, not just S7 objects (#500).
Conversion
convert()falls back to the correspondingas.*()function when converting to a base type and no method or inheritance-based default applies. For example,convert(1, class_character)usesas.character()(#472).convert()now accepts a named list of property overrides when downcasting, passed as a single unnamed argument through...(#497).New
convert_lazy()is a non-strict variant ofconvert()that returnsfromunchanged if it already inherits fromto, preserving the more specific class and any extra properties instead of stripping them (#428).
Introspection and debugging
print()for S7 classes now shows property defaults inline and marks read-only properties with[read-only](#439).New
prop_info()returns a data frame describing an object’s or class’s properties, with one row per property and columns for name, default, class, getter, setter, and validator (#551).S7_class()returns a class specification for any R object, not just S7 objects: aclass_*wrapper for base types, anew_S3_class()wrapper for S3 objects, or an S4 class for S4 objects. The result can be passed directly tomethod()and other S7 dispatch helpers (#559).New
S7_class_desc()formats a class specification as a short, human-readable string (#594).New
S7_classes()andS7_generics()list the S7 classes and generics defined in an environment or package (#335).New
S7_generic_call(),S7_generic_fun(), andS7_user_frame()provide access to a method’s generic call, generic function, and caller frame (#596).S7_inherits()andcheck_is_S7()accept any class specification, including S7 unions, S3 and S4 classes, and base type wrappers (#556).New
S7_methods()lists methods registered on a generic, or methods associated with a class across generics in attached packages (#435).trace()anduntrace()work with S7 generics and methods, enabling standard debugging workflows. For example,trace("myclass", browser, where = my_generic@methods)sets a breakpoint in the method formyclass(#584).
Package development
New
vignette("evolution")explains how to evolve S7 classes and generics without breaking downstream packages, and what downstream authors can do to smooth transitions (#143).New
deprecated_class(),deprecated_generic(), anddeprecated_property()help package authors deprecate APIs while keeping existing code working. A deprecated class warns on construction while preserving its methods and subclasses; an optionalnewis recommended in the warning. Withalias = TRUE, the deprecated class name instead uses the replacement’s constructor and class identity; existing objects and subclasses are not migrated. A deprecated generic can warn or forward calls and method registrations to a replacement supplied asnew. Deprecated properties can forward reads and writes to a replacement property. Warnings can use base R or lifecycle (#727, #730).New
S7_on_build()removes the temporary placeholders returned bymethod<-()for generics owned by other packages, avoiding embedded copies of those generics in your namespace. Call it at the top level ofzzz.R, after all method registrations, not inside.onLoad(). Seevignette("packages")for details (#364).S7_on_load()is the new name formethods_register(), which remains available for backward compatibility. Call it from.onLoad()to register methods (#615).New
S7_on_unload()unregisters active methods and removes hooks added byS7_on_load(). Call it from.onUnload()(#316).
Performance
new_object()is faster thanks to cached class names and dispatch vectors and faster class type detection. In benchmarks, construction is 1.4x faster for a direct subclass ofS7_objectand 2.4x faster for a class with 10 ancestors.S7_inherits()is 1.5x faster andsuper()is 2.1x faster (#723).new_object()stores a shared internal class reference instead of copying the class on each construction. Objects serialized together also share this reference. Constructors created by older versions of S7 continue to work (#742).new_object()preserves ALTREP parent values, so wrapping a large compact integer sequence uses O(1) rather than O(n) memory (@kschaubroeck, #607).new_object()avoids re-running validators for properties inherited unchanged from an already-validated parent, so each property is validated only once when constructing deeply nested subclasses (#539).new_object()andvalidate()are 25-30% faster through direct access to class metadata. Avoiding duplicate checks for base type properties also makes construction 1.4x faster for classes with 10 such properties and 1.6x faster for classes with 50. On R 4.3 and later, S7 uses base R’s@directly (#723).
Bug fixes and minor improvements
Classes and construction
Base type wrappers such as
class_integernow define their constructors and validators in the S7 namespace (#553).class_POSIXctuses thetzoneattribute, rather thantz, and allows it to be absent (#401).new_class()now rejects property overrides whose type does not extend the parent’s property type, since such classes cannot be instantiated (#352, #708).new_class()generates compact property defaults for bundled concrete S3 wrappers, keeping constructor bodies out of generated documentation (#755).new_class()generates a working default constructor when the parent has a custom constructor. It forwards...to the parent, allowing the parent to match and evaluate its own argument defaults. Properties added by the subclass follow...as named arguments (#609, #317).new_class()uses the subclass’s default and setter for overridden properties. Override values are passed to both the parent constructor and the new object, allowing subclasses to override parent properties with mandatory defaults (#467, #585).new_external_class()accepts exported aliases, so references continue to work when an old name points to a renamed or moved class (#727).new_object()allows an abstract class’s constructor to run while constructing the parent part of a subclass. This supports subclasses of abstract classes in other packages referenced throughnew_external_class()(#717).new_object()gives an informative error when_parentis a class specification rather than an instance of the parent class (#409).new_object()now strips foreign attributes, including properties of sibling classes, when constructing classes rooted inS7_object(#760).new_S3_class()objects work withinherits()and other functions that usenameOfClass()on R 4.3 and later (@lawremi, #521).S7_class()reads class metadata from the internal_S7_classattribute, previously namedS7_class, avoiding collisions with user-defined properties. Objects created by older versions of S7, including saved objects and objects in installed packages, remain supported. UseS7_class()rather than accessing the attribute directly (#677).
Conversion and underlying data
convert()returnsfromunchanged when it already has the target class. When upcasting a more specific subclass, dispatch is restricted to classes more specific thanto, so an inherited downcasting method is not selected in place of an upcast (#429).convert()now errors when upcasting to an abstract class instead of creating an instance of that class (#680, #686).convert()only applies the default downcast whentois a descendant of the source class, rather than converting between sibling classes (#509).convert()supports conversion from base and S3 objects to S7 subclasses of their class, passing the source value as.datato the target constructor (#537).S7_data()preserves the S3 class when the S7 class inherits from an S3 class. For example, extracting data from an S7 subclass of data.frame returns a data.frame (#380).S7_data<-()preserves attributes such asnamesanddimfrom the replacement data, rather than retaining the originals, so resizing the underlying data works correctly (#478).
Generics and method registration
Dispatch on
class_missingcorrectly handles missing arguments forwarded through wrapper functions (#595).Operator methods propagate missing-method errors raised inside their bodies instead of silently falling back to base behavior (#490).
method<-()gives a clear error when a primitive function, such aslog, is assigned as a method (#608).method<-()silently accepts re-registration of an identical method, avoiding spurious “Overwriting method” messages fromdevtools::load_all()(#474).method<-()checks method signatures for consistency with their generics only in development contexts: duringpkgload::load_all(), whenR CMD checkchecks a package involved in the registration, or when registering a method outside a package. Users of installed packages no longer receive these warnings or errors after an upstream generic changes. Registrations inside testthat tests are treated as an end-user context, keeping local tests consistent withR CMD check(#726, #728).method<-()warns and skips registration of incompatible methods duringpkgload::load_all(), rather than stopping with an error. This lets a package remain sourceable while its methods are updated to match a changed generic (#726).S7_on_load()avoids accumulating duplicate registration hooks when a package is loaded repeatedly (#316).S7_on_load()warns and skips registration when an upstream generic has been renamed or removed, allowing the downstream package to load. It also resolves generics through package exports, so generics can move between packages and be re-exported without breaking installed downstream packages (#729).
Properties and validation
new_property()runs the property class’s own validator when checking values, in addition to checking their class. Properties restricted to an S3 class such asclass_factornow enforce constraints that are not visible inclass()(#401).new_property()warns whendefaultis a complex value, such as a named vector, that would be inlined into the constructor and could causeR CMD checkfailures. Wrap these defaults inquote(). Empty vectors without attributes can be supplied directly, e.g.integer()orlist(). This warning will become an error in a future release (#541).prop()keeps objects usable after a custom getter signals an error (#520, #640, #638).prop<-()supports assigning calls and symbols to properties (#511, #633, #638).
Errors and printing
Errors from S7 report the function where they occurred (#646).
S7_error_method_not_foundhas a correct class vector without a duplicate"error"entry (@jjjermiah, #604).print()andstr()omit properties created bydeprecated_property()(#754).prop()andprop<-()report errors from getters and setters with a synthetic<Class>@<prop>call, identifying the property that triggered the error (#416, #536, #638).S7_dispatch()preserves argument expressions in method calls on R < 4.4, so errors and warnings can be printed without evaluating argument promises (#783).S7_dispatch()gives a clear error when called from a function that is not an S7 generic, such asunclass(generic)()(#684).str()works for S7 objects that inherit from data.frame and other S3 classes whosedimattribute is incompatible with the bare underlying type (#494).validate()signals validation errors with classS7_error_validation_failed, allowing them to be caught withtryCatch()(#602, #605).
S7 0.2.1
CRAN release: 2025-11-14
props<-()andset_props()gaincheck/.checkarguments, letting you set properties without callingvalidate()(#574, #575).Internal changes to support R-devel (4.6) (#577).
S7 0.2.0
CRAN release: 2024-11-07
New features
The default object constructor returned by
new_class()has been updated. It now accepts lazy (promise) property defaults and includes dynamic properties with asetterin the constructor. Additionally, all custom property setters are now consistently invoked by the default constructor. If you’re using S7 in an R package, you’ll need to re-document to ensure that your documentation matches the updated usage (#438, #445).The call context of a dispatched method (as visible in
sys.calls()andtraceback()) no longer includes the inlined method and generic, resulting in more compact and readable tracebacks. The dispatched method call now contains only the method name, which serves as a hint for retrieving the method. For example:method(my_generic, class_double)(x=10, …). (#486)New
nameOfClass()method exported for S7 base classes, to enable usage likeinherits("foo", S7::class_character)(#432, #458)Added support for more base/S3 classes (#434):
class_POSIXlt,class_POSIXt,class_formula,class_call,class_language, andclass_name.S7 provides a new automatic backward compatibility mechanism to provide a version of
@that works in R before version 4.3 (#326).
Bug fixes and minor improvements
new_class()now automatically infers the package name when called from within an R package (#459).Improved error message when custom validators return invalid values (#454, #457).
Fixed S3 methods registration across packages (#422).
convert()now provides a default method to transform a parent class instance into a subclass, enabling class construction from a prototype (#444).A custom property
getter()no longer infinitely recurses when accessing itself (reported in #403, fixed in #406).method()generates an informative message with classS7_error_method_not_foundwhen dispatch fails (#387).method<-()can create multimethods that dispatch onNULL.In
new_class(), properties can either be named by naming the element of the list or by supplying thenameargument tonew_property()(#371).The
Opsgeneric now falls back to base Ops behaviour when one of the arguments is not an S7 object (#320). This means that you get the somewhat inconsistent base behaviour, but means that S7 doesn’t introduce a new axis of inconsistency.prop()(#395) andprop<-/@<-(#396) have been optimized and rewritten in C.validate()is now always called after a custom property setter was invoked (reported in #393, fixed in #396).
S7 0.1.1
CRAN release: 2023-09-17
Classes get a more informative print method (#346).
Correctly register S3 methods for S7 objects with a package (#333).
External methods are now registered using an attribute of the S3 methods table rather than an element of that environment. This prevents a warning being generated during the “code/documentation mismatches” check in
R CMD check(#342).class_missingandclass_anycan now be unioned with|(#337).new_object()no longer acceptsNULLas.parent.new_object()now correctly runs the validator from abstract parent classes (#329).new_object()works better when custom property setters modify other properties.new_property()gains avalidatorargument that allows you to specify a per-property validator (#275).new_property()clarifies that it’s the user’s responsibility to return the correct class; it is not automatically validated.Properties with a custom setter are now validated after the setter has run and are validated when the object is constructed or when you call
validate(), not just when you modify them after construction.S7_inherits()now acceptsclass = NULLto test if an object is any sort of S7 object (#347).
S7 0.1.0
CRAN release: 2023-08-24
May-July 2023
new_external_generic()is only needed when you want a soft dependency on another package.methods_register()now also registers S3 and S4 methods (#306).
Jan-May 2023
Subclasses of abstract class can have readonly properties (#269).
During construction, validation is now only performed once for each element of the class hierarchy (#248).
Implemented a better filtering strategy for the S4 class hierarchy so you can now correctly dispatch on virtual classes (#252).
New
set_props()to make a modified copy of an object (#229).R CMD checknow passes on R 3.5 and greater (for tidyverse compatibility).Dispatching on an evaluated argument no longer causes a crash (#254).
Improve method dispatch failure message (#231).
Can use
|to create unions from S7 classes (#224).Can no longer subclass an environment via
class_environmentbecause we need to think the consequences of this behaviour through more fully (#253).
Rest of 2022
Add
[.S7_object,[<-.S7_object,[[.S7_object, and[[<-.S7_objectmethods to avoid “object of type ‘S4’ is not subsettable” error (@jamieRowen, #236).Base classes now show as
class_xinstead of"x"in method print (#232)
Mar 2022
Exported
class_factor,class_Date,class_POSIXct, andclass_data.frame.New
S7_inherits()andcheck_is_S7()(#193)new_class()can create abstract classes (#199).method_call()is nowS7_dispatch()(#200).Can now register methods for double-dispatch base Ops (currently only works if both classes are S7, or the first argument is S7 and the second doesn’t have a method for the Ops generic) (#128).
All built-in wrappers around base types use
class_. You can no longer refer to a base type with a string or a constructor function (#170).convert()allows you to convert an object into another class (#136).
Feb 2022
class_anyandclass_missingmake it possible to dispatch on absent arguments and arguments of any class (#67).New
method_explain()to explain dispatch (#194).Minor property improvements: use same syntax for naming short-hand and full property specifications; input type automatically validated for custom setters. A property with a getter but no setter is read-only (#168).
When creating an object, unspecified properties are initialized with their default value (#67). DISCUSS: to achieve this, the constructor arguments default to
class_missing.Add
$.S7_objectand$<-.S7_objectmethods to avoid “object of type ‘S4’ is not subsettable” error (#204).Dispatch now disambiguates between S4 and S3/S7, and, optionally, between S7 classes in different packages (#48, #163).
new_generic()now requiresdispatch_args(#180). This means thatnew_generic()will typically be called without names. Eithernew_generic("foo", "x")for a “standard” generic, ornew_generic("foo", "x", function(x, y) call_method())for a non-standard method.new_external_generic()now requiresdispatch_argsso we can eagerly check the signature.Revamp website. README now shows brief example and more info in
vignette("S7"). Initial design docs and minutes are now articles so they appear on the website.
Jan 2022
- New
props<-for setting multiple properties simultaneously and validating afterwards (#149). - Validation now happens recursively, and validates types before validating the object (#149)
- Classes (base types, S3, S4, and S7) are handled consistently wherever they are used. Strings now only refer to base types. New explicit
new_S3_class()for referring to S3 classes (#134). S4 unions are converted to S7 unions (#150). - Base numeric, atomic, and vector “types” are now represented as class unions (#147).
- Different evaluation mechanism for method dispatch, and greater restrictions on dispatch args (#141)
-
x@.data->S7_data(); probably to be replaced by casting. - In generic,
signature->dispatch_args. - Polished
str()andprint()methods -
new_class()has properties as 3rd argument (instead of constructor).