horse <- new_class("horse", properties = list(
+ Horse <- new_class("Horse", properties = list(
name = class_character,
colour = class_character,
height = class_numeric
))
-lexington <- horse(colour = "bay", height = 15, name = "Lex")
+lexington <- Horse(colour = "bay", height = 15, name = "Lex")
lexington@colour
#> [1] "bay"
prop(lexington, "colour")
diff --git a/reference/prop_names.html b/reference/prop_names.html
index 84c3a6d9..19e2a42d 100644
--- a/reference/prop_names.html
+++ b/reference/prop_names.html
@@ -89,8 +89,8 @@ Value
Examples
- foo <- new_class("foo", properties = list(a = class_character, b = class_integer))
-f <- foo()
+ Foo <- new_class("Foo", properties = list(a = class_character, b = class_integer))
+f <- Foo()
prop_names(f)
#> [1] "a" "b"
diff --git a/reference/props.html b/reference/props.html
index d525bca8..ac2084db 100644
--- a/reference/props.html
+++ b/reference/props.html
@@ -108,12 +108,12 @@ Value
Examples
-
@@ -222,46 +222,46 @@ Value
Examples
- foo1 <- new_class("foo1", properties = list(x = class_numeric, y = class_numeric))
-foo2 <- new_class("foo2", foo1, properties = list(z = class_numeric))
+ 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
+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
+method(total, Foo2) <- function(x) total(x) + x@z
# We could write
-method(total, foo2) <- function(x) x@x + x@y + x@z
-#> Overwriting method total(<foo2>)
+method(total, Foo2) <- function(x) x@x + x@y + x@z
+#> Overwriting method total(<Foo2>)
# but then we'd need to remember to update it if the implementation
-# for total(<foo1>) ever changed.
+# 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@z
-#> Overwriting method total(<foo2>)
-total(foo2(1, 2, 3))
+method(total, Foo2) <- function(x) total(super(x, to = Foo1)) + x@z
+#> Overwriting method total(<Foo2>)
+total(Foo2(1, 2, 3))
#> [1] 6
# To see the difference between convert() and super() we need a
# method that calls another generic
bar1 <- new_generic("bar1", "x")
-method(bar1, foo1) <- function(x) 1
-method(bar1, foo2) <- function(x) 2
+method(bar1, Foo1) <- function(x) 1
+method(bar1, Foo2) <- function(x) 2
bar2 <- new_generic("bar2", "x")
-method(bar2, foo1) <- function(x) c(1, bar1(x))
-method(bar2, foo2) <- function(x) c(2, bar1(x))
+method(bar2, Foo1) <- function(x) c(1, bar1(x))
+method(bar2, Foo2) <- function(x) c(2, bar1(x))
-obj <- foo2(1, 2, 3)
+obj <- Foo2(1, 2, 3)
bar2(obj)
#> [1] 2 2
# convert() affects every generic:
-bar2(convert(obj, to = foo1))
+bar2(convert(obj, to = Foo1))
#> [1] 1 1
# super() only affects the _next_ call to a generic:
-bar2(super(obj, to = foo1))
+bar2(super(obj, to = Foo1))
#> [1] 1 2
diff --git a/search.json b/search.json
index ae65b1cd..d5ff787d 100644
--- a/search.json
+++ b/search.json
@@ -1 +1 @@
-[{"path":"https://rconsortium.github.io/S7/LICENSE.html","id":null,"dir":"","previous_headings":"","what":"MIT License","title":"MIT License","text":"Copyright (c) 2021 S7 authors Permission hereby granted, free charge, person obtaining copy software associated documentation files (“Software”), deal Software without restriction, including without limitation rights use, copy, modify, merge, publish, distribute, sublicense, /sell copies Software, permit persons Software furnished , subject following conditions: copyright notice permission notice shall included copies substantial portions Software. SOFTWARE PROVIDED “”, WITHOUT WARRANTY KIND, EXPRESS IMPLIED, INCLUDING LIMITED WARRANTIES MERCHANTABILITY, FITNESS PARTICULAR PURPOSE NONINFRINGEMENT. EVENT SHALL AUTHORS COPYRIGHT HOLDERS LIABLE CLAIM, DAMAGES LIABILITY, WHETHER ACTION CONTRACT, TORT OTHERWISE, ARISING , CONNECTION SOFTWARE USE DEALINGS SOFTWARE.","code":""},{"path":"https://rconsortium.github.io/S7/articles/S7.html","id":"classes-and-objects","dir":"Articles","previous_headings":"","what":"Classes and objects","title":"S7 basics","text":"S7 classes formal definition create new_class(). two arguments ’ll use almost every class: name class, supplied first argument. class properties, data associated instance class. easiest way define properties supply named list values define valid types property. following code defines simple dog class two properties: character name numeric age. S7 provides number built-definitions allow refer existing base types S7 classes. can recognize definitions start class_. Note ’ve assigned return value new_class() object name class. important! object represents class use construct instances class: S7 object, can get set properties using @: S7 automatically validates type property using type supplied new_class(): Given object, can retrieves class S7_class(): S7 objects also S3 class(). used compatibility existing S3 generics can learn vignette(\"compatibility\"). want learn details S7 classes objects, including validation methods details properties, please see vignette(\"classes-objects\").","code":"dog <- new_class(\"dog\", properties = list( name = class_character, age = class_numeric )) dog #> class #> @ parent : #> @ constructor: function(name, age) {...} #> @ validator : #> @ properties : #> $ name: #> $ age : or lola <- dog(name = \"Lola\", age = 11) lola #> #> @ name: chr \"Lola\" #> @ age : num 11 lola@age <- 12 lola@age #> [1] 12 lola@age <- \"twelve\" #> Error: @age must be or , not S7_class(lola) #> class #> @ parent : #> @ constructor: function(name, age) {...} #> @ validator : #> @ properties : #> $ name: #> $ age : or class(lola) #> [1] \"dog\" \"S7_object\""},{"path":"https://rconsortium.github.io/S7/articles/S7.html","id":"generics-and-methods","dir":"Articles","previous_headings":"","what":"Generics and methods","title":"S7 basics","text":"S7, like S3 S4, built around idea generic functions, generics short. generic defines interface, uses different implementation depending class one arguments. implementation specific class called method, generic finds appropriate method performing method dispatch. Use new_generic() create S7 generic. simplest form, needs two arguments: name generic (used error messages) name argument used method dispatch: Like new_class(), always assign result new_generic() variable name first argument. generic, can register methods specific classes method(generic, class) <- implementation. method registered, generic use appropriate: Let’s define another class, one cats, define another method speak(): get error call generic class doesn’t method:","code":"speak <- new_generic(\"speak\", \"x\") method(speak, dog) <- function(x) { \"Woof\" } speak(lola) #> [1] \"Woof\" cat <- new_class(\"cat\", properties = list( name = class_character, age = class_double )) method(speak, cat) <- function(x) { \"Meow\" } fluffy <- cat(name = \"Fluffy\", age = 5) speak(fluffy) #> [1] \"Meow\" speak(1) #> Error: Can't find method for `speak()`."},{"path":"https://rconsortium.github.io/S7/articles/S7.html","id":"method-dispatch-and-inheritance","dir":"Articles","previous_headings":"","what":"Method dispatch and inheritance","title":"S7 basics","text":"cat dog classes share properties, use common parent class extract duplicated specification. first define parent class: use parent argument new_class: created new classes, need recreate existing lola fluffy objects: Method dispatch takes advantage hierarchy parent classes: method defined class, try method parent class, finds method gives error. inheritance powerful mechanism sharing code across classes. can define fallback method S7 object registering method S7_object: Printing generic show methods currently defined: can use method() retrieve implementation one methods: Learn method dispatch vignette(\"generics-methods\").","code":"pet <- new_class(\"pet\", properties = list( name = class_character, age = class_numeric ) ) cat <- new_class(\"cat\", parent = pet) dog <- new_class(\"dog\", parent = pet) cat #> class #> @ parent : #> @ constructor: function(name, age) {...} #> @ validator : #> @ properties : #> $ name: #> $ age : or dog #> class #> @ parent : #> @ constructor: function(name, age) {...} #> @ validator : #> @ properties : #> $ name: #> $ age : or lola <- dog(name = \"Lola\", age = 11) fluffy <- cat(name = \"Fluffy\", age = 5) describe <- new_generic(\"describe\", \"x\") method(describe, pet) <- function(x) { paste0(x@name, \" is \", x@age, \" years old\") } describe(lola) #> [1] \"Lola is 11 years old\" describe(fluffy) #> [1] \"Fluffy is 5 years old\" method(describe, dog) <- function(x) { paste0(x@name, \" is a \", x@age, \" year old dog\") } describe(lola) #> [1] \"Lola is a 11 year old dog\" describe(fluffy) #> [1] \"Fluffy is 5 years old\" method(describe, S7_object) <- function(x) { \"An S7 object\" } cocktail <- new_class(\"cocktail\", properties = list( ingredients = class_character ) ) martini <- cocktail(ingredients = c(\"gin\", \"vermouth\")) describe(martini) #> [1] \"An S7 object\" describe #> describe(x, ...) with 3 methods: #> 1: method(describe, pet) #> 2: method(describe, dog) #> 3: method(describe, S7_object) method(describe, pet) #> method(describe, pet) #> function (x) #> { #> paste0(x@name, \" is \", x@age, \" years old\") #> } #> "},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"validation","dir":"Articles","previous_headings":"","what":"Validation","title":"Classes and objects","text":"S7 classes can optional validator checks values properties OK. validator function takes object (called self) returns NULL valid returns character vector listing problems.","code":""},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"basics","dir":"Articles","previous_headings":"Validation","what":"Basics","title":"Classes and objects","text":"following example create range class enforces @start @end single numbers, @start less @end: can typically write validator series -else statements, note order statements important. example, code , can’t check self@end < self@start ’ve checked @start @end length 1. ’ll discuss shortly, can also perform validation per-property basis, generally class validators reserved interactions properties.","code":"range <- new_class(\"range\", properties = list( start = class_double, end = class_double ), validator = function(self) { if (length(self@start) != 1) { \"@start must be length 1\" } else if (length(self@end) != 1) { \"@end must be length 1\" } else if (self@end < self@start) { sprintf( \"@end (%i) must be greater than or equal to @start (%i)\", self@end, self@start ) } } )"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"when-is-validation-performed","dir":"Articles","previous_headings":"Validation","what":"When is validation performed?","title":"Classes and objects","text":"Objects validated automatically constructed property modified: can also manually validate() object use low-level R function bypass usual checks balances @:","code":"x <- range(1, 2:3) #> Error: object properties are invalid: #> - @end must be , not x <- range(10, 1) #> Error: object is invalid: #> - @end (1) must be greater than or equal to @start (10) x <- range(1, 10) x@start <- 20 #> Error: object is invalid: #> - @end (10) must be greater than or equal to @start (20) x <- range(1, 2) attr(x, \"start\") <- 3 validate(x) #> Error: object is invalid: #> - @end (2) must be greater than or equal to @start (3)"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"avoiding-validation","dir":"Articles","previous_headings":"Validation","what":"Avoiding validation","title":"Classes and objects","text":"Imagine wanted write function shift property left right: ’s problem shift larger @end - @start: end result shift() valid, intermediate state . easiest way resolve problem set properties : object still validated, ’s validated , properties modified.","code":"shift <- function(x, shift) { x@start <- x@start + shift x@end <- x@end + shift x } shift(range(1, 10), 1) #> #> @ start: num 2 #> @ end : num 11 shift(range(1, 10), 10) #> Error: object is invalid: #> - @end (10) must be greater than or equal to @start (11) shift <- function(x, shift) { props(x) <- list( start = x@start + shift, end = x@end + shift ) x } shift(range(1, 10), 10) #> #> @ start: num 11 #> @ end : num 20"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"properties","dir":"Articles","previous_headings":"","what":"Properties","title":"Classes and objects","text":"far ’ve focused simplest form property specification use named list supply desired type property. convenient shorthand call new_property(). example, property definition range shorthand : Calling new_property() explicitly allows control aspects property type. following sections show add validator, provide default value, compute property value demand, provide fully dynamic property.","code":"range <- new_class(\"range\", properties = list( start = new_property(class_double), end = new_property(class_double) ) )"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"validation-1","dir":"Articles","previous_headings":"Properties","what":"Validation","title":"Classes and objects","text":"can optionally provide validator property. example, instead validating length start end validator Range class, implement property level: Note property validators shouldn’t include name property validation messages S7 add automatically. makes possible use property definition multiple properties type, .","code":"prop_number <- new_property( class = class_double, validator = function(value) { if (length(value) != 1L) \"must be length 1\" } ) range <- new_class(\"range\", properties = list( start = prop_number, end = prop_number ), validator = function(self) { if (self@end < self@start) { sprintf( \"@end (%i) must be greater than or equal to @start (%i)\", self@end, self@start ) } } ) range(start = c(1.5, 3.5)) #> Error: object properties are invalid: #> - @start must be length 1 #> - @end must be length 1 range(end = c(1.5, 3.5)) #> Error: object properties are invalid: #> - @start must be length 1 #> - @end must be length 1"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"default-value","dir":"Articles","previous_headings":"Properties","what":"Default value","title":"Classes and objects","text":"defaults new_class() create class can constructed arguments: default values properties filled “empty” instances. can instead provide defaults using default argument: quoted call becomes standard function promise default constructor, evaluated time object constructed.","code":"empty <- new_class(\"empty\", properties = list( x = class_double, y = class_character, z = class_logical )) empty() #> #> @ x: num(0) #> @ y: chr(0) #> @ z: logi(0) empty <- new_class(\"empty\", properties = list( x = new_property(class_numeric, default = 0), y = new_property(class_character, default = \"\"), z = new_property(class_logical, default = NA) ) ) empty() #> #> @ x: num 0 #> @ y: chr \"\" #> @ z: logi NA stopwatch <- new_class(\"stopwatch\", properties = list( start_time = new_property( class = class_POSIXct, default = quote(Sys.time()) ), elapsed = new_property( getter = function(self) { difftime(Sys.time(), self@start_time, units = \"secs\") } ) )) args(stopwatch) #> function (start_time = Sys.time()) #> NULL round(stopwatch()@elapsed) #> Time difference of 0 secs round(stopwatch(Sys.time() - 1)@elapsed) #> Time difference of 1 secs"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"computed-properties","dir":"Articles","previous_headings":"Properties","what":"Computed properties","title":"Classes and objects","text":"’s sometimes useful property computed demand. example, ’d convenient pretend range length, just distance @start @end. can dynamically compute value property defining getter: Computed properties read-:","code":"range <- new_class(\"range\", properties = list( start = class_double, end = class_double, length = new_property( getter = function(self) self@end - self@start, ) ) ) x <- range(start = 1, end = 10) x #> #> @ start : num 1 #> @ end : num 10 #> @ length: num 9 x@length <- 20 #> Error: Can't set read-only property @length"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"dynamic-properties","dir":"Articles","previous_headings":"Properties","what":"Dynamic properties","title":"Classes and objects","text":"can make computed property fully dynamic can read written also supplying setter. setter function arguments self value returns modified object. example, extend previous example allow @length set, modifying @end vector:","code":"range <- new_class(\"range\", properties = list( start = class_double, end = class_double, length = new_property( class = class_double, getter = function(self) self@end - self@start, setter = function(self, value) { self@end <- self@start + value self } ) ) ) x <- range(start = 1, end = 10) x #> #> @ start : num 1 #> @ end : num(0) #> @ length: num(0) x@length <- 5 x #> #> @ start : num 1 #> @ end : num 6 #> @ length: num 5"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"common-patterns","dir":"Articles","previous_headings":"Properties","what":"Common Patterns","title":"Classes and objects","text":"getter, setter, default, validator can used implement many common patterns properties.","code":""},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"deprecated-properties","dir":"Articles","previous_headings":"Properties > Common Patterns","what":"Deprecated properties","title":"Classes and objects","text":"setter + getter can used deprecate property:","code":"Person <- new_class(\"Person\", properties = list( first_name = class_character, firstName = new_property( class_character, default = quote(first_name), getter = function(self) { warning(\"@firstName is deprecated; please use @first_name instead\", call. = FALSE) self@first_name }, setter = function(self, value) { if (identical(value, self@first_name)) { return(self) } warning(\"@firstName is deprecated; please use @first_name instead\", call. = FALSE) self@first_name <- value self } ) )) args(Person) #> function (first_name = character(0), firstName = first_name) #> NULL hadley <- Person(firstName = \"Hadley\") #> Warning: @firstName is deprecated; please use @first_name instead hadley <- Person(first_name = \"Hadley\") # no warning hadley@firstName #> Warning: @firstName is deprecated; please use @first_name instead #> [1] \"Hadley\" hadley@firstName <- \"John\" #> Warning: @firstName is deprecated; please use @first_name instead hadley@first_name # no warning #> [1] \"John\""},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"required-properties","dir":"Articles","previous_headings":"Properties > Common Patterns","what":"Required properties","title":"Classes and objects","text":"can make property required constructor either : relying validator error default value, setting property default quoted error call.","code":"Person <- new_class(\"Person\", properties = list( name = new_property(class_character, validator = function(value) { if (length(value) != 1 || is.na(value) || value == \"\") \"must be a non-empty string\" })) ) try(Person()) #> Error : object properties are invalid: #> - @name must be a non-empty string try(Person(1)) # class_character$validator() is also checked. #> Error : object properties are invalid: #> - @name must be , not Person(\"Alice\") #> #> @ name: chr \"Alice\" Person <- new_class(\"Person\", properties = list( name = new_property(class_character, default = quote(stop(\"@name is required\"))) )) try(Person()) #> Error in Person() : @name is required Person(\"Alice\") #> #> @ name: chr \"Alice\""},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"frozen-properties","dir":"Articles","previous_headings":"Properties > Common Patterns","what":"Frozen properties","title":"Classes and objects","text":"can mark property read-construction providing custom setter.","code":"Person <- new_class(\"Person\", properties = list( birth_date = new_property( class_Date, setter = function(self, value) { if(!is.null(self@birth_date)) { stop(\"@birth_date is read-only\", call. = FALSE) } self@birth_date <- as.Date(value) self } ))) person <- Person(\"1999-12-31\") try(person@birth_date <- \"2000-01-01\") #> Error : @birth_date is read-only"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"constructors","dir":"Articles","previous_headings":"","what":"Constructors","title":"Classes and objects","text":"can see source code class’s constructor accessing constructor property: cases, S7’s default constructor need. However, cases might want something custom. example, range class, maybe ’d like construct vector numeric values, automatically computing min max. implement : constructor must always end call new_object(). first argument new_object() object parent class (haven’t specified parent argument new_class(), use S7_object() parent ). argument followed one named argument property. ’s one drawback custom constructors aware : subclass also require custom constructor.","code":"range@constructor #> function (start = numeric(0), end = numeric(0), length = numeric(0)) #> { #> start #> end #> length #> S7::new_object(S7::S7_object(), start = start, end = end, #> length = length) #> } range <- new_class(\"range\", properties = list( start = class_numeric, end = class_numeric ), constructor = function(x) { new_object(S7_object(), start = min(x, na.rm = TRUE), end = max(x, na.rm = TRUE)) } ) range(c(10, 5, 0, 2, 5, 7)) #> #> @ start: num 0 #> @ end : num 10"},{"path":"https://rconsortium.github.io/S7/articles/compatibility.html","id":"s3","dir":"Articles","previous_headings":"","what":"S3","title":"Compatibility with S3 and S4","text":"S7 objects S3 objects, S7 implemented top S3. two main differences S7 object S3 object: well class attribute possessed S3 objects, S7 objects additional S7_class attribute contains object defines class. S7 objects properties; S3 objects attributes. Properties implemented top attributes, can access directly attr friends. working inside S7, never use attributes directly, mean existing code continue work. , means usage S7 S3 just work. S7 can register methods : S7 class S3 generic S3 class S7 generic S7 classes can extend S3 classes S3 classes can extend S7 classes","code":""},{"path":"https://rconsortium.github.io/S7/articles/compatibility.html","id":"methods","dir":"Articles","previous_headings":"S3","what":"Methods","title":"Compatibility with S3 and S4","text":"method() designed single tool method registration need working S7 classes. can also register method S7 class S3 generic without using S7, S7 objects S3 classes, S3 dispatch operate normally.","code":"foo <- new_class(\"foo\") class(foo()) #> [1] \"foo\" \"S7_object\" mean.foo <- function(x, ...) { \"mean of foo\" } mean(foo()) #> [1] \"mean of foo\""},{"path":"https://rconsortium.github.io/S7/articles/compatibility.html","id":"classes","dir":"Articles","previous_headings":"S3","what":"Classes","title":"Compatibility with S3 and S4","text":"’s possible extend S7 class S3. primarily useful many cases allows change class hierarchy inside : can provide formal definition S3 class using S7, subclasses don’t need change.","code":""},{"path":"https://rconsortium.github.io/S7/articles/compatibility.html","id":"list-classes","dir":"Articles","previous_headings":"S3","what":"List classes","title":"Compatibility with S3 and S4","text":"Many simple S3 classes implemented lists, e.g. rle. two ways convert S7. keep structure exactly , using list underlying data structure using constructor enforce structure: Alternatively convert natural representation using S7: allow existing methods work ’ll need override $ access properties instead list elements: chief disadvantage approach subclasses need converted S7 well.","code":"rle <- function(x) { if (!is.vector(x) && !is.list(x)) { stop(\"'x' must be a vector of an atomic type\") } n <- length(x) if (n == 0L) { new_rle(integer(), x) } else { y <- x[-1L] != x[-n] i <- c(which(y | is.na(y)), n) new_rle(diff(c(0L, i)), x[i]) } } new_rle <- function(lengths, values) { structure( list( lengths = lengths, values = values ), class = \"rle\" ) } new_rle <- new_class(\"rle\", parent = class_list, constructor = function(lengths, values) { new_object(list(lengths = lengths, values = values)) } ) rle(1:10) #> Run Length Encoding #> lengths: int [1:10] 1 1 1 1 1 1 1 1 1 1 #> values : int [1:10] 1 2 3 4 5 6 7 8 9 10 new_rle <- new_class(\"rle\", properties = list( lengths = class_integer, values = class_atomic )) method(`$`, new_rle) <- prop rle(1:10) #> Run Length Encoding #> lengths: int [1:10] 1 1 1 1 1 1 1 1 1 1 #> values : int [1:10] 1 2 3 4 5 6 7 8 9 10"},{"path":"https://rconsortium.github.io/S7/articles/compatibility.html","id":"s4","dir":"Articles","previous_headings":"","what":"S4","title":"Compatibility with S3 and S4","text":"S7 properties equivalent S4 slots. chief difference can dynamic. S7 classes can extend S4 classes S4 classes can extend S3 classes S7 class S4 generic S4 class S7 generic","code":""},{"path":"https://rconsortium.github.io/S7/articles/compatibility.html","id":"unions","dir":"Articles","previous_headings":"S4","what":"Unions","title":"Compatibility with S3 and S4","text":"S4 unions automatically converted S7 unions. ’s important difference way class unions handled S4 S7. S4, ’re handled method dispatch time, create setUnion(\"u1\", c(\"class1\", \"class2\")), class1 class2 now extend u1. S7, unions handled method registration time registering method union just short-hand registering method classes. S7 unions allow restrict type property way S4 unions allow restrict type slot.","code":"class1 <- new_class(\"class1\") class2 <- new_class(\"class2\") union1 <- new_union(class1, class2) foo <- new_generic(\"foo\", \"x\") method(foo, union1) <- function(x) \"\" foo #> foo(x, ...) with 2 methods: #> 1: method(foo, class1) #> 2: method(foo, class2)"},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"generic-method-compatibility","dir":"Articles","previous_headings":"","what":"Generic-method compatibility","title":"Generics and methods","text":"register method, S7 checks method compatible generic. formal arguments generic methods must agree. means : arguments generic , method must . particular, arguments method start arguments generic dispatches , arguments must default arguments. method can contain arguments generic , long generic includes … argument list.","code":""},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"generic-with-dots-method-without-dots","dir":"Articles","previous_headings":"Generic-method compatibility","what":"Generic with dots; method without dots","title":"Generics and methods","text":"default generic includes … generally methods . ensures misspelled arguments won’t silently swallowed method. important difference S3. Take simple implementation mean(): pass additional argument , ’ll get error: can still add additional arguments desired: (’ll come back case requiring methods implement na.rm = TRUE argument shortly.)","code":"mean <- new_generic(\"mean\", \"x\") method(mean, class_numeric) <- function(x) sum(x) / length(x) mean(100, na.rm = TRUE) method(mean, class_numeric) <- function(x, na.rm = TRUE) { if (na.rm) { x <- x[!is.na(x)] } sum(x) / length(x) } #> Overwriting method mean() #> Overwriting method mean() mean(c(100, NA), na.rm = TRUE) #> [1] 100"},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"generic-and-method-with-dots","dir":"Articles","previous_headings":"Generic-method compatibility","what":"Generic and method with dots","title":"Generics and methods","text":"cases need take … method, particularly problematic need re-call generic recursively. example, imagine simple print method like : want print list? ’s fine long elements list numbers, soon add character vector, get error: solve situation, methods generally need ignore arguments haven’t specifically designed handle, .e. need use …: case really want silently ignore unknown arguments might apply methods. ’s unfortunately easy way avoid problem without relying fairly esoteric technology (done rlang::check_dots_used()).","code":"simple_print <- new_generic(\"simple_print\", \"x\") method(simple_print, class_double) <- function(x, digits = 3) {} method(simple_print, class_character) <- function(x, max_length = 100) {} method(simple_print, class_list) <- function(x, ...) { for (el in x) { simple_print(el, ...) } } simple_print(list(1, 2, 3), digits = 3) simple_print(list(1, 2, \"x\"), digits = 3) method(simple_print, class_double) <- function(x, ..., digits = 3) {} #> Overwriting method simple_print() method(simple_print, class_character) <- function(x, ..., max_length = 100) {} #> Overwriting method simple_print() simple_print(list(1, 2, \"x\"), digits = 3) simple_print(list(1, 2, \"x\"), diggits = 3)"},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"generic-and-method-without-dots","dir":"Articles","previous_headings":"Generic-method compatibility","what":"Generic and method without dots","title":"Generics and methods","text":"Occasional ’s useful create generic without … functions useful property: call succeeds one type input, succeed type input. create generic, ’ll need use third argument new_generic(): optional function powers generic. function one key property: must call call_method() actually perform dispatch. general, property needed low-level functions precisely defined semantics. good example function length(): Omitting … generic signature strong restriction prevents methods adding extra arguments. reason, ’s used special situations.","code":"length <- new_generic(\"length\", \"x\", function(x) { S7_dispatch() })"},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"customizing-generics","dir":"Articles","previous_headings":"","what":"Customizing generics","title":"Generics and methods","text":"cases, ’ll supply first two arguments new_generic() allow automatically generate body generic: important part body S7_dispatch(); function finds method matches arguments used dispatch calls arguments supplied generic. can useful customize body. previous section showed one case might want supply body : dropping … formals generic. three useful cases: add required arguments. add optional arguments. Perform standard work. custom fun must always include call call_method(), usually last call.","code":"display <- new_generic(\"display\", \"x\") S7_data(display) #> function (x, ...) #> S7::S7_dispatch()"},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"add-required-arguments","dir":"Articles","previous_headings":"Customizing generics","what":"Add required arguments","title":"Generics and methods","text":"add required arguments aren’t dispatched upon, just need add additional arguments lack default values: Now methods need provide y argument. , ’ll get warning: warning, error, generic might defined different package process changing interfaces. ’ll always want address warning see .","code":"foo <- new_generic(\"foo\", \"x\", function(x, y, ...) { S7_dispatch() }) method(foo, class_integer) <- function(x, ...) { 10 } #> Warning: foo() doesn't have argument `y`"},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"add-optional-arguments","dir":"Articles","previous_headings":"Customizing generics","what":"Add optional arguments","title":"Generics and methods","text":"Adding optional argument similar, generally come …. ensures user must supply full name argument calling function, makes easier extend function future. Forgetting argument using different default value generate warning.","code":"mean <- new_generic(\"mean\", \"x\", function(x, ..., na.rm = TRUE) { S7_dispatch() }) method(mean, class_integer) <- function(x, na.rm = TRUE) { if (na.rm) { x <- x[!is.na(x)] } sum(x) / length(x) } method(mean, class_double) <- function(x, na.rm = FALSE) {} #> Warning: In mean(), default value of `na.rm` is not the same as the generic #> - Generic: TRUE #> - Method: FALSE method(mean, class_logical) <- function(x) {} #> Warning: mean() doesn't have argument `na.rm`"},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"do-some-work","dir":"Articles","previous_headings":"Customizing generics","what":"Do some work","title":"Generics and methods","text":"generic additional arguments, might want additional work verify ’re expected type. example, mean() function verify na.rm correctly specified: downside performing error checking constraint interface methods; reason method found useful allow na.rm number string, provide alternative argument.","code":"mean <- new_generic(\"mean\", \"x\", function(x, ..., na.rm = TRUE) { if (!identical(na.rm, TRUE) && !identical(na.rm = FALSE)) { stop(\"`na.rm` must be either TRUE or FALSE\") } S7_dispatch() })"},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"super","dir":"Articles","previous_headings":"","what":"super()","title":"Generics and methods","text":"Sometimes ’s useful define method terms superclass. good example computing mean date — since dates represent number days since 1970-01-01, computing mean just matter computing mean underlying numeric vector converting back date. demonstrate idea, ’ll first define mean generic method numbers: Date class: Now compute mean write: Let’s unpack method inside : First call super(x, = class_double) — make call next generic treat x like ’s double, rather date. call mean() super() call mean() method defined . Finally, take number returned mean convert back date. ’re familiar S3 S4 might recognize super() fills similar role NextMethod() callNextMethod(). However, ’s much explicit: need supply name parent class, generic use, arguments generic. explicitness makes code easier understand eventually enable certain performance optimizations otherwise difficult.","code":"mean <- new_generic(\"mean\", \"x\") method(mean, class_numeric) <- function(x) { sum(x) / length(x) } mean(1:10) #> [1] 5.5 date <- new_class(\"date\", parent = class_double) # Cheat by using the existing base .Date class method(print, date) <- function(x) print(.Date(x)) date(c(1, 10, 100)) #> [1] \"1970-01-02\" \"1970-01-11\" \"1970-04-11\" method(mean, date) <- function(x) { date(mean(super(x, to = class_double))) } mean(date(c(1, 10, 100))) #> [1] \"1970-02-07\""},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"multiple-dispatch","dir":"Articles","previous_headings":"","what":"Multiple dispatch","title":"Generics and methods","text":"far focused primarily single dispatch, .e. generics dispatch_on single string. also possible supply length 2 (!) vector dispatch_on create generic performs multiple dispatch, .e. uses classes one object find appropriate method. Multiple dispatch feature primarily S4, although S3 includes limited special cases arithmetic operators. Multiple dispatch heavily used S4; don’t expect heavily used S7, occasionally useful.","code":""},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"a-simple-example","dir":"Articles","previous_headings":"Multiple dispatch","what":"A simple example","title":"Generics and methods","text":"Let’s take speak example vignette(\"S7\") extend teach pets speak multiple languages:","code":"pet <- new_class(\"pet\") dog <- new_class(\"dog\", pet) cat <- new_class(\"cat\", pet) language <- new_class(\"language\") english <- new_class(\"english\", language) french <- new_class(\"french\", language) speak <- new_generic(\"speak\", c(\"x\", \"y\")) method(speak, list(dog, english)) <- function(x, y) \"Woof\" method(speak, list(cat, english)) <- function(x, y) \"Meow\" method(speak, list(dog, french)) <- function(x, y) \"Ouaf Ouaf\" method(speak, list(cat, french)) <- function(x, y) \"Miaou\" speak(cat(), english()) #> [1] \"Meow\" speak(dog(), french()) #> [1] \"Ouaf Ouaf\" # This example was originally inspired by blog.klipse.tech/javascript/2021/10/03/multimethod.html # which has unfortunately since disappeaed."},{"path":"https://rconsortium.github.io/S7/articles/generics-methods.html","id":"special-classes","dir":"Articles","previous_headings":"Multiple dispatch","what":"Special “classes”","title":"Generics and methods","text":"two special classes become particularly useful multiple dispatch: class_any() match class class_missing() match missing argument (.e. NA, argument supplied)","code":""},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/minutes/2021-05-18.html","id":"process-ml","dir":"Articles > Minutes","previous_headings":"Discussion","what":"Process (ML)","title":"Minutes 2021-05-18","text":"Active discussion GitHub issues: https://github.com/RConsortium/S7 Need little process finalise discussion. Proposal: original author reads discussion, summarises, creates pull request closes issue. participants discussion added reviewers. issues resolved, can start move forward next steps syntax, implementation, …","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2021-05-18.html","id":"do-you-have-a-sense-for-what-the-system-might-look-like-hb","dir":"Articles > Minutes","previous_headings":"Discussion","what":"Do you have a sense for what the system might look like? (HB)","title":"Minutes 2021-05-18","text":"Unfortunately don’t lot time, reading happy review. much implementation head? ML: Quite bit; wanted make sure step back make sure requirements. yes, vision head. HW: clear idea head; mostly building top S3","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2021-05-18.html","id":"would-it-look-more-like-s3-s4-or-something-completely-new-hb","dir":"Articles > Minutes","previous_headings":"Discussion","what":"Would it look more like S3, S4, or something completely new? (HB)","title":"Minutes 2021-05-18","text":"ML: one main restrictions build existing system HW: one advantages NAMESPACE ML: base uses LT: want able define new methods “[”, means maximally compatible base.","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2021-05-18.html","id":"double-dispatch-hwlt","dir":"Articles > Minutes","previous_headings":"Discussion","what":"Double dispatch (HW/LT)","title":"Minutes 2021-05-18","text":"LT: double dispatch S3. S4 overly ambitious v hard maintain. CLOS written v. smart people still got things wrong. HW: may need carve double dispatch v2. HO: multiple inheritance falling favour (diamond inheritance problem etc). use interface based approach instead?","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2021-05-18.html","id":"generic-interfaces-lt","dir":"Articles > Minutes","previous_headings":"Discussion","what":"Generic “interfaces” (LT)","title":"Minutes 2021-05-18","text":"LT: need consider just generics , related. Related contracts Eiffel, concepts C++20, ...","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2021-05-18.html","id":"message-passing-oop-ho","dir":"Articles > Minutes","previous_headings":"Discussion","what":"Message passing OOP (HO)","title":"Minutes 2021-05-18","text":"HO: considering ? (.e. R6 ref classes) ML: currently scope","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2021-05-18.html","id":"what-are-the-implications-for-compiling-hb","dir":"Articles > Minutes","previous_headings":"Discussion","what":"What are the implications for compiling? (HB)","title":"Minutes 2021-05-18","text":"LT: compiling currently entirely within functions; one day might nice compile across functions/methods etc. Always thinking compilation might work. generally features hard compile hard users understand.","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2021-05-18.html","id":"action-items","dir":"Articles > Minutes","previous_headings":"","what":"Action items","title":"Minutes 2021-05-18","text":"Continue discussion issues Start turning issues PRs Feel free fix minor issues","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2022-02-12.html","id":"process-check-in","dir":"Articles > Minutes","previous_headings":"","what":"Process check-in","title":"Minutes 2022-02-12","text":"Minor stuff ’m just . Bigger changes get Davis review. semantic changes without discussion issues","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2022-02-12.html","id":"changes","dir":"Articles > Minutes","previous_headings":"","what":"Changes","title":"Minutes 2022-02-12","text":"as_class() centralises handling classes (base types, S3, S4, S7) Strings now refer base types (previously used S3 + S7 class parent frame) new_class( parent = \"function\") provide wrappers base unions consider get rid string representation altogether? (e.g. base_atomic) https://github.com/RConsortium/S7/pull/148 makes possible S7 class extend S3 class export S3 class definitions common base S3 classes? provide base_factor etc. S4 unions converted S7 unions Clarify difference registration dispatch. flow refactoring C code near future. Dispatch base/S3/S4/S7 class S7 generic. Registration base/S3/S4/S7 class S7 generic S7 class S3/S4 generic. combinations now tested. Thorough refactoring method registration generic, signature -> dispatch_args. Simplified evaluation restricted dispatch args (probably much, relax needed) provide default dispatch argument name? x Validate types, call validator() Also call validator() super classes New props<- multi-assignment. validates end. General QoL improvements print() str() methods","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2022-02-12.html","id":"next-up","dir":"Articles > Minutes","previous_headings":"","what":"Next up","title":"Minutes 2022-02-12","text":"Refactor method dispatch Defaults properties, get point constructor() without argument usually works Dispatch Missing/Coercion (cast()) Eliminate next_method() (up_cast()) Documentation","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2022-02-12.html","id":"discussion","dir":"Articles > Minutes","previous_headings":"","what":"Discussion","title":"Minutes 2022-02-12","text":"Still need real world application Hadley submit proposals useR + rstudio::conf; determine speakers topics closer time Next time meet, discuss ways advertise Ok rename default branch master main? ~5 minutes work unlikely cause problems. Ok eliminate prop_safely()? https://github.com/RConsortium/S7/issues/156 show S3/S4 _arg looks good Coercion: https://github.com/RConsortium/S7/issues/136","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2022-03-08.html","id":"changes","dir":"Articles > Minutes","previous_headings":"","what":"Changes","title":"Minutes 2022-03-08","text":"Website updates (https://rconsortium.github.io/OOP-WG/): refreshed readme, minutes articles. Working documentation overhaul https://github.com/RConsortium/S7/pull/187. new_generic() now requires dispatch_args. means new_generic() typically called without names: new_generic(\"foo\", \"x\") new_generic(\"foo\", \"x\", function(x, y) call_method()) New class_any (already define methods S7_object) class_missing sentinels. creating object, unspecified properties initialized default value (#67). achieve , constructor arguments default class_missing, since NULL prevent default value NULL, missing() requires many gymnastics. new_class(\"foo\", properties = list(x = integer, y = double))@constructor https://github.com/RConsortium/S7/blob/main/R/class.R#L209-L212 See minor changes inhttps://github.com/RConsortium/S7/blob/main/NEWS.md#feb-2022","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2022-03-08.html","id":"discussion","dir":"Articles > Minutes","previous_headings":"","what":"Discussion","title":"Minutes 2022-03-08","text":"Rename method_call()? https://github.com/RConsortium/S7/issues/200 — S7_dispatch() sounds good. Abstract classes? https://github.com/RConsortium/S7/issues/199. Easy implement worth ? call S7 equivalent inherits? https://github.com/RConsortium/S7/issues/193 — call S7_inherits() now; eventually just part inherits. Propose generic extension mechanism inherits. Explicit S4 registration: https://github.com/RConsortium/S7/pull/182 New names base types? https://github.com/RConsortium/S7/issues/170 — class_ sounds good. Convert super: https://github.com/RConsortium/S7/pull/181 offer encapsulated OO? https://github.com/RConsortium/S7/issues/202","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2022-08-01.html","id":"updates-from-hadley","dir":"Articles > Minutes","previous_headings":"","what":"Updates from Hadley","title":"Minutes 2022-08-08","text":"Talks useR rstudio::conf() appeared go well resulted useful issues PRs (esp. https://github.com/jamieRowen https://github.com/dgkf). Interest Merck via email","code":""},{"path":"https://rconsortium.github.io/S7/articles/minutes/2022-08-01.html","id":"next-steps","dir":"Articles > Minutes","previous_headings":"","what":"Next steps","title":"Minutes 2022-08-08","text":"HW continue work smaller issues. R-core meeting Vienna (Sept). ML present S7 work hopefully get buy-. Base patches? (https://github.com/RConsortium/S7/issues/222) Get CRAN get exposure figure get base R .","code":""},{"path":"https://rconsortium.github.io/S7/articles/motivation.html","id":"challenges-with-s3","dir":"Articles","previous_headings":"","what":"Challenges with S3","title":"Motivation for S7","text":"S3 informal, meaning ’s formal definition class. makes impossible know exactly properties object possess, even parent class . S7 resolves problem formal definition encoded class object produced new_class(). includes support validation (avoiding validation needed) inspired S4. new user encounter S3 generic, often confused implementation function appears missing. S7 thoughtfully designed print method makes clear methods available find source code. Properties S3 class usually stored attributes, , default, attr() partial matching, can lead bugs hard diagnose. Additionally, attr() returns NULL attribute doesn’t exist, misspelling attribute can lead subtle bugs. @ fixes problems. S3 method dispatch complicated compatibility S. complexity affects relatively little code, attempt dive details makes UseMethod() hard understand. much possible, S7 avoids “funny” business environments promises, distinction argument values local values. S3 primarily designed single dispatch double dispatch provided handful base generics. ’s possible reuse implementation user generics. S7 provides standard way multiple dispatch (including double dispatch) can used generic. NextMethod() unpredictable since can’t tell exactly method called reading code; instead need know complete class hierarchy methods currently registered (loading package might change methods). S7 takes difference approach super(), requiring explicit specification superclass used. Conversion S3 classes implemented via loose convention: implement class foo, also provide generic .foo() convert objects type. S7 avoids problem providing double-dispatch convert() generic need provide appropriate methods.","code":""},{"path":"https://rconsortium.github.io/S7/articles/motivation.html","id":"challenges-with-s4","dir":"Articles","previous_headings":"","what":"Challenges with S4","title":"Motivation for S7","text":"Multiple inheritance seemed like powerful idea time, practice appears generate problems solves. S7 support multiple inheritance. S4’s method dispatch uses principled complex distance metric pick best method presence ambiguity. Time shown approach hard people understand makes hard predict happen new methods registered. S7 implements much simpler, greedy, approach trades additional work behalf class author system simpler easier understand. S4 clean break S3. made possible make radical changes made harder switch S3 S4, leading general lack adoption R community. S7 designed drop-compatible S3, making possible convert existing packages use S7 instead S3 hour two work. least within Bioconductor, slots generally thought implementation detail directly accessed end-user. leads two problems. Firstly, implementing S4 Bioconductor class often also requires plethora accessor functions thin wrapper around @ @<-. Secondly, users know @ use access object internals even though ’re supposed . S7 avoids problems accepting fact R data language, ’s way stop users pulling data need object. make possible change internal implementation details object preserving existing @ usage, S7 provides dynamic properties.","code":""},{"path":"https://rconsortium.github.io/S7/articles/packages.html","id":"method-registration","dir":"Articles","previous_headings":"","what":"Method registration","title":"Using S7 in a package","text":"always call methods_register() .onLoad(): S7’s way registering methods, rather using export directives NAMESPACE like S3 S4 . strictly necessary registering methods generics packages, ’s harm adding ensures won’t forget later. (’re importing S7 namespace quiet R CMD check NOTE.)","code":".onLoad <- function(...) { S7::methods_register() }"},{"path":"https://rconsortium.github.io/S7/articles/packages.html","id":"documentation-and-exports","dir":"Articles","previous_headings":"","what":"Documentation and exports","title":"Using S7 in a package","text":"want users create instances class, need export class constructor. means also need document , since constructor function, means document arguments properties class (unless customised constructor). export class, must also set package argument, ensuring classes name disambiguated across packages. document generics like regular functions (since !). expect others create methods generic, may want include section describing properties expect methods . plan provide easy way methods generic, yet implemented . can track progress https://github.com/RConsortium/S7/issues/167. don’t currently recommendations documenting methods. ’s need document order pass R CMD check, obviously cases ’s nice provide additional details method, particularly takes extra arguments compared generic. ’re tracking issue https://github.com/RConsortium/S7/issues/315.","code":""},{"path":"https://rconsortium.github.io/S7/articles/packages.html","id":"backward-compatibility","dir":"Articles","previous_headings":"","what":"Backward compatibility","title":"Using S7 in a package","text":"using S7 package want package work versions R 4.3.0, need know versions R @ works S4 objects. two workarounds. easiest least convenient workaround just prop() instead @. Otherwise, can conditionally make S7-aware @ available package custom NAMESPACE directive: @ work users package S7 automatically attaches environment containing needed definition ’s loaded.","code":"# enable usage of @name in package code #' @rawNamespace if (getRversion() < \"4.3.0\") importFrom(\"S7\", \"@\") NULL"},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"objects","dir":"Articles > Spec","previous_headings":"","what":"Objects","title":"Design specification","text":"define S7 object R object : class object attribute, reference class object, retrieved classObject(). S3 compatibility, class attribute, character vector class names. Additional attributes storing properties defined class, accessible @/prop().","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"classes","dir":"Articles > Spec","previous_headings":"","what":"Classes","title":"Design specification","text":"S7 classes first class objects (Req2) following components: Name, human-meaningful descriptor class. used default print method error messages; identify class. Parent, class object parent class. implies single inheritance (Req6). constructor, user-facing function used create new objects class. always ends call newObject() initialize class. function (wrapped appropriately) represents class. validator, function takes object returns NULL object valid, otherwise character vector error messages (like methods package). Properties, list property objects (see ) define data objects can possess. Classes constructed supplying components call newClass(). newClass() returns class object can called construct object class. example:","code":"newClass( name, parent = Object, constructor = function(...) newObject(...), validator = function(x) NULL, properties = list() ) Range <- newClass(\"Range\", Vector, constructor = function(start, end) { stopifnot(is.numeric(start), is.numeric(end), end >= start) newObject(start = start, end = end) }, validator = function(x) { if (x@end < x@start) { \"end must be greater than or equal to start\" } }, properties = c(start = \"numeric\", end = \"numeric\") ) Range(start = 1, end = 10)"},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"initialization","dir":"Articles > Spec","previous_headings":"Classes","what":"Initialization","title":"Design specification","text":"constructor uses newObject() initialize new object. : Inspects enclosing scope find “current” class. Creates prototype, either calling parent constructor creating base type adding class classObject attributes . Validates properties adds prototype. Validates complete object. Steps 2 3 similar calling structure(), except property values initialized validated property system.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"shortcuts","dir":"Articles > Spec","previous_headings":"Classes","what":"Shortcuts","title":"Design specification","text":"convention, argument takes class object can instead take name class object string. name used find class object calling frame. Similarly, instead providing list property objects, can instead provide named character vector. example, c(name = \"character\", age = \"integer\") shorthand list(newProperty(\"name\", \"character\"), newProperty(\"age\", \"integer\")).","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"validation","dir":"Articles > Spec","previous_headings":"Classes","what":"Validation","title":"Design specification","text":"Objects validated initialization every time property modified. temporarily opt-validation (e.g. order transition temporarily invalid state), S7 provides eventuallyValid(): example, wanted move Range object right, write: ensures validation trigger x@start + y greater x@end. S7 also provides implicitlyValid() expert use. similar eventuallyValid() check validity end. can used performance critical code can ascertain sequence operations can never make valid object invalid. (can quite hard: example, move_right() example , might think x@start < x@end true beginning, x@start + y < x@end + y still true end, don’t need re-validate object. ’s necessarily true: x@start == 1, x@end == 2, abs(y) > 2e16 x@start + y == x@end + y!)","code":"eventuallyValid <- function(object, fun) { object$internal_validation_flag <- FALSE out <- fun(object) out$internal_validation_flag <- TRUE validate(out) } move_right <- function(x, y) { eventuallyValid(x, function(x) { x@start <- x@start + y x@end <- x@end + y x }) }"},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"unions","dir":"Articles > Spec","previous_headings":"Classes","what":"Unions","title":"Design specification","text":"class union represents list possible classes. used properties allow property one set classes, method dispatch convenience defining method multiple classes.","code":"ClassUnion <- defineClass(\"ClassUnion\", properties = list(classes = \"list\"), validator = function(x) { # Checks that all elements of classes are Class object }, constructor = function(...) { classes <- list(...) # look up Class object from any class vectors newObject(classes = classes) } ) ClassUnion(\"character\", \"NULL\") ClassUnion(Range, NULL)"},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"properties","dir":"Articles > Spec","previous_headings":"","what":"Properties","title":"Design specification","text":"Properties store data needed object. Properties object can accessed using @/@<- prop()/prop<-. Setting properties object always triggers validation. Properties less encapsulated equivalents languages R users expect transparency want get actual data inside object directly manipulate get work done. Properties support typical usage still providing ability encapsulate data hide implementation details. Every property definition : name, used label output humans. optional class (class union). optional accessor function overrides getting setting, much like active binding (default, value stored attribute, like S3/S4). Property objects created newProperty(): Compared S3 attributes, properties considerably stricter: class defines names types properties. Compared S4 slots, properties enable object change internals without breaking existing usage can provides custom accessor redirects new representation. built-support emitting deprecation messages. tempting support public vs. private scoping properties, obvious , code privileged another. clear whether package define boundary, packages sometimes define methods classes defined packages want take advantage internal representations. believe encapsulation afforded properties good compromise.","code":"newProperty( name, class = NULL, accessor = NULL )"},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"generics","dir":"Articles > Spec","previous_headings":"","what":"Generics","title":"Design specification","text":"generic separates function interface implementation: generic defines interface, methods provide implementation. introspection purposes, knows name names arguments signature (arguments considered dispatch). Calling newGeneric() defines new generic. signature: signature default first argument, .e. formals(FUN)[1]. body FUN resemble S3 S4 generics. might just call UseMethod(). convention, argument takes generic function, can instead take name generic function supplied string. name used find class object calling frame.","code":"newGeneric(name, FUN, signature)"},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"creation","dir":"Articles > Spec","previous_headings":"Methods","what":"Creation","title":"Design specification","text":"Methods defined calling method<-(generic, signature, method): generic generic function. signature single class object, class union, list class objects/unions, character vector. method compatible function, .e. arguments ... names order; generic doesn’t ... arguments must . Documentation discuss risks defining method don’t either generic class. method<- designed work run-time (just package build-time) methods can defined packages define classes generics loaded package defines methods. typically occurs providing methods generics classes suggested packages.","code":"method(generic, signature) <- function(x, ...) {} whenLoaded(\"pkg\", { method(mean, pkg::A) <- function() 10 method(sum, pkg::A) <- function() 5 })"},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"dispatch","dir":"Articles > Spec","previous_headings":"Methods","what":"Dispatch","title":"Design specification","text":"Dispatch nested, meaning multiple arguments generic signature, dispatch first argument, second. Nested dispatch likely easier predict understand compared treating arguments equal precedence. Nested dispatch also easier implement efficiently, classes effectively inherit methods, implement inheritance using environments. example, plot() generic dispatching x implemented like : publish() publishes object x destination y, dispatching arguments, implemented : method dispatch nested, presumably equivalent something like: Alternatively, generics just call UseMethod(), gain support nested dispatch.","code":"plot <- function(x) { method(plot, classObject(x))(x) } publish <- function(x, y, ...) { sig <- list(classObject(x), classObject(y)) method(publish, sig)(x, y, ...) } publish <- function(x, y, ...) { publish_x <- method(publish, classObject(x)) publish_xy <- method(publish_x, classObject(y)) publish_xy(x, y, ...) }"},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"s3","dir":"Articles > Spec","previous_headings":"Compatibility","what":"S3","title":"Design specification","text":"Since class attribute semantics S3, S3 dispatch fully compatible. new generics also able handle legacy S3 objects.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"s4","dir":"Articles > Spec","previous_headings":"Compatibility","what":"S4","title":"Design specification","text":"principle, modify methods package S4 generics can operate new class definitions. Since new generics fallback S3 dispatch, support S4 objects just S3 generics support now.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/design.html","id":"documentation","dir":"Articles > Spec","previous_headings":"","what":"Documentation","title":"Design specification","text":"primary challenge availability methods classes depends packages installed, need generate index pages list methods generic methods particular class signature. documentation blurbs lazily scraped runtime build index, similar help search index created now. generate help pages index. index installed methods provide additional benefits, including introspection. generic prompt user load package fails find applicable method, installed, unloaded method available. case arise method defined outside packages define class generic.","code":""},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s3","dir":"Articles > Spec","previous_headings":"Single dispatch","what":"S3","title":"Dispatch","text":"basic rules S3 dispatch simple. object class attribute c(\"\", \"b\", \"c\") generic f() looks methods following order: f.() f.b() f.c() f.default() method found, errors.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s7","dir":"Articles > Spec","previous_headings":"Single dispatch","what":"S7","title":"Dispatch","text":"S7 behave S3.","code":""},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s3-1","dir":"Articles > Spec","previous_headings":"Method lookup","what":"S3","title":"Dispatch","text":"precisely UseMethod() look methods? R 4.0.0, looks following three places: method table special environment .__S3MethodsTable__. found environment generic defined. chain environments starting parent.frame() call generic, ending global environment. base environment (..e. skips search).","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s7-1","dir":"Articles > Spec","previous_headings":"Method lookup","what":"S7","title":"Dispatch","text":"S7 methods defined using assignment: Behind scenes, acts directly upon method table, method lookup S7 generics never needs look parent frame. method<- likely start shim around .S3method() may want consider separate .__S7MethodsTable__.. use new data structure resolves generic/class ambiguity (e.g. .equal.data.frame()). Methods S7 classes defined S3 generics still use S3 method table. consider attaching method table generic, instead containing environment. Method lookup cached performance, performed per class. Cached methods marked special attribute flushed whenever new method generic added.","code":"method(\"mean\", \"numeric\") <- function(x) sum(x) / length(x)"},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s3-2","dir":"Articles > Spec","previous_headings":"Method call frame","what":"S3","title":"Dispatch","text":"method found, must called. UseMethod() work like regular function call instead: Changes arguments ignored. Method can access objects created generic. (Changed R 4.4.0.) parent frame method call parent frame generic. properties summarised following example:","code":"foo <- function(x, y) { y <- 2 z <- 2 UseMethod(\"foo\") } foo.numeric <- function(x, y) { print(parent.frame()) c(x = x, y = y, z = z) } # In R 4.3 and earlier foo(1, 1) #> x y z #> 1 1 2 foo(1, 1) #> #> Error in foo.numeric(1, 1): object 'z' not found"},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s7-2","dir":"Articles > Spec","previous_headings":"Method call frame","what":"S7","title":"Dispatch","text":"Can eliminate special behaviour make just like regular function call? Presumably easier changing dispatch rules ’ll call function UseMethod(). Need make precise arguments passed method. plot() least assumes works: intersect assignment within generic?","code":"foo <- function(x, y) { UseMethod(\"foo\") } foo.numeric <- function(x, y) { deparse(substitute(x)) } x <- 10 foo(x) #> [1] \"x\""},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s3-3","dir":"Articles > Spec","previous_headings":"Inheritance","what":"S3","title":"Dispatch","text":".e. NextMethod() work: currently state recorded special variables like .Generic, etc. Can avoid confusion:","code":"foo <- function(x) { UseMethod(\"foo\") } foo.a <- function(x) { x <- factor(\"x\") NextMethod() } foo.b <- function(x) { print(\"In B\") print(class(x)) } foo(structure(1, class = c(\"a\", \"b\"))) #> [1] \"In B\" #> [1] \"factor\""},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s4","dir":"Articles > Spec","previous_headings":"Inheritance","what":"S4","title":"Dispatch","text":"Want avoid sort code, rely magic callGeneric() pass values current call.","code":"method(\"mean\", \"foofy\") <- function(x, ..., na.rm = TRUE) { x <- x@values callGeneric() }"},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s7-3","dir":"Articles > Spec","previous_headings":"Inheritance","what":"S7","title":"Dispatch","text":"Can require generic object arguments make code easier reason ?","code":"method(\"mean\", \"POSIXct\") <- function(x) { POSIXct(NextMethod(), tz = attr(x, \"tz\")) } # Explicit is nice: method(\"mean\", \"POSIXct\") <- function(x) { POSIXct(NextMethod(\"mean\", x), tz = attr(x, \"tz\")) } # But what does this do? Is this just an error? method(\"mean\", \"POSIXct\") <- function(x) { POSIXct(NextMethod(\"sd\", 10), tz = attr(x, \"tz\")) }"},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s3-4","dir":"Articles > Spec","previous_headings":"Group generics","what":"S3","title":"Dispatch","text":"Group generics (Math, Ops, Summary, Complex): exist internal generics. Looked final fallback.","code":"sloop::s3_dispatch(sum(Sys.time())) #> sum.POSIXct #> sum.POSIXt #> sum.default #> => Summary.POSIXct #> Summary.POSIXt #> Summary.default #> -> sum (internal)"},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s7-4","dir":"Articles > Spec","previous_headings":"Group generics","what":"S7","title":"Dispatch","text":"Keep .","code":""},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s3-5","dir":"Articles > Spec","previous_headings":"Double dispatch","what":"S3","title":"Dispatch","text":"Used Ops group generic. Basic process find method first second arguments. : , ok one internal, use Otherwise, warn use internal","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s7-5","dir":"Articles > Spec","previous_headings":"Double dispatch","what":"S7","title":"Dispatch","text":"Goal use iterated dispatch implies asymmetry dispatch order. User responsible ensuring x + y equivalent y + x (types almost always , values likely different). vctrs, question remove inheritance double dispatch. already done vec_ptype2() vec_cast() coercion hierarchy often match class hierarchy. May also vec_arith().","code":"double_dispatch <- function(x, y, generic = \"+\") { grid <- rev(expand.grid(sloop::s3_class(y), sloop::s3_class(x))) writeLines(paste0(\"* \", generic, \".\", grid[[1]], \".\", grid[[2]])) } ab <- structure(list(), class = c(\"a\", \"b\")) cd <- structure(list(), class = c(\"c\", \"d\")) double_dispatch(ab, cd) #> * +.a.c #> * +.a.d #> * +.b.c #> * +.b.d double_dispatch(cd, ab) #> * +.c.a #> * +.c.b #> * +.d.a #> * +.d.b double_dispatch(1, 1L) #> * +.double.integer #> * +.double.numeric #> * +.numeric.integer #> * +.numeric.numeric"},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s3-6","dir":"Articles > Spec","previous_headings":"Implicit class","what":"S3","title":"Dispatch","text":"UseMethod() receives object without class attribute, uses implicit class, provided .class2(). made four rough categories: dimension, type, language, numeric. Note internal generics behave differently, instead immediately falling back default default case.","code":"# dimension class .class2(matrix(\"a\")) #> [1] \"matrix\" \"array\" \"character\" .class2(array(\"a\")) #> [1] \"array\" \"character\" # typeof(), with some renaming .class2(sum) #> [1] \"function\" .class2(quote(x)) #> [1] \"name\" # language class .class2(quote({})) #> [1] \"{\" # similarly for if, while, for, =, <-, ( # numeric .class2(1) #> [1] \"double\" \"numeric\""},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s7-6","dir":"Articles > Spec","previous_headings":"Implicit class","what":"S7","title":"Dispatch","text":"Suggest defining new r7class() function returns simplified implicit class, dropping language classes. Dispatch use rules R C. (performance implications?)","code":""},{"path":[]},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s3-7","dir":"Articles > Spec","previous_headings":"Multi-dispatch","what":"S3","title":"Dispatch","text":"Special dispatch? c(), cbind(), rbind() (+ cbind2() rbind2()) — iterated double dispatch. Need describe detail solid assessment S7 might need.ez gitDot-dot-dot dispatch, assumes class vctrs used two pass approach (find type coerce)","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/dispatch.html","id":"s7-7","dir":"Articles > Spec","previous_headings":"Multi-dispatch","what":"S7","title":"Dispatch","text":"Initially, don’t provide support user generics dispatch …? Instead suggest people use Reduce plus double-dispatch.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/proposal.html","id":"executive-summary","dir":"Articles > Spec","previous_headings":"","what":"Executive summary","title":"Proposal","text":"Object-oriented programming enables R developers implement abstractions, introduce domain-specific data models, interface external systems, among things. Unlike modern programming languages, R lacks dominant approach object-oriented programming. competition existing approaches unproductive contributes social fragmentation community technical hurdles integrating across different systems. propose form working group develop design proposal system combine important elements existing approaches remaining compatible . involve key technical experts community stakeholders, including R Core tidyverse. Upon publishing design proposal, invite entire community contribute feedback. Finally, conclude working group developing releasing strategy implementing, maintaining, adopting framework.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/proposal.html","id":"background","dir":"Articles > Spec","previous_headings":"","what":"Background","title":"Proposal","text":"R, everything object. principle facilitates interacting data, dataset modeled tangible, real world object. user can display contents object, introspect structure manipulate various ways. R provides basic types objects necessary statistical computing. effectively reason domain, like genomics, need specialize data types richly model semantics particular domain. specialized set data structures, recognize need /abstraction/, models commonality across data types. Abstraction also useful existing data types, package provide alternative implementation, example, based database distributed computing system. Abstraction requires system classification, object corresponds specific class, class derives another. classification system transcends class instances (objects), helpful explicitly refer classes programming. Fundamentally, class-based object-oriented system two requirements: * centrally defined class hierarchy, * Every object instance class. class defined contract governing structure contents. contract extends contract parent class order add semantics additional constraints remaining compatible parent contract. Code manipulating objects often make assumptions structure content objects. mitigate risk, low-level code benefits validation function, essentially codification class contract, verify assumptions. intrinsic value formal modeling data, software fully take advantage richer semantics, requires /polymorphism/, behavior software respect object depends class object. object-oriented languages implement message-passing OOP, classes define behavior holding functions, called methods, addition fields. one class calls method another class, passes message. R systems based message-passing, notably R6 package reference classes methods package. rely message-passing part objects mutable easier reason code can typically assume receiver mutated. exclude scope systems mutable objects, immutable objects generally preferable interactive data analysis, relegating mutable systems niche applications, GUIs caching mechanisms. appropriate statistical computing language, R functional roots, prevalent object-oriented approaches R functional systems, namely S3 S4, corresponding third fourth version S language, respectively. Objects tend immutable, top-level functions can generic, means means dispatch another function, called method, based types passed arguments. simplest type generic dispatches single argument. single dispatch supports applications polymorphism, many cases behavior depends interaction two classes. Typical examples include arithmetic, converting object one class another combining two different types object. considerations, conclude good object-oriented system support: * explicit class hierarchy (represented reified objects) * Systematic instance construction validation; * Multiple, least double, dispatch, * Objects transparent, introspectable structure.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/proposal.html","id":"problem-statement","dir":"Articles > Spec","previous_headings":"","what":"Problem statement","title":"Proposal","text":"two major OOP frameworks R, S3 S4, limitations, neither one sufficiently applicable gain dominance. led social fracturing community technical impediments compatibility interoperability. summarize limitations table . S3 defines classes implicitly instance level, explicit class hierarchy. S3 system supports tracking class every object, systematic means constructing validating ensure correctness. S3 supports single dispatch, difficult write polymorphic code arithmetic, merging objects, converting objects, etc. S4 solutions problems, quite ambitious, introducing significant complexity, unusual syntax loss transparency. Multiple inheritance, expressive powerful, allows multiple overlapping taxonomies, difficult reason , difficulty increases quadratically combined multiple dispatch, method selection uses distance calculation /n/ dimensions /n/ number arguments. syntax defining classes methods non-idiomatic relies side effects. Finally, S4 convention (although requirement) hide slots behind API, improves encapsulation prevents basic introspection capabilities desirable analyzing data R users come expect. Somewhat tangentionally, still motivating, also technical issues methods package, implementation S4 system. incremental growth decades led excessive complexity, well performance issues. absence new system, need reimplement S4, implementation effort regardless. Documentation limitations afflict S3 S4. difficult describe programming interface consists generic functions coupled class. package can define method generic extend class, documentation needs adapt according packages loaded.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/proposal.html","id":"proposal","dir":"Articles > Spec","previous_headings":"","what":"Proposal","title":"Proposal","text":"believe may better way, solutions obvious. Across popular programming languages, notable exception Julia, functional OOP much less common less well developed message-passing, examples R follow advances likely require research. Therefore, propose bring together panel experts formally assess situation design solution. Since aiming solution unify community, aim widespread adoption, require involvement key community leaders. invite community review proposal contribute feedback ideas. working group integrate feedback finalize proposal. conclude developing strategy implementation, adoption long-term maintenance, directly responsible. funding required requested effort.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/proposal.html","id":"objectives","dir":"Articles > Spec","previous_headings":"","what":"Objectives","title":"Proposal","text":"Release finalized design specification unifying object-oriented programming system, Recommend ISC strategy implementing maintaining system, well driving adoption.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/proposal.html","id":"milestones","dir":"Articles > Spec","previous_headings":"","what":"Milestones","title":"Proposal","text":"Finalize membership, Agree upon prioritize system requirements, Iterate design proposals, Release proposal community review contribution, Incorporate community contributions, Submit finalized proposal, Develop submit implementation adoption strategy.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/proposal.html","id":"membership","dir":"Articles > Spec","previous_headings":"","what":"Membership","title":"Proposal","text":"founding members : Michael Lawrence :: Representing R-core (S4-based) Bioconductor, maintainer methods package; Hadley Wickham :: Representing RStudio tidyverse project, relies heavily S3; Martin Maechler :: Representing R-core, maintainer S4-based Matrix Rmpfr packages, maintainer methods package. also invited representatives R Ladies ROpenSci communities. collaborate others R Core, keeping informed plans incorporating feedback.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"list-of-requirements","dir":"Articles > Spec","previous_headings":"","what":"List of requirements","title":"Requirements","text":"system compatible possible existing systems, particularly S3 Classes first class objects, extrinsic instances system support formal construction, casting, coercion create new instances classes. convenient systematically validate instance Double dispatch, possibly general multiple dispatch, supported Inheritance simple possible, ideally single (features might compensate lack multiple inheritance) Syntax intuitive idiomatic, rely side effects loose conventions Namespace management complicated S3 currently Performance competitive existing solutions design simple implement possible package define method generic classes defined outside package aim facilitate API evolution, particularly relates inter-package dependencies Methods must include formal arguments generic (like Julia) Generics support ... formal argument lists methods can append arguments lists Fields “public” visibility, support encapsulation (implicit getters setters) system support reflection system support lazy dynamic registration classes methods.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"compatibility","dir":"Articles > Spec","previous_headings":"","what":"Compatibility","title":"Requirements","text":"Ideally new system extension S3, S3 already bottom stack many systems compatibility S3.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"classes-as-first-class-objects","dir":"Articles > Spec","previous_headings":"","what":"Classes as first class objects","title":"Requirements","text":"important classes defined extrinsically instances, makes data contract obvious developers (reading code) users (interacting objects). S4 represents classes proper objects; however, typically developers refer classes name (string literal) interacting S4 API. Interacting directly objects instead likely simplify API (syntax) implementation.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"generics-as-extended-function-objects","dir":"Articles > Spec","previous_headings":"","what":"Generics as extended function objects","title":"Requirements","text":"Generic functions represented special class function object tracks method table. puts generic functions level classes, essence functional OOP, likely enable natural syntax method registration.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"formal-instantiation-and-type-conversion","dir":"Articles > Spec","previous_headings":"","what":"Formal instantiation and type conversion","title":"Requirements","text":"Class instantiation happen formal constructor. created, object keep original class unless subjected formal casting formal coercion. class object robust predictable, direct class assignment (e.g. class<-()) generally avoided.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"systematic-validation","dir":"Articles > Spec","previous_headings":"","what":"Systematic validation","title":"Requirements","text":"Class contracts often complicated simple list typed fields. formally supported convention around validating objects important code can make necessary assumptions data structures content. Otherwise, developers trying defensive resort ad hoc checks scattered throughout code.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"multiple-dispatch","dir":"Articles > Spec","previous_headings":"","what":"Multiple dispatch","title":"Requirements","text":"system least need support double dispatch, code can polymorphic interaction two arguments. many obvious examples: arithmetic, serialization (object, target), coercion, etc. less likely require dispatch beyond two arguments, advantages probably outweighed increase complexity. many cases triple dispatch higher wild, developers abusing dispatch implement type checks, instead polymorphism. Almost always, can deconstruct multiple dispatch series double dispatch calls. General multiple dispatch makes software harder understand difficult implement.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"inheritance","dir":"Articles > Spec","previous_headings":"","what":"Inheritance","title":"Requirements","text":"Inheritance lets us define data taxonomy, often natural organize set data structures multiple, orthogonal taxonomies. Multiple inheritance enables ; however, also adds lot complexity, software relying underlying system. often possible beneficial (long term) rely techniques like composition delegation instead. consider single inheritance languages like Java successful, although directly comparable functional system.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"syntax","dir":"Articles > Spec","previous_headings":"","what":"Syntax","title":"Requirements","text":"entire API free side effects odd conventions, like making . character significant method names. Whereas S3 supports implicit method registration based name method, new system require methods registered explicitly. Direct manipulation class generic objects enable .","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"namespaces","dir":"Articles > Spec","previous_headings":"","what":"Namespaces","title":"Requirements","text":"system support exporting generics classes. classes objects, can treated like object exporting importing symbols. generics objects, simple export methods generic. clear whether selective method export important. One use case hide method internal class signature avoid unnecessary documentation. Perhaps R CMD check ignore methods unexported classes. need explicit method registration.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"third-party-methods","dir":"Articles > Spec","previous_headings":"","what":"Third party methods","title":"Requirements","text":"fully realize potential interoperability afforded functional OOP, treating generics classes orthogonal, allow packages extend externally defined API supports externally defined classes. cases, method defined either owner generic owner class, “ownership” somewhat nebulous concept. acknowledge potential conflicts arising multiple packages defining methods generic overlapping signatures, well danger injecting new behaviors violate assumptions existing method definitions.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"formal-arguments","dir":"Articles > Spec","previous_headings":"","what":"Formal arguments","title":"Requirements","text":"formal arguments generic must present every method generic. contrast Julia, methods can completely different sets arguments. favor fixed set formal arguments sake consistency enable calling code depend minimal set arguments always present. generic formal argument list includes ..., methods can add arguments. extra arguments useful controlling specialized behaviors, long calling code can assume calling generic always dispatch method handles accordance documentation. accordance Liskov Substitution Principle, explore enforcing method adds arguments method dispatching parent class. easiest conceptualize useful single dispatch case, also able develop set rules nested multiple dispatch.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"field-visibility","dir":"Articles > Spec","previous_headings":"","what":"Field visibility","title":"Requirements","text":"Functional OOP incompatible notion private fields, code run context class, thus way accept deny access field. R users also expect appreciate object transparency. consider enabling encapsulation field access modification similar reference classes allow defining fields active bindings.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"reflection-and-dynamism","dir":"Articles > Spec","previous_headings":"","what":"Reflection and dynamism","title":"Requirements","text":"Given class generic, able find appropriate method without calling . important building tooling around system.","code":""},{"path":"https://rconsortium.github.io/S7/articles/spec/requirements.html","id":"lazy-and-dynamic-registration","dir":"Articles > Spec","previous_headings":"","what":"Lazy and dynamic registration","title":"Requirements","text":"flip side, also able register method lazily/dynamically run-time. important : Generics classes suggested packages, method registration can occur dependency loaded. Testing, since may want define method within test. particularly useful used eliminate need mocking. Interface evolution, can provide method generic class yet exist, anticipating future release dependency.","code":""},{"path":"https://rconsortium.github.io/S7/authors.html","id":null,"dir":"","previous_headings":"","what":"Authors","title":"Authors and Citation","text":"Object-Oriented Programming Working Group. Copyright holder. Davis Vaughan. Author. Jim Hester. Author. Tomasz Kalinowski. Author. Landau. Author. Michael Lawrence. Author. Martin Maechler. Author. Luke Tierney. Author. Hadley Wickham. Author, maintainer.","code":""},{"path":"https://rconsortium.github.io/S7/authors.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Authors and Citation","text":"Vaughan D, Hester J, Kalinowski T, Landau W, Lawrence M, Maechler M, Tierney L, Wickham H (2024). S7: Object Oriented System Meant Become Successor S3 S4. R package version 0.1.1.9000, https://rconsortium.github.io/S7/, https://github.com/rconsortium/S7/.","code":"@Manual{, title = {S7: An Object Oriented System Meant to Become a Successor to S3 and S4}, author = {Davis Vaughan and Jim Hester and Tomasz Kalinowski and Will Landau and Michael Lawrence and Martin Maechler and Luke Tierney and Hadley Wickham}, year = {2024}, note = {R package version 0.1.1.9000, https://rconsortium.github.io/S7/}, url = {https://github.com/rconsortium/S7/}, }"},{"path":"https://rconsortium.github.io/S7/index.html","id":"s7","dir":"","previous_headings":"","what":"An Object Oriented System Meant to Become a Successor to S3 and S4","title":"An Object Oriented System Meant to Become a Successor to S3 and S4","text":"S7 package new OOP system designed successor S3 S4. designed implemented collaboratively R Consortium Object-Oriented Programming Working Group, includes representatives R-Core, Bioconductor, tidyverse/Posit, wider R community. S7 somewhat experimental; confident design relatively little usage wild currently. hope avoid major breaking changes, reserve right discover major problems.","code":""},{"path":"https://rconsortium.github.io/S7/index.html","id":"installation","dir":"","previous_headings":"","what":"Installation","title":"An Object Oriented System Meant to Become a Successor to S3 and S4","text":"long-term goal project merge S7 base R. now, can experiment installing CRAN:","code":"install.packages(\"S7\")"},{"path":"https://rconsortium.github.io/S7/index.html","id":"usage","dir":"","previous_headings":"","what":"Usage","title":"An Object Oriented System Meant to Become a Successor to S3 and S4","text":"section gives brief overview entirety S7. Learn basics vignette(\"S7\"), generics methods vignette(\"generics-methods\"), classes objects vignette(\"classes-objects\"), compatibility S3 S4 vignette(\"compatibility\").","code":"library(S7)"},{"path":"https://rconsortium.github.io/S7/index.html","id":"classes-and-objects","dir":"","previous_headings":"Usage","what":"Classes and objects","title":"An Object Oriented System Meant to Become a Successor to S3 and S4","text":"S7 classes formal definition, includes list properties optional validator. Use new_class() define class: new_class() returns class object, also constructor use create instances class:","code":"range <- new_class(\"range\", properties = list( start = class_double, end = class_double ), validator = function(self) { if (length(self@start) != 1) { \"@start must be length 1\" } else if (length(self@end) != 1) { \"@end must be length 1\" } else if (self@end < self@start) { \"@end must be greater than or equal to @start\" } } ) x <- range(start = 1, end = 10) x #> #> @ start: num 1 #> @ end : num 10"},{"path":"https://rconsortium.github.io/S7/index.html","id":"properties","dir":"","previous_headings":"Usage","what":"Properties","title":"An Object Oriented System Meant to Become a Successor to S3 and S4","text":"data possessed object called properties. Use @ get set properties: Properties automatically validated type declared new_class() (double case), class validator:","code":"x@start #> [1] 1 x@end <- 20 x #> #> @ start: num 1 #> @ end : num 20 x@end <- \"x\" #> Error: @end must be , not x@end <- -1 #> Error: object is invalid: #> - @end must be greater than or equal to @start"},{"path":"https://rconsortium.github.io/S7/index.html","id":"generics-and-methods","dir":"","previous_headings":"Usage","what":"Generics and methods","title":"An Object Oriented System Meant to Become a Successor to S3 and S4","text":"Like S3 S4, S7 uses functional OOP methods belong generic functions, method calls look like function calls: generic(object, arg2, arg3). style called functional outside looks like regular function call, internally components also functions. Use new_generic() create new generic: first argument generic name (used error messages) second gives arguments used dispatch. third, optional argument, supplies body generic. needed generic additional arguments aren’t used method dispatch. generic, can define method specific class method<-: can use method<- register methods base types S7 generics, S7 classes S3 S4 generics. See vignette(\"compatibility\") details.","code":"inside <- new_generic(\"inside\", \"x\") # Add a method for our class method(inside, range) <- function(x, y) { y >= x@start & y <= x@end } inside(x, c(0, 5, 10, 15)) #> [1] FALSE TRUE TRUE TRUE"},{"path":"https://rconsortium.github.io/S7/reference/S4_register.html","id":null,"dir":"Reference","previous_headings":"","what":"Register an S7 class with S4 — S4_register","title":"Register an S7 class with S4 — S4_register","text":"want use method<- register method S4 generic S7 class, need call S4_register() .","code":""},{"path":"https://rconsortium.github.io/S7/reference/S4_register.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Register an S7 class with S4 — S4_register","text":"","code":"S4_register(class, env = parent.frame())"},{"path":"https://rconsortium.github.io/S7/reference/S4_register.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Register an S7 class with S4 — S4_register","text":"class S7 class created new_class(). env Expert use . Environment S4 class registered.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S4_register.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Register an S7 class with S4 — S4_register","text":"Nothing; function called side-effect.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S4_register.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Register an S7 class with S4 — S4_register","text":"","code":"methods::setGeneric(\"S4_generic\", function(x) { standardGeneric(\"S4_generic\") }) #> [1] \"S4_generic\" foo <- new_class(\"foo\") S4_register(foo) method(S4_generic, foo) <- function(x) \"Hello\" S4_generic(foo()) #> [1] \"Hello\""},{"path":"https://rconsortium.github.io/S7/reference/S7_class.html","id":null,"dir":"Reference","previous_headings":"","what":"Retrieve the S7 class of an object — S7_class","title":"Retrieve the S7 class of an object — S7_class","text":"Given S7 object, find class.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_class.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Retrieve the S7 class of an object — S7_class","text":"","code":"S7_class(object)"},{"path":"https://rconsortium.github.io/S7/reference/S7_class.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Retrieve the S7 class of an object — S7_class","text":"object S7 object","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_class.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Retrieve the S7 class of an object — S7_class","text":"S7 class.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_class.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Retrieve the S7 class of an object — S7_class","text":"","code":"foo <- new_class(\"foo\") S7_class(foo()) #> class #> @ parent : #> @ constructor: function() {...} #> @ validator : #> @ properties :"},{"path":"https://rconsortium.github.io/S7/reference/S7_data.html","id":null,"dir":"Reference","previous_headings":"","what":"Get/set underlying ","title":"Get/set underlying ","text":"S7 class inherits existing base type, can useful work underlying object, .e. S7 object stripped class properties.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_data.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Get/set underlying ","text":"","code":"S7_data(object) S7_data(object, check = TRUE) <- value"},{"path":"https://rconsortium.github.io/S7/reference/S7_data.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Get/set underlying ","text":"object object S7 class check TRUE, check value correct type run validate() object returning. value Object used replace underlying data.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_data.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Get/set underlying ","text":"S7_data() returns data stored base object; S7_data<-() called side-effects returns object invisibly.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_data.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Get/set underlying ","text":"","code":"text <- new_class(\"text\", parent = class_character) y <- text(c(foo = \"bar\")) y #> Named chr \"bar\" #> - attr(*, \"names\")= chr \"foo\" S7_data(y) #> foo #> \"bar\" S7_data(y) <- c(\"a\", \"b\") y #> Named chr [1:2] \"a\" \"b\" #> - attr(*, \"names\")= chr [1:2] \"foo\" NA"},{"path":"https://rconsortium.github.io/S7/reference/S7_inherits.html","id":null,"dir":"Reference","previous_headings":"","what":"Does this object inherit from an S7 class? — S7_inherits","title":"Does this object inherit from an S7 class? — S7_inherits","text":"S7_inherits() returns TRUE FALSE. check_is_S7() throws error x specified class.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_inherits.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Does this object inherit from an S7 class? — S7_inherits","text":"","code":"S7_inherits(x, class = NULL) check_is_S7(x, class = NULL, arg = deparse(substitute(x)))"},{"path":"https://rconsortium.github.io/S7/reference/S7_inherits.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Does this object inherit from an S7 class? — S7_inherits","text":"x object class S7 class NULL. NULL, tests whether x S7 object without testing specific class. arg Argument name used error message.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_inherits.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Does this object inherit from an S7 class? — S7_inherits","text":"S7_inherits() returns single TRUE FALSE. check_is_S7() returns nothing; called side-effects.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_inherits.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Does this object inherit from an S7 class? — S7_inherits","text":"","code":"foo1 <- new_class(\"foo1\") foo2 <- new_class(\"foo2\") S7_inherits(foo1(), foo1) #> [1] TRUE check_is_S7(foo1()) check_is_S7(foo1(), foo1) S7_inherits(foo1(), foo2) #> [1] FALSE try(check_is_S7(foo1(), foo2)) #> Error : `foo1()` must be a , not a "},{"path":"https://rconsortium.github.io/S7/reference/S7_object.html","id":null,"dir":"Reference","previous_headings":"","what":"Base S7 class — S7_object","title":"Base S7 class — S7_object","text":"base class S7 classes eventually inherit .","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_object.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Base S7 class — S7_object","text":"","code":"S7_object()"},{"path":"https://rconsortium.github.io/S7/reference/S7_object.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Base S7 class — S7_object","text":"base S7 object.","code":""},{"path":"https://rconsortium.github.io/S7/reference/S7_object.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Base S7 class — S7_object","text":"","code":"S7_object #> class #> @ parent : #> @ constructor: function() {...} #> @ validator : function(self) {...} #> @ properties :"},{"path":"https://rconsortium.github.io/S7/reference/as_class.html","id":null,"dir":"Reference","previous_headings":"","what":"Standard class specifications — as_class","title":"Standard class specifications — as_class","text":"used interface S7 R's OO systems, allowing use S7 classes methods base types, informal S3 classes, formal S4 classes.","code":""},{"path":"https://rconsortium.github.io/S7/reference/as_class.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Standard class specifications — as_class","text":"","code":"as_class(x, arg = deparse(substitute(x)))"},{"path":"https://rconsortium.github.io/S7/reference/as_class.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Standard class specifications — as_class","text":"x class specification. One following: S7 class (created new_class()). S7 union (created new_union()). S3 class (created new_S3_class()). S4 class (created methods::getClass() methods::new()). base class, like class_logical, class_integer, class_double. \"special\", either class_missing class_any. arg Argument name used generating errors.","code":""},{"path":"https://rconsortium.github.io/S7/reference/as_class.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Standard class specifications — as_class","text":"standardised class: either NULL, S7 class, S7 union, new_S3_class, S4 class.","code":""},{"path":"https://rconsortium.github.io/S7/reference/as_class.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Standard class specifications — as_class","text":"","code":"as_class(class_logical) #> : as_class(new_S3_class(\"factor\")) #> : S3"},{"path":"https://rconsortium.github.io/S7/reference/base_classes.html","id":null,"dir":"Reference","previous_headings":"","what":"S7 wrappers for base types — base_classes","title":"S7 wrappers for base types — base_classes","text":"following S7 classes represent base types allowing used within S7: class_logical class_integer class_double class_complex class_character class_raw class_list class_expression class_name class_call class_function class_environment (can used properties) also include three union types model numerics, atomics, vectors respectively: class_numeric union class_integer class_double. class_atomic union class_logical, class_numeric, class_complex, class_character, class_raw. class_vector union class_atomic, class_list, class_expression. class_language union class_name class_call.","code":""},{"path":"https://rconsortium.github.io/S7/reference/base_classes.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"S7 wrappers for base types — base_classes","text":"","code":"class_logical class_integer class_double class_complex class_character class_raw class_list class_expression class_name class_call class_function class_environment class_numeric class_atomic class_vector class_language"},{"path":"https://rconsortium.github.io/S7/reference/base_classes.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"S7 wrappers for base types — base_classes","text":"S7 classes wrapping around common base types S3 classes.","code":""},{"path":"https://rconsortium.github.io/S7/reference/base_classes.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"S7 wrappers for base types — base_classes","text":"","code":"class_integer #> : class_numeric #> : or class_factor #> : S3"},{"path":"https://rconsortium.github.io/S7/reference/base_s3_classes.html","id":null,"dir":"Reference","previous_headings":"","what":"S7 wrappers for key S3 classes — base_s3_classes","title":"S7 wrappers for key S3 classes — base_s3_classes","text":"S7 bundles S3 definitions key S3 classes provided base packages: class_data.frame data frames. class_Date dates. class_factor factors. class_POSIXct, class_POSIXlt class_POSIXt date-times. class_formula formulas.","code":""},{"path":"https://rconsortium.github.io/S7/reference/base_s3_classes.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"S7 wrappers for key S3 classes — base_s3_classes","text":"","code":"class_factor class_Date class_POSIXct class_POSIXlt class_POSIXt class_data.frame class_formula"},{"path":"https://rconsortium.github.io/S7/reference/class_any.html","id":null,"dir":"Reference","previous_headings":"","what":"Dispatch on any class — class_any","title":"Dispatch on any class — class_any","text":"Use class_any register default method called methods matched.","code":""},{"path":"https://rconsortium.github.io/S7/reference/class_any.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Dispatch on any class — class_any","text":"","code":"class_any"},{"path":"https://rconsortium.github.io/S7/reference/class_any.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Dispatch on any class — class_any","text":"","code":"foo <- new_generic(\"foo\", \"x\") method(foo, class_numeric) <- function(x) \"number\" method(foo, class_any) <- function(x) \"fallback\" foo(1) #> [1] \"number\" foo(\"x\") #> [1] \"fallback\""},{"path":"https://rconsortium.github.io/S7/reference/class_missing.html","id":null,"dir":"Reference","previous_headings":"","what":"Dispatch on a missing argument — class_missing","title":"Dispatch on a missing argument — class_missing","text":"Use class_missing dispatch user supplied argument, .e. missing sense missing(), sense .na().","code":""},{"path":"https://rconsortium.github.io/S7/reference/class_missing.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Dispatch on a missing argument — class_missing","text":"","code":"class_missing"},{"path":"https://rconsortium.github.io/S7/reference/class_missing.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Dispatch on a missing argument — class_missing","text":"Sentinel objects used special types dispatch.","code":""},{"path":"https://rconsortium.github.io/S7/reference/class_missing.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Dispatch on a missing argument — class_missing","text":"","code":"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) #> [1] \"number\" foo() #> [1] \"missing\" foo(\"\") #> [1] \"fallback\""},{"path":"https://rconsortium.github.io/S7/reference/convert.html","id":null,"dir":"Reference","previous_headings":"","what":"Convert an object from one type to another — convert","title":"Convert an object from one type to another — convert","text":"convert(, ) built-generic converting object one type another. special three ways: uses double-dispatch, conversion depends . uses non-standard dispatch class, object. use inheritance argument. understand , imagine written methods objects various types classParent. create new classChild inherits classParent, expect methods written classParent work methods return classParent objects, classChild objects. convert() provides two default implementations: inherits , strips properties possesses (downcasting). subclass 's class, creates new object class , copying existing properties initializing new properties (upcasting). converting object solely purposes accessing method superclass, probably want super() instead. See docs details.","code":""},{"path":"https://rconsortium.github.io/S7/reference/convert.html","id":"s-amp-s-","dir":"Reference","previous_headings":"","what":"S3 & S4","title":"Convert an object from one type to another — convert","text":"convert() plays similar role convention defining .foo() functions/generics S3, ()/setAs() S4.","code":""},{"path":"https://rconsortium.github.io/S7/reference/convert.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Convert an object from one type to another — convert","text":"","code":"convert(from, to, ...)"},{"path":"https://rconsortium.github.io/S7/reference/convert.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Convert an object from one type to another — convert","text":"S7 object convert. S7 class specification, passed as_class(). ... arguments passed custom convert() methods. upcasting, can used override existing properties set new ones.","code":""},{"path":"https://rconsortium.github.io/S7/reference/convert.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Convert an object from one type to another — convert","text":"Either coerced class , error coercion possible.","code":""},{"path":"https://rconsortium.github.io/S7/reference/convert.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Convert an object from one type to another — convert","text":"","code":"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) #> #> @ x: int 1 # Upcasting: S7 also provides a default implementation for coercing an object # to one of its child classes: convert(foo1(x = 1L), to = foo2) #> #> @ x: int 1 #> @ y: num(0) convert(foo1(x = 1L), to = foo2, y = 2.5) # Set new property #> #> @ x: int 1 #> @ y: num 2.5 convert(foo1(x = 1L), to = foo2, x = 2L, y = 2.5) # Override existing and set new #> #> @ x: int 2 #> @ y: num 2.5 # For all other cases, you'll need to provide your own. try(convert(foo1(x = 1L), to = class_integer)) #> Error : Can't find method for generic `convert()` with dispatch classes: #> - from: #> - to : #> method(convert, list(foo1, class_integer)) <- function(from, to) { from@x } convert(foo1(x = 1L), to = class_integer) #> [1] 1 # Note that conversion does not respect inheritance so if we define a # convert method for integer to foo1 method(convert, list(class_integer, foo1)) <- function(from, to) { foo1(x = from) } convert(1L, to = foo1) #> #> @ x: int 1 # Converting to foo2 will still error try(convert(1L, to = foo2)) #> Error : Can't find method for generic `convert()` with dispatch classes: #> - from: #> - to : #> # This is probably not surprising because foo2 also needs some value # for `@y`, but it definitely makes dispatch for convert() special"},{"path":"https://rconsortium.github.io/S7/reference/method-set.html","id":null,"dir":"Reference","previous_headings":"","what":"Register an S7 method for a generic — method<-","title":"Register an S7 method for a generic — method<-","text":"generic defines interface function. created generic new_generic(), provide implementations specific signatures registering methods method<-. goal method<- single function need working S7 generics S7 classes. means well registering methods S7 classes S7 generics, can also register methods S7 classes S3 S4 generics, S3 S4 classes S7 generics. general method registration function: least one generic signature needs S7. Note writing package, must call methods_register() .onLoad. ensures methods dynamically registered needed.","code":""},{"path":"https://rconsortium.github.io/S7/reference/method-set.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Register an S7 method for a generic — method<-","text":"","code":"method(generic, signature) <- value"},{"path":"https://rconsortium.github.io/S7/reference/method-set.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Register an S7 method for a generic — method<-","text":"generic generic function, .e. S7 generic, external generic, S3 generic, S4 generic. signature method signature. S7 generics use single dispatch, must one following: S7 class (created new_class()). S7 union (created new_union()). S3 class (created new_S3_class()). S4 class (created methods::getClass() methods::new()). base type like class_logical, class_integer, class_numeric. special type like class_missing class_any. S7 generics use multiple dispatch, must list types. S3 generics, must single S7 class. S4 generics, must either S7 class, list includes least one S7 class. value function implements generic specification given signature.","code":""},{"path":"https://rconsortium.github.io/S7/reference/method-set.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Register an S7 method for a generic — method<-","text":"generic, invisibly.","code":""},{"path":"https://rconsortium.github.io/S7/reference/method-set.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Register an S7 method for a generic — method<-","text":"","code":"# Create a generic bizarro <- new_generic(\"bizarro\", \"x\") # Register some methods method(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 automatically bizarro(head(mtcars)) #> carb gear am vs qsec wt drat hp disp cyl mpg #> Mazda RX4 1 3 0 1 20.22 3.460 2.76 105 225 6 18.1 #> Mazda RX4 Wag 2 3 0 0 17.02 3.440 3.15 175 360 8 18.7 #> Datsun 710 1 3 0 1 19.44 3.215 3.08 110 258 6 21.4 #> Hornet 4 Drive 1 4 1 1 18.61 2.320 3.85 93 108 4 22.8 #> Hornet Sportabout 4 4 1 0 17.02 2.875 3.90 110 160 6 21.0 #> Valiant 4 4 1 0 16.46 2.620 3.90 110 160 6 21.0"},{"path":"https://rconsortium.github.io/S7/reference/method.html","id":null,"dir":"Reference","previous_headings":"","what":"Find a method for an S7 generic — method","title":"Find a method for an S7 generic — method","text":"method() takes generic class signature performs method dispatch find corresponding method implementation. rarely needed usually rely generic dispatch (via S7_dispatch()). However, introspection useful want see implementation specific method.","code":""},{"path":"https://rconsortium.github.io/S7/reference/method.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Find a method for an S7 generic — method","text":"","code":"method(generic, class = NULL, object = NULL)"},{"path":"https://rconsortium.github.io/S7/reference/method.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Find a method for an S7 generic — method","text":"generic generic function, .e. S7 generic, external generic, S3 generic, S4 generic. class, object Perform introspection either class (processed as_class()) concrete object. generic uses multiple dispatch object class must list classes/objects.","code":""},{"path":"https://rconsortium.github.io/S7/reference/method.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Find a method for an S7 generic — method","text":"Either function class S7_method error matching method found.","code":""},{"path":[]},{"path":"https://rconsortium.github.io/S7/reference/method.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Find a method for an S7 generic — method","text":"","code":"# Create a generic and register some methods bizarro <- 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 method bizarro #> bizarro(x, ...) with 3 methods: #> 1: method(bizarro, class_integer) #> 2: method(bizarro, class_double) #> 3: method(bizarro, new_S3_class(\"factor\")) # And you can use method() to inspect specific implementations method(bizarro, class = class_integer) #> method(bizarro, class_integer) #> function (x) #> rev(x) #> method(bizarro, object = 1) #> method(bizarro, class_double) #> function (x) #> rev(x) #> method(bizarro, class = class_factor) #> method(bizarro, new_S3_class(\"factor\")) #> function (x) #> { #> levels(x) <- rev(levels(x)) #> x #> } #> # errors if method not found try(method(bizarro, class = class_data.frame)) #> Error : Can't find method for `bizarro(S3)`. try(method(bizarro, object = \"x\")) #> Error : Can't find method for `bizarro()`."},{"path":"https://rconsortium.github.io/S7/reference/method_explain.html","id":null,"dir":"Reference","previous_headings":"","what":"Explain method dispatch — method_explain","title":"Explain method dispatch — method_explain","text":"method_explain() shows possible methods call generic might use, ones exist, one actually called. Note method dispatch uses string representation class class hierarchy. class system uses slightly different convention avoid ambiguity. S7: pkg::class class S4: S4/pkg::class S4/class S3: class","code":""},{"path":"https://rconsortium.github.io/S7/reference/method_explain.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Explain method dispatch — method_explain","text":"","code":"method_explain(generic, class = NULL, object = NULL)"},{"path":"https://rconsortium.github.io/S7/reference/method_explain.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Explain method dispatch — method_explain","text":"generic generic function, .e. S7 generic, external generic, S3 generic, S4 generic. class, object Perform introspection either class (processed as_class()) concrete object. generic uses multiple dispatch object class must list classes/objects.","code":""},{"path":"https://rconsortium.github.io/S7/reference/method_explain.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Explain method dispatch — method_explain","text":"Nothing; function called side effects.","code":""},{"path":"https://rconsortium.github.io/S7/reference/method_explain.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Explain method dispatch — method_explain","text":"","code":"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)) #> add([foo2], [foo2]) #> -> add([foo2], [foo1]) #> add([foo2], [S7_object]) #> add([foo2], [ANY]) #> add([foo1], [foo2]) #> * add([foo1], [foo1]) #> add([foo1], [S7_object]) #> add([foo1], [ANY]) #> add([S7_object], [foo2]) #> add([S7_object], [foo1]) #> add([S7_object], [S7_object]) #> add([S7_object], [ANY]) #> add([ANY], [foo2]) #> add([ANY], [foo1]) #> add([ANY], [S7_object]) #> add([ANY], [ANY])"},{"path":"https://rconsortium.github.io/S7/reference/methods_register.html","id":null,"dir":"Reference","previous_headings":"","what":"Register methods in a package — methods_register","title":"Register methods in a package — methods_register","text":"using S7 package always call methods_register() package loaded. ensures methods registered needed implement methods generics (S3, S4, S7) packages. (strictly necessary register methods generics package, better include need forget include hit weird errors.)","code":""},{"path":"https://rconsortium.github.io/S7/reference/methods_register.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Register methods in a package — methods_register","text":"","code":"methods_register()"},{"path":"https://rconsortium.github.io/S7/reference/methods_register.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Register methods in a package — methods_register","text":"Nothing; called side-effects.","code":""},{"path":"https://rconsortium.github.io/S7/reference/methods_register.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Register methods in a package — methods_register","text":"","code":".onLoad <- function(...) { S7::methods_register() }"},{"path":"https://rconsortium.github.io/S7/reference/new_S3_class.html","id":null,"dir":"Reference","previous_headings":"","what":"Declare an S3 class — new_S3_class","title":"Declare an S3 class — new_S3_class","text":"use S3 class S7, must explicitly declare using new_S3_class() S3 lacks formal class definition. (Unless important base class already defined base_s3_classes.)","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_S3_class.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Declare an S3 class — new_S3_class","text":"","code":"new_S3_class(class, constructor = NULL, validator = NULL)"},{"path":"https://rconsortium.github.io/S7/reference/new_S3_class.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Declare an S3 class — new_S3_class","text":"class S3 class vector (.e. class() returns). method registration, can abbreviate single string, S3 class name. constructor optional constructor can used create objects specified class. needed wish S7 class inherit S3 class use S3 class property without default. must specified way S7 constructor: first argument .data (base type whose attributes modified). arguments constructor default values constructor called arguments, returns returns \"empty\", valid, object. validator optional validator used validate() check S7 object adheres constraints S3 class. validator single argument function takes object validate returns NULL object valid. object invalid, returns character vector problems.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_S3_class.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Declare an S3 class — new_S3_class","text":"S7 definition S3 class, .e. list class S7_S3_class.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_S3_class.html","id":"method-dispatch-properties-and-unions","dir":"Reference","previous_headings":"","what":"Method dispatch, properties, and unions","title":"Declare an S3 class — new_S3_class","text":"three ways using S3 S7 require S3 class vector: Registering S3 method S7 generic. Restricting S7 property S3 class. Using S3 class S7 union. easy, can usually include new_S3_class() call inline:","code":"method(my_generic, new_S3_class(\"factor\")) <- function(x) \"A factor\" new_class(\"my_class\", properties = list(types = new_S3_class(\"factor\"))) new_union(\"character\", new_S3_class(\"factor\"))"},{"path":"https://rconsortium.github.io/S7/reference/new_S3_class.html","id":"extending-an-s-class","dir":"Reference","previous_headings":"","what":"Extending an S3 class","title":"Declare an S3 class — new_S3_class","text":"Creating S7 class extends S3 class requires work. also need provide constructor S3 class follows S7 conventions. means first argument constructor .data, followed one argument attribute used class. can awkward base S3 classes usually heavily wrapped user convenience low level constructor available. example, factor class integer vector character vector levels, base R function takes integer vector values character vector levels, verifies consistent, creates factor object. may optionally want also provide validator function ensure validate() confirms validity S7 classes build class. Unlike S7 validator, responsible validating types attributes. following code shows might wrap base Date class. Date numeric vector class Date can constructed .Date().","code":"S3_Date <- new_S3_class(\"Date\", function(.data = integer()) { .Date(.data) }, function(self) { if (!is.numeric(self)) { \"Underlying data must be numeric\" } } )"},{"path":"https://rconsortium.github.io/S7/reference/new_S3_class.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Declare an S3 class — new_S3_class","text":"","code":"# No checking, just used for dispatch Date <- 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()) #> [1] \"This is a date\""},{"path":"https://rconsortium.github.io/S7/reference/new_class.html","id":null,"dir":"Reference","previous_headings":"","what":"Define a new S7 class — new_class","title":"Define a new S7 class — new_class","text":"class specifies properties (data) objects possess. class, parent, determines method used object passed generic. Learn vignette(\"classes-objects\")","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_class.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Define a new S7 class — new_class","text":"","code":"new_class( name, parent = S7_object, package = topNamespaceName(parent.frame()), properties = list(), abstract = FALSE, constructor = NULL, validator = NULL ) new_object(.parent, ...)"},{"path":"https://rconsortium.github.io/S7/reference/new_class.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Define a new S7 class — new_class","text":"name name class, string. result calling new_class() always assigned variable name, .e. foo <- new_class(\"foo\"). parent parent class inherit behavior . three options: S7 class, like S7_object. S3 class wrapped new_S3_class(). base type, like class_logical, class_integer, etc. package Package name. automatically resolved class defined package, NULL otherwise. Note, class intended external use, constructor exported. Learn vignette(\"packages\"). properties named list specifying properties (data) belong instance class. element list can either type specification (processed as_class()) full property specification created new_property(). abstract abstract class? abstract class can instantiated. constructor constructor function. cases, can rely default constructor, generate function one argument property. custom constructor call new_object() create S7 object. first argument, .data, instance parent class (used). subsequent arguments used set properties. validator function taking single argument, self, object validate. job validator determine whether object valid, .e. current property values form allowed combination. types properties always automatically validated job validator verify values individual properties ok (.e. maybe property length 1, always positive), combination values multiple properties ok. called construction whenever property set. validator return NULL object valid. , return character vector element describes single problem, using @prop_name describe problem lies. See validate() details, examples, temporarily suppress validation needed. .parent, ... Parent object named properties used construct object.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_class.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Define a new S7 class — new_class","text":"object constructor, function can used create objects given class.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_class.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Define a new S7 class — new_class","text":"","code":"# Create an class that represents a range using a numeric start and end range <- new_class(\"range\", properties = list( start = class_numeric, end = class_numeric ) ) r <- range(start = 10, end = 20) r #> #> @ start: num 10 #> @ end : num 20 # get and set properties with @ r@start #> [1] 10 r@end <- 40 r@end #> [1] 40 # S7 automatically ensures that properties are of the declared types: try(range(start = \"hello\", end = 20)) #> Error : object properties are invalid: #> - @start must be or , not # But we might also want to use a validator to ensure that start and end # are length 1, and that start is < end range <- 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)) #> Error : object is invalid: #> - @start must be a single number try(range(start = 20, end = 10)) #> Error : object is invalid: #> - @end must be great than or equal to @start r <- range(start = 10, end = 20) try(r@start <- 25) #> Error : object is invalid: #> - @end must be great than or equal to @start"},{"path":"https://rconsortium.github.io/S7/reference/new_external_generic.html","id":null,"dir":"Reference","previous_headings":"","what":"Generics in other packages — new_external_generic","title":"Generics in other packages — new_external_generic","text":"need explicit external generic want provide methods generic (S3, S4, S7) defined another package, want take hard dependency package. easiest way provide methods generics packages import generic NAMESPACE. , however, creates hard dependency, sometimes want soft dependency, registering method package already installed. new_external_generic() allows provide minimal needed information generic methods can registered run time, needed, using methods_register(). Note tests, need explicitly call generic external package pkg::generic().","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_external_generic.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Generics in other packages — new_external_generic","text":"","code":"new_external_generic(package, name, dispatch_args, version = NULL)"},{"path":"https://rconsortium.github.io/S7/reference/new_external_generic.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Generics in other packages — new_external_generic","text":"package Package generic defined . name Name generic, string. dispatch_args Character vector giving arguments used dispatch. version optional version package must meet method registered.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_external_generic.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Generics in other packages — new_external_generic","text":"S7 external generic, .e. list class S7_external_generic.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_external_generic.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Generics in other packages — new_external_generic","text":"","code":"my_class <- new_class(\"my_class\") your_generic <- new_external_generic(\"stats\", \"median\", \"x\") method(your_generic, my_class) <- function(x) \"Hi!\""},{"path":"https://rconsortium.github.io/S7/reference/new_generic.html","id":null,"dir":"Reference","previous_headings":"","what":"Define a new generic — new_generic","title":"Define a new generic — new_generic","text":"generic function uses different implementations (methods) depending class one arguments (signature). Create new generic new_generic() use method<- add methods . Method dispatch performed S7_dispatch(), must always included body generic, cases new_generic() generate . Learn vignette(\"generics-methods\")","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_generic.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Define a new generic — new_generic","text":"","code":"new_generic(name, dispatch_args, fun = NULL) S7_dispatch()"},{"path":"https://rconsortium.github.io/S7/reference/new_generic.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Define a new generic — new_generic","text":"name name generic. object assign . dispatch_args character vector giving names one arguments used find method. fun optional specification generic, must call S7_dispatch() dispatch methods. usually generated automatically dispatch_args, may want supply want add additional required arguments, omit ..., perform standardised computation generic. dispatch_args must first arguments fun, , present, ... must immediately follow .","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_generic.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Define a new generic — new_generic","text":"S7 generic, .e. function class S7_generic.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_generic.html","id":"dispatch-arguments","dir":"Reference","previous_headings":"","what":"Dispatch arguments","title":"Define a new generic — new_generic","text":"arguments used pick method called dispatch arguments. cases, one argument, case generic said use single dispatch. consists one argument, said use multiple dispatch. two restrictions dispatch arguments: must first arguments generic generic uses ..., must occur immediately dispatch arguments.","code":""},{"path":[]},{"path":"https://rconsortium.github.io/S7/reference/new_generic.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Define a new generic — new_generic","text":"","code":"# A simple generic with methods for some base types and S3 classes type_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) #> [1] \"A data frame\" type_of(letters) #> [1] \"A character vector\" type_of(mean) #> [1] \"A function\" # 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\") } #> Warning: mean2() doesn't have argument `na.rm`"},{"path":"https://rconsortium.github.io/S7/reference/new_property.html","id":null,"dir":"Reference","previous_headings":"","what":"Define a new property — new_property","title":"Define a new property — new_property","text":"property defines named component object. Properties typically used store (meta) data object, often limited data specific class. specifying getter /setter, can make property \"dynamic\" computed accessed non-standard behaviour modified. Dynamic properties included argument default class constructor. See \"Properties: Common Patterns\" section vignette(\"class-objects\") examples.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_property.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Define a new property — new_property","text":"","code":"new_property( class = class_any, getter = NULL, setter = NULL, validator = NULL, default = NULL, name = NULL )"},{"path":"https://rconsortium.github.io/S7/reference/new_property.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Define a new property — new_property","text":"class Class property must instance . See as_class() details. getter optional function used get value. function take self sole argument return value. supply getter, responsible ensuring returns object correct class; validated automatically. property getter setter, read . setter optional function used set value. function take self value return modified object. validator function taking single argument, value, value validate. job validator determine whether property value valid. return NULL object valid, valid, single string describing problem. message include name property automatically appended beginning message. validator called class verified, code can assume value known type. default object created property supplied, default ? NULL, defaults \"empty\" instance class. can also quoted call, becomes standard function promise default constructor, evaluated time object constructed. name Property name, primarily used error messages. Generally need set , convenient supply element name defining list properties. name list-name supplied, list-name used.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_property.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Define a new property — new_property","text":"S7 property, .e. list class S7_property.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_property.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Define a new property — new_property","text":"","code":"# Simple properties store data inside an object pizza <- new_class(\"pizza\", properties = list( slices = new_property(class_numeric, default = 10) )) my_pizza <- pizza(slices = 6) my_pizza@slices #> [1] 6 my_pizza@slices <- 5 my_pizza@slices #> [1] 5 your_pizza <- pizza() your_pizza@slices #> [1] 10 # Dynamic properties can compute on demand clock <- new_class(\"clock\", properties = list( now = new_property(getter = function(self) Sys.time()) )) my_clock <- clock() my_clock@now; Sys.sleep(1) #> [1] \"2024-11-04 17:22:27 UTC\" my_clock@now #> [1] \"2024-11-04 17:22:28 UTC\" # This property is read only, because there is a 'getter' but not a 'setter' try(my_clock@now <- 10) #> Error : Can't set read-only property @now # Because the property is dynamic, it is not included as an # argument to the default constructor try(clock(now = 10)) #> Error in clock(now = 10) : unused argument (now = 10) args(clock) #> function () #> NULL"},{"path":"https://rconsortium.github.io/S7/reference/new_union.html","id":null,"dir":"Reference","previous_headings":"","what":"Define a class union — new_union","title":"Define a class union — new_union","text":"class union represents list possible classes. can create new_union(, b, c) | b | c. Unions can used two places: allow property one set classes, new_property(class_integer | Range). default default value property constructor first object union. means want create \"optional\" property (.e. one can NULL specified type), need write (e.g.) NULL | class_integer. convenient short-hand define methods multiple classes. method(foo, X | Y) <- f short-hand method(foo, X) <- f; method(foo, Y) <- foo S7 includes built-unions \"numeric\" (integer double vectors), \"atomic\" (logical, numeric, complex, character, raw vectors) \"vector\" (atomic vectors, lists, expressions).","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_union.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Define a class union — new_union","text":"","code":"new_union(...)"},{"path":"https://rconsortium.github.io/S7/reference/new_union.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Define a class union — new_union","text":"... classes include union. See as_class() details.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_union.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Define a class union — new_union","text":"S7 union, .e. list class S7_union.","code":""},{"path":"https://rconsortium.github.io/S7/reference/new_union.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Define a class union — new_union","text":"","code":"logical_or_character <- new_union(class_logical, class_character) logical_or_character #> : or # or with shortcut syntax logical_or_character <- class_logical | class_character Foo <- new_class(\"Foo\", properties = list(x = logical_or_character)) Foo(x = TRUE) #> #> @ x: logi TRUE Foo(x = letters[1:5]) #> #> @ x: chr [1:5] \"a\" \"b\" \"c\" \"d\" \"e\" try(Foo(1:3)) #> Error : object properties are invalid: #> - @x must be or , not bar <- new_generic(\"bar\", \"x\") # Use built-in union method(bar, class_atomic) <- function(x) \"Hi!\" bar #> bar(x, ...) with 6 methods: #> 1: method(bar, class_integer) #> 2: method(bar, class_complex) #> 3: method(bar, class_double) #> 4: method(bar, class_character) #> 5: method(bar, class_logical) #> 6: method(bar, class_raw) bar(TRUE) #> [1] \"Hi!\" bar(letters) #> [1] \"Hi!\" try(bar(NULL)) #> Error : Can't find method for `bar()`."},{"path":"https://rconsortium.github.io/S7/reference/prop.html","id":null,"dir":"Reference","previous_headings":"","what":"Get/set a property — prop","title":"Get/set a property — prop","text":"prop(x, \"name\") / prop@name get value property, erroring property exist. prop(x, \"name\") <- value / prop@name <- value set value property.","code":""},{"path":"https://rconsortium.github.io/S7/reference/prop.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Get/set a property — prop","text":"","code":"prop(object, name) prop(object, name, check = TRUE) <- value object@name"},{"path":"https://rconsortium.github.io/S7/reference/prop.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Get/set a property — prop","text":"object object S7 class name name parameter character. Partial matching performed. check TRUE, check value correct type run validate() object returning. value new value property. object automatically checked validity replacement done.","code":""},{"path":"https://rconsortium.github.io/S7/reference/prop.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Get/set a property — prop","text":"prop() @ return value property. prop<-() @<- called side-effects return modified object, invisibly.","code":""},{"path":"https://rconsortium.github.io/S7/reference/prop.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Get/set a property — prop","text":"","code":"horse <- new_class(\"horse\", properties = list( name = class_character, colour = class_character, height = class_numeric )) lexington <- horse(colour = \"bay\", height = 15, name = \"Lex\") lexington@colour #> [1] \"bay\" prop(lexington, \"colour\") #> [1] \"bay\" lexington@height <- 14 prop(lexington, \"height\") <- 15"},{"path":"https://rconsortium.github.io/S7/reference/prop_names.html","id":null,"dir":"Reference","previous_headings":"","what":"Property introspection — prop_names","title":"Property introspection — prop_names","text":"prop_names(x) returns names properties prop_exists(x, \"prop\") returns TRUE iif x property prop.","code":""},{"path":"https://rconsortium.github.io/S7/reference/prop_names.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Property introspection — prop_names","text":"","code":"prop_names(object) prop_exists(object, name)"},{"path":"https://rconsortium.github.io/S7/reference/prop_names.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Property introspection — prop_names","text":"object object S7 class name name parameter character. Partial matching performed.","code":""},{"path":"https://rconsortium.github.io/S7/reference/prop_names.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Property introspection — prop_names","text":"prop_names() returns character vector; prop_exists() returns single TRUE FALSE.","code":""},{"path":"https://rconsortium.github.io/S7/reference/prop_names.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Property introspection — prop_names","text":"","code":"foo <- new_class(\"foo\", properties = list(a = class_character, b = class_integer)) f <- foo() prop_names(f) #> [1] \"a\" \"b\" prop_exists(f, \"a\") #> [1] TRUE prop_exists(f, \"c\") #> [1] FALSE"},{"path":"https://rconsortium.github.io/S7/reference/props.html","id":null,"dir":"Reference","previous_headings":"","what":"Get/set multiple properties — props","title":"Get/set multiple properties — props","text":"props(x) returns properties. props(x) <- list(name1 = val1, name2 = val2) modifies existing object setting multiple properties simultaneously. set_props(x, name1 = val1, name2 = val2) creates copy existing object new values specified properties.","code":""},{"path":"https://rconsortium.github.io/S7/reference/props.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Get/set multiple properties — props","text":"","code":"props(object, names = prop_names(object)) props(object) <- value set_props(object, ...)"},{"path":"https://rconsortium.github.io/S7/reference/props.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Get/set multiple properties — props","text":"object object S7 class names character vector property names retrieve. Default properties. value named list values. object checked validity replacements performed. ... Name-value pairs given property modify new value.","code":""},{"path":"https://rconsortium.github.io/S7/reference/props.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Get/set multiple properties — props","text":"named list property values.","code":""},{"path":"https://rconsortium.github.io/S7/reference/props.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Get/set multiple properties — props","text":"","code":"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) #> $name #> [1] \"Lex\" #> #> $colour #> [1] \"bay\" #> #> $height #> [1] 15 #> props(lexington) <- list(height = 14, name = \"Lexington\") lexington #> #> @ name : chr \"Lexington\" #> @ colour: chr \"bay\" #> @ height: num 14"},{"path":"https://rconsortium.github.io/S7/reference/super.html","id":null,"dir":"Reference","previous_headings":"","what":"Force method dispatch to use a superclass — super","title":"Force method dispatch to use a superclass — super","text":"super(, ) causes dispatch next generic use method superclass instead actual class . needed want implement method terms implementation superclass.","code":""},{"path":"https://rconsortium.github.io/S7/reference/super.html","id":"s-amp-s-","dir":"Reference","previous_headings":"","what":"S3 & S4","title":"Force method dispatch to use a superclass — super","text":"super() performs similar role NextMethod() S3 methods::callNextMethod() S4, much explicit: super class super() use known write super() (.e. statically) opposed generic called (.e. dynamically). arguments generic explicit; automatically passed along. makes super() verbose, substantially easier understand reason .","code":""},{"path":"https://rconsortium.github.io/S7/reference/super.html","id":"super-in-s-generics","dir":"Reference","previous_headings":"","what":"super() in S3 generics","title":"Force method dispatch to use a superclass — super","text":"Note use super() methods S3 generic. example, imagine made subclass \"integer\": Now go write custom print method: work print() S7 generic understand interpret special object super() produces. resolve problem NextMethod() (S7 implemented top S3), instead recommend using S7_data() extract underlying base object:","code":"myint <- new_class(\"myint\", parent = class_integer, package = NULL) method(print, myint) <- function(x, ...) { cat(\"\") print(super(x, to = class_integer)) } myint(10L) #> super(, ) method(print, myint) <- function(x, ...) { cat(\"\") print(S7_data(x)) } myint(10L) #> [1] 10"},{"path":"https://rconsortium.github.io/S7/reference/super.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Force method dispatch to use a superclass — super","text":"","code":"super(from, to)"},{"path":"https://rconsortium.github.io/S7/reference/super.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Force method dispatch to use a superclass — super","text":"S7 object cast. S7 class specification, passed as_class(). Must superclass object.","code":""},{"path":"https://rconsortium.github.io/S7/reference/super.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Force method dispatch to use a superclass — super","text":"S7_super object always passed immediately generic. special behavior.","code":""},{"path":"https://rconsortium.github.io/S7/reference/super.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Force method dispatch to use a superclass — super","text":"","code":"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 write method(total, foo2) <- function(x) x@x + x@y + x@z #> Overwriting method total() # but then we'd need to remember to update it if the implementation # for total() 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@z #> Overwriting method total() total(foo2(1, 2, 3)) #> [1] 6 # To see the difference between convert() and super() we need a # method that calls another generic bar1 <- new_generic(\"bar1\", \"x\") method(bar1, foo1) <- function(x) 1 method(bar1, foo2) <- function(x) 2 bar2 <- 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) #> [1] 2 2 # convert() affects every generic: bar2(convert(obj, to = foo1)) #> [1] 1 1 # super() only affects the _next_ call to a generic: bar2(super(obj, to = foo1)) #> [1] 1 2"},{"path":"https://rconsortium.github.io/S7/reference/validate.html","id":null,"dir":"Reference","previous_headings":"","what":"Validate an S7 object — validate","title":"Validate an S7 object — validate","text":"validate() ensures S7 object valid calling validator provided new_class(). done automatically constructing new objects modifying properties. valid_eventually() disables validation, modifies object, revalidates. useful sequence operations otherwise lead object temporarily invalid, repeated property modification causes performance bottleneck validator relatively expensive. valid_implicitly() validate object end. used rarely, performance critical code certain sequence operations produce invalid object.","code":""},{"path":"https://rconsortium.github.io/S7/reference/validate.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Validate an S7 object — validate","text":"","code":"validate(object, recursive = TRUE, properties = TRUE) valid_eventually(object, fun) valid_implicitly(object, fun)"},{"path":"https://rconsortium.github.io/S7/reference/validate.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Validate an S7 object — validate","text":"object S7 object recursive TRUE, calls validator parent classes recursively. properties TRUE, default, checks property types executing validator. fun function call object validation.","code":""},{"path":"https://rconsortium.github.io/S7/reference/validate.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Validate an S7 object — validate","text":"Either object invisibly valid, otherwise error.","code":""},{"path":"https://rconsortium.github.io/S7/reference/validate.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Validate an S7 object — validate","text":"","code":"# A range class might validate that the start is less than the end Range <- 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)) #> Error : object is invalid: #> - start must be smaller than end # And you can't create an invalid object with @<- r <- Range(1, 2) try(r@end <- 1) #> Error : object is invalid: #> - start must be smaller than end # 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 state try(rightwards(r, 10)) #> Error : object is invalid: #> - start must be smaller than end # 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) #> #> @ start: num 11 #> @ end : num 12 # Alternatively, you can set multiple properties at once using props<-, # which validates once at the end rightwards <- function(r, x) { props(r) <- list(start = r@start + x, end = r@end + x) r } rightwards(r, 20) #> #> @ start: num 21 #> @ end : num 22"},{"path":"https://rconsortium.github.io/S7/news/index.html","id":"s7-development-version","dir":"Changelog","previous_headings":"","what":"S7 (development version)","title":"S7 (development version)","text":"new_class() now automatically infers package name called within R package (#459). Improved error message custom validators return invalid values (#454, #457). New nameOfClass() method exported S7 base classes, enable usage like inherits(\"foo\", S7::class_character) (#432, #458) Added support base/S3 classes (#434): class_POSIXlt, class_POSIXt, class_formula, class_call, class_language, class_name Fixed S3 methods registration across packages (#422). convert() now provides default method transform parent class instance subclass, enabling class construction prototype (#444). default object constructor returned new_class() updated. now accepts lazy (promise) property defaults includes dynamic properties setter constructor. Additionally, custom property setters now consistently invoked default constructor. ’re using S7 R package, ’ll need re-document ensure documentation matches updated usage (#438, #445). Fixed issue custom property getter() infinitely recurse accessing (reported #403, fixed #406). Property setting (via prop<- @<-) rewritten C performance (#396). Fixed regression validate() called custom property setter invoked (reported #393, fixed #396). method found, error now class S7_error_method_not_found. Ops generic now falls back base Ops behaviour one arguments S7 object (#320). means get somewhat inconsistent base behaviour, means S7 doesn’t introduce new axis inconsistency. new_class(), properties can either named naming element list supplying name argument new_property() (#371). super() now works Ops methods (#357). method() now generates informative message dispatch fails (#387). S7 provides new automatic backward compatibility mechanism provide version @ works R version 4.3 (#326). Can create multimethods dispatch NULL. prop() optimized rewritten C (#395).","code":""},{"path":"https://rconsortium.github.io/S7/news/index.html","id":"s7-011","dir":"Changelog","previous_headings":"","what":"S7 0.1.1","title":"S7 0.1.1","text":"CRAN release: 2023-09-17 Classes get informative print method (#346). Correctly register S3 methods S7 objects package (#333). External methods now registered using attribute S3 methods table rather element environment. prevents warning generated “code/documentation mismatches” check R CMD check (#342). class_missing class_any can now unioned | (#337). new_object() longer accepts NULL .parent. new_object() now correctly runs validator abstract parent classes (#329). new_object() works better custom property setters modify properties. new_property() gains validator argument allows specify per-property validator (#275). new_property() clarifies ’s user’s responsibility return correct class; automatically validated. Properties custom setter now validated setter run validated object constructed call validate(), just modify construction. S7_inherits() now accepts class = NULL test object sort S7 object (#347).","code":""},{"path":"https://rconsortium.github.io/S7/news/index.html","id":"s7-010","dir":"Changelog","previous_headings":"","what":"S7 0.1.0","title":"S7 0.1.0","text":"CRAN release: 2023-08-24","code":""},{"path":"https://rconsortium.github.io/S7/news/index.html","id":"may-july-0-1-0","dir":"Changelog","previous_headings":"","what":"May-July 2023","title":"S7 0.1.0","text":"new_external_generic() needed want soft dependency another package. methods_register() now also registers S3 S4 methods (#306).","code":""},{"path":"https://rconsortium.github.io/S7/news/index.html","id":"jan-may-0-1-0","dir":"Changelog","previous_headings":"","what":"Jan-May 2023","title":"S7 0.1.0","text":"Subclasses abstract class can readonly properties (#269). construction, validation now performed element class hierarchy (#248). Implemented better filtering strategy S4 class hierarchy can now correctly dispatch virtual classes (#252). New set_props() make modified copy object (#229). R CMD check now passes R 3.5 greater (tidyverse compatibility). Dispatching evaluated argument longer causes crash (#254). Improve method dispatch failure message (#231). Can use | create unions S7 classes (#224). Can longer subclass environment via class_environment need think consequences behaviour fully (#253).","code":""},{"path":"https://rconsortium.github.io/S7/news/index.html","id":"rest-of-0-1-0","dir":"Changelog","previous_headings":"","what":"Rest of 2022","title":"S7 0.1.0","text":"Add [.S7_object, [<-.S7_object, [[.S7_object, [[<-.S7_object methods avoid “object type ‘S4’ subsettable” error (@jamieRowen, #236). Combining S7 classes c() now gives error (#230) Base classes now show class_x instead \"x\" method print (#232)","code":""},{"path":"https://rconsortium.github.io/S7/news/index.html","id":"mar-0-1-0","dir":"Changelog","previous_headings":"","what":"Mar 2022","title":"S7 0.1.0","text":"Exported class_factor, class_Date, class_POSIXct, class_data.frame. New S7_inherits() check_is_S7() (#193) new_class() can create abstract classes (#199). method_call() now S7_dispatch() (#200). Can now register methods double-dispatch base Ops (currently works classes S7, first argument S7 second doesn’t method Ops generic) (#128). built-wrappers around base types use class_. can longer refer base type string constructor function (#170). convert() allows convert object another class (#136). super() replaces next_method() (#110).","code":""},{"path":"https://rconsortium.github.io/S7/news/index.html","id":"feb-0-1-0","dir":"Changelog","previous_headings":"","what":"Feb 2022","title":"S7 0.1.0","text":"class_any class_missing make possible dispatch absent arguments arguments class (#67). New method_explain() explain dispatch (#194). Minor property improvements: use syntax naming short-hand full property specifications; input type automatically validated custom setters. property getter setter read-(#168). creating object, unspecified properties initialized default value (#67). DISCUSS: achieve , constructor arguments default class_missing. Add $.S7_object $<-.S7_object methods avoid “object type ‘S4’ subsettable” error (#204). Dispatch now disambiguates S4 S3/S7, , optionally, S7 classes different packages (#48, #163). new_generic() now requires dispatch_args (#180). means new_generic() typically called without names. Either new_generic(\"foo\", \"x\") “standard” generic, new_generic(\"foo\", \"x\", function(x, y) call_method()) non-standard method. new_external_generic() now requires dispatch_args can eagerly check signature. Revamp website. README now shows brief example info vignette(\"S7\"). Initial design docs minutes now articles appear website.","code":""},{"path":"https://rconsortium.github.io/S7/news/index.html","id":"jan-0-1-0","dir":"Changelog","previous_headings":"","what":"Jan 2022","title":"S7 0.1.0","text":"New props<- setting multiple properties simultaneously validating afterwards (#149). Validation now happens recursively, validates types validating object (#149) Classes (base types, S3, S4, S7) handled consistently wherever used. Strings now refer base types. New explicit new_S3_class() referring S3 classes (#134). S4 unions converted S7 unions (#150). Base numeric, atomic, vector “types” now represented class unions (#147). Different evaluation mechanism method dispatch, greater restrictions dispatch args (#141) x@.data -> S7_data(); probably replaced casting. generic, signature -> dispatch_args. Polished str() print() methods new_class() properties 3rd argument (instead constructor).","code":""}]
+[{"path":"https://rconsortium.github.io/S7/LICENSE.html","id":null,"dir":"","previous_headings":"","what":"MIT License","title":"MIT License","text":"Copyright (c) 2021 S7 authors Permission hereby granted, free charge, person obtaining copy software associated documentation files (“Software”), deal Software without restriction, including without limitation rights use, copy, modify, merge, publish, distribute, sublicense, /sell copies Software, permit persons Software furnished , subject following conditions: copyright notice permission notice shall included copies substantial portions Software. SOFTWARE PROVIDED “”, WITHOUT WARRANTY KIND, EXPRESS IMPLIED, INCLUDING LIMITED WARRANTIES MERCHANTABILITY, FITNESS PARTICULAR PURPOSE NONINFRINGEMENT. EVENT SHALL AUTHORS COPYRIGHT HOLDERS LIABLE CLAIM, DAMAGES LIABILITY, WHETHER ACTION CONTRACT, TORT OTHERWISE, ARISING , CONNECTION SOFTWARE USE DEALINGS SOFTWARE.","code":""},{"path":"https://rconsortium.github.io/S7/articles/S7.html","id":"classes-and-objects","dir":"Articles","previous_headings":"","what":"Classes and objects","title":"S7 basics","text":"S7 classes formal definition create new_class(). two arguments ’ll use almost every class: name class, supplied first argument. class properties, data associated instance class. easiest way define properties supply named list values define valid types property. following code defines simple dog class two properties: character name numeric age. S7 provides number built-definitions allow refer existing base types S7 classes. can recognize definitions start class_. Note ’ve assigned return value new_class() object name class. important! object represents class use construct instances class: S7 object, can get set properties using @: S7 automatically validates type property using type supplied new_class(): Given object, can retrieves class S7_class(): S7 objects also S3 class(). used compatibility existing S3 generics can learn vignette(\"compatibility\"). want learn details S7 classes objects, including validation methods details properties, please see vignette(\"classes-objects\").","code":"Dog <- new_class(\"Dog\", properties = list( name = class_character, age = class_numeric )) Dog #> class #> @ parent : #> @ constructor: function(name, age) {...} #> @ validator : #> @ properties : #> $ name: #> $ age : or lola <- Dog(name = \"Lola\", age = 11) lola #> #> @ name: chr \"Lola\" #> @ age : num 11 lola@age <- 12 lola@age #> [1] 12 lola@age <- \"twelve\" #> Error: @age must be or , not S7_class(lola) #> class #> @ parent : #> @ constructor: function(name, age) {...} #> @ validator : #> @ properties : #> $ name: #> $ age : or class(lola) #> [1] \"Dog\" \"S7_object\""},{"path":"https://rconsortium.github.io/S7/articles/S7.html","id":"generics-and-methods","dir":"Articles","previous_headings":"","what":"Generics and methods","title":"S7 basics","text":"S7, like S3 S4, built around idea generic functions, generics short. generic defines interface, uses different implementation depending class one arguments. implementation specific class called method, generic finds appropriate method performing method dispatch. Use new_generic() create S7 generic. simplest form, needs two arguments: name generic (used error messages) name argument used method dispatch: Like new_class(), always assign result new_generic() variable name first argument. generic, can register methods specific classes method(generic, class) <- implementation. method registered, generic use appropriate: Let’s define another class, one cats, define another method speak(): get error call generic class doesn’t method:","code":"speak <- new_generic(\"speak\", \"x\") method(speak, Dog) <- function(x) { \"Woof\" } speak(lola) #> [1] \"Woof\" Cat <- new_class(\"Cat\", properties = list( name = class_character, age = class_double )) method(speak, Cat) <- function(x) { \"Meow\" } fluffy <- Cat(name = \"Fluffy\", age = 5) speak(fluffy) #> [1] \"Meow\" speak(1) #> Error: Can't find method for `speak()`."},{"path":"https://rconsortium.github.io/S7/articles/S7.html","id":"method-dispatch-and-inheritance","dir":"Articles","previous_headings":"","what":"Method dispatch and inheritance","title":"S7 basics","text":"cat dog classes share properties, use common parent class extract duplicated specification. first define parent class: use parent argument new_class: created new classes, need recreate existing lola fluffy objects: Method dispatch takes advantage hierarchy parent classes: method defined class, try method parent class, finds method gives error. inheritance powerful mechanism sharing code across classes. can define fallback method S7 object registering method S7_object: Printing generic show methods currently defined: can use method() retrieve implementation one methods: Learn method dispatch vignette(\"generics-methods\").","code":"Pet <- new_class(\"Pet\", properties = list( name = class_character, age = class_numeric ) ) Cat <- new_class(\"Cat\", parent = Pet) Dog <- new_class(\"Dog\", parent = Pet) Cat #> class #> @ parent : #> @ constructor: function(name, age) {...} #> @ validator : #> @ properties : #> $ name: #> $ age : or Dog #> class #> @ parent : #> @ constructor: function(name, age) {...} #> @ validator : #> @ properties : #> $ name: #> $ age : or lola <- Dog(name = \"Lola\", age = 11) fluffy <- Cat(name = \"Fluffy\", age = 5) describe <- new_generic(\"describe\", \"x\") method(describe, Pet) <- function(x) { paste0(x@name, \" is \", x@age, \" years old\") } describe(lola) #> [1] \"Lola is 11 years old\" describe(fluffy) #> [1] \"Fluffy is 5 years old\" method(describe, Dog) <- function(x) { paste0(x@name, \" is a \", x@age, \" year old dog\") } describe(lola) #> [1] \"Lola is a 11 year old dog\" describe(fluffy) #> [1] \"Fluffy is 5 years old\" method(describe, S7_object) <- function(x) { \"An S7 object\" } Cocktail <- new_class(\"Cocktail\", properties = list( ingredients = class_character ) ) martini <- Cocktail(ingredients = c(\"gin\", \"vermouth\")) describe(martini) #> [1] \"An S7 object\" describe #> describe(x, ...) with 3 methods: #> 1: method(describe, Dog) #> 2: method(describe, S7_object) #> 3: method(describe, Pet) method(describe, Pet) #> method(describe, Pet) #> function (x) #> { #> paste0(x@name, \" is \", x@age, \" years old\") #> } #> "},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"validation","dir":"Articles","previous_headings":"","what":"Validation","title":"Classes and objects","text":"S7 classes can optional validator checks values properties OK. validator function takes object (called self) returns NULL valid returns character vector listing problems.","code":""},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"basics","dir":"Articles","previous_headings":"Validation","what":"Basics","title":"Classes and objects","text":"following example create Range class enforces @start @end single numbers, @start less @end: can typically write validator series -else statements, note order statements important. example, code , can’t check self@end < self@start ’ve checked @start @end length 1. ’ll discuss shortly, can also perform validation per-property basis, generally class validators reserved interactions properties.","code":"Range <- new_class(\"Range\", properties = list( start = class_double, end = class_double ), validator = function(self) { if (length(self@start) != 1) { \"@start must be length 1\" } else if (length(self@end) != 1) { \"@end must be length 1\" } else if (self@end < self@start) { sprintf( \"@end (%i) must be greater than or equal to @start (%i)\", self@end, self@start ) } } )"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"when-is-validation-performed","dir":"Articles","previous_headings":"Validation","what":"When is validation performed?","title":"Classes and objects","text":"Objects validated automatically constructed property modified: can also manually validate() object use low-level R function bypass usual checks balances @:","code":"x <- Range(1, 2:3) #> Error: object properties are invalid: #> - @end must be , not x <- Range(10, 1) #> Error: object is invalid: #> - @end (1) must be greater than or equal to @start (10) x <- Range(1, 10) x@start <- 20 #> Error: object is invalid: #> - @end (10) must be greater than or equal to @start (20) x <- Range(1, 2) attr(x, \"start\") <- 3 validate(x) #> Error: object is invalid: #> - @end (2) must be greater than or equal to @start (3)"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"avoiding-validation","dir":"Articles","previous_headings":"Validation","what":"Avoiding validation","title":"Classes and objects","text":"Imagine wanted write function shift property left right: ’s problem shift larger @end - @start: end result shift() valid, intermediate state . easiest way resolve problem set properties : object still validated, ’s validated , properties modified.","code":"shift <- function(x, shift) { x@start <- x@start + shift x@end <- x@end + shift x } shift(Range(1, 10), 1) #> #> @ start: num 2 #> @ end : num 11 shift(Range(1, 10), 10) #> Error: object is invalid: #> - @end (10) must be greater than or equal to @start (11) shift <- function(x, shift) { props(x) <- list( start = x@start + shift, end = x@end + shift ) x } shift(Range(1, 10), 10) #> #> @ start: num 11 #> @ end : num 20"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"properties","dir":"Articles","previous_headings":"","what":"Properties","title":"Classes and objects","text":"far ’ve focused simplest form property specification use named list supply desired type property. convenient shorthand call new_property(). example, property definition range shorthand : Calling new_property() explicitly allows control aspects property type. following sections show add validator, provide default value, compute property value demand, provide fully dynamic property.","code":"Range <- new_class(\"Range\", properties = list( start = new_property(class_double), end = new_property(class_double) ) )"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"validation-1","dir":"Articles","previous_headings":"Properties","what":"Validation","title":"Classes and objects","text":"can optionally provide validator property. example, instead validating length start end validator Range class, implement property level: Note property validators shouldn’t include name property validation messages S7 add automatically. makes possible use property definition multiple properties type, .","code":"prop_number <- new_property( class = class_double, validator = function(value) { if (length(value) != 1L) \"must be length 1\" } ) Range <- new_class(\"Range\", properties = list( start = prop_number, end = prop_number ), validator = function(self) { if (self@end < self@start) { sprintf( \"@end (%i) must be greater than or equal to @start (%i)\", self@end, self@start ) } } ) Range(start = c(1.5, 3.5)) #> Error: object properties are invalid: #> - @start must be length 1 #> - @end must be length 1 Range(end = c(1.5, 3.5)) #> Error: object properties are invalid: #> - @start must be length 1 #> - @end must be length 1"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"default-value","dir":"Articles","previous_headings":"Properties","what":"Default value","title":"Classes and objects","text":"defaults new_class() create class can constructed arguments: default values properties filled “empty” instances. can instead provide defaults using default argument: quoted call becomes standard function promise default constructor, evaluated time object constructed.","code":"Empty <- new_class(\"Empty\", properties = list( x = class_double, y = class_character, z = class_logical )) Empty() #> #> @ x: num(0) #> @ y: chr(0) #> @ z: logi(0) Empty <- new_class(\"Empty\", properties = list( x = new_property(class_numeric, default = 0), y = new_property(class_character, default = \"\"), z = new_property(class_logical, default = NA) ) ) Empty() #> #> @ x: num 0 #> @ y: chr \"\" #> @ z: logi NA Stopwatch <- new_class(\"Stopwatch\", properties = list( start_time = new_property( class = class_POSIXct, default = quote(Sys.time()) ), elapsed = new_property( getter = function(self) { difftime(Sys.time(), self@start_time, units = \"secs\") } ) )) args(Stopwatch) #> function (start_time = Sys.time()) #> NULL round(Stopwatch()@elapsed) #> Time difference of 0 secs round(Stopwatch(Sys.time() - 1)@elapsed) #> Time difference of 1 secs"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"computed-properties","dir":"Articles","previous_headings":"Properties","what":"Computed properties","title":"Classes and objects","text":"’s sometimes useful property computed demand. example, ’d convenient pretend range length, just distance @start @end. can dynamically compute value property defining getter: Computed properties read-:","code":"Range <- new_class(\"Range\", properties = list( start = class_double, end = class_double, length = new_property( getter = function(self) self@end - self@start, ) ) ) x <- Range(start = 1, end = 10) x #> #> @ start : num 1 #> @ end : num 10 #> @ length: num 9 x@length <- 20 #> Error: Can't set read-only property @length"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"dynamic-properties","dir":"Articles","previous_headings":"Properties","what":"Dynamic properties","title":"Classes and objects","text":"can make computed property fully dynamic can read written also supplying setter. setter function arguments self value returns modified object. example, extend previous example allow @length set, modifying @end vector:","code":"Range <- new_class(\"Range\", properties = list( start = class_double, end = class_double, length = new_property( class = class_double, getter = function(self) self@end - self@start, setter = function(self, value) { self@end <- self@start + value self } ) ) ) x <- Range(start = 1, end = 10) x #> #> @ start : num 1 #> @ end : num(0) #> @ length: num(0) x@length <- 5 x #> #> @ start : num 1 #> @ end : num 6 #> @ length: num 5"},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"common-patterns","dir":"Articles","previous_headings":"Properties","what":"Common Patterns","title":"Classes and objects","text":"getter, setter, default, validator can used implement many common patterns properties.","code":""},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"deprecated-properties","dir":"Articles","previous_headings":"Properties > Common Patterns","what":"Deprecated properties","title":"Classes and objects","text":"setter + getter can used deprecate property:","code":"Person <- new_class(\"Person\", properties = list( first_name = class_character, firstName = new_property( class_character, default = quote(first_name), getter = function(self) { warning(\"@firstName is deprecated; please use @first_name instead\", call. = FALSE) self@first_name }, setter = function(self, value) { if (identical(value, self@first_name)) { return(self) } warning(\"@firstName is deprecated; please use @first_name instead\", call. = FALSE) self@first_name <- value self } ) )) args(Person) #> function (first_name = character(0), firstName = first_name) #> NULL hadley <- Person(firstName = \"Hadley\") #> Warning: @firstName is deprecated; please use @first_name instead hadley <- Person(first_name = \"Hadley\") # no warning hadley@firstName #> Warning: @firstName is deprecated; please use @first_name instead #> [1] \"Hadley\" hadley@firstName <- \"John\" #> Warning: @firstName is deprecated; please use @first_name instead hadley@first_name # no warning #> [1] \"John\""},{"path":"https://rconsortium.github.io/S7/articles/classes-objects.html","id":"required-properties","dir":"Articles","previous_headings":"Properties > Common Patterns","what":"Required properties","title":"Classes and objects","text":"can make property required constructor either : relying validator error default value, setting property default quoted error call.","code":"Person <- new_class(\"Person\", properties = list( name = new_property( class_character, validator = function(value) { if (length(value) != 1 || is.na(value) || value == \"\") \"must be a non-empty string\" } ) )) try(Person()) #> Error : object properties are invalid: #> - @name must be a non-empty string try(Person(1)) # class_character$validator() is also checked. #> Error : object properties are invalid: #> - @name must be , not