From 02b99d49a217a7559669f0ea88d854fb6b32a3c4 Mon Sep 17 00:00:00 2001 From: Matt Heaphy Date: Sat, 9 Nov 2024 07:49:00 -0500 Subject: [PATCH 1/8] generics vignette typo fixes --- vignettes/generics-methods.Rmd | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vignettes/generics-methods.Rmd b/vignettes/generics-methods.Rmd index ef7b9778..5f4d2e35 100644 --- a/vignettes/generics-methods.Rmd +++ b/vignettes/generics-methods.Rmd @@ -114,7 +114,7 @@ simple_print(list(1, 2, "x"), diggits = 3) Occasional it's useful to create a generic without `…` because such functions have a useful property: if a call succeeds for one type of input, it will succeed for any type of input. To create such a generic, you'll need to use the third argument to `new_generic()`: an optional function that powers the generic. -This function has one key property: it must call `call_method()` to actually perform dispatch. +This function has one key property: it must call `S7_dispatch()` to actually perform dispatch. In general, this property is only needed for very low-level functions with precisely defined semantics. A good example of such a function is `length()`: @@ -126,7 +126,7 @@ length <- new_generic("length", "x", function(x) { ``` Omitting `…` from the generic signature is a strong restriction as it prevents methods from adding extra arguments. -For this reason, it's should only be used in special situations. +For this reason, it should only be used in special situations. ## Customizing generics @@ -137,7 +137,7 @@ display <- new_generic("display", "x") S7_data(display) ``` -The most important part of the body is `S7_dispatch()`; this function finds the method the matches the arguments used for dispatch and calls it with the arguments supplied to the generic. +The most important part of the body is `S7_dispatch()`; this function finds the method that matches the arguments used for dispatch and calls it with the arguments supplied to the generic. It can be useful to customize this body. The previous section showed one case when you might want to supply the body yourself: dropping `…` from the formals of the generic. @@ -147,7 +147,7 @@ There are three other useful cases: - To add optional arguments. - Perform some standard work. -A custom `fun` must always include a call to `call_method()`, which will usually be the last call. +A custom `fun` must always include a call to `S7_dispatch()`, which will usually be the last call. ### Add required arguments @@ -213,7 +213,7 @@ The only downside to performing error checking is that you constraint the interf ## `super()` -Sometimes it's useful to define a method for in terms of its superclass. +Sometimes it's useful to define a method for a class that relies on its superclass. A good example of this is computing the mean of a date --- since dates represent the number of days since 1970-01-01, computing the mean is just a matter of computing the mean of the underlying numeric vector and converting it back to a date. To demonstrate this idea, I'll first define a mean generic with a method for numbers: @@ -256,8 +256,8 @@ This explicitness makes the code easier to understand and will eventually enable ## Multiple dispatch -So far we have focused primarily on single dispatch, i.e. generics where `dispatch_on` is a single string. -It is also possible to supply a length 2 (or more!) vector `dispatch_on` to create a generic that performs multiple dispatch, i.e. it uses the classes of more than one object to find the appropriate method. +So far we have focused primarily on single dispatch, i.e. generics where `dispatch_args` is a single string. +It is also possible to supply a length 2 (or more!) vector `dispatch_args` to create a generic that performs multiple dispatch, i.e. it uses the classes of more than one object to find the appropriate method. Multiple dispatch is a feature primarily of S4, although S3 includes some limited special cases for arithmetic operators. Multiple dispatch is heavily used in S4; we don't expect it to be heavily used in S7, but it is occasionally useful. From 699917f24c73008619713d7c4b5e2fe6497fc057 Mon Sep 17 00:00:00 2001 From: Matt Heaphy Date: Sat, 9 Nov 2024 08:30:07 -0500 Subject: [PATCH 2/8] classes & objects vignette typo fixes --- vignettes/classes-objects.Rmd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vignettes/classes-objects.Rmd b/vignettes/classes-objects.Rmd index 29e1c0d1..9b4eccf4 100644 --- a/vignettes/classes-objects.Rmd +++ b/vignettes/classes-objects.Rmd @@ -24,7 +24,7 @@ library(S7) ## Validation S7 classes can have an optional **validator** that checks that the values of the properties are OK. -A validator is a function that takes the object (called `self`) and returns `NULL` if its valid or returns a character vector listing the problems. +A validator is a function that takes the object (called `self`) and returns `NULL` if it's valid or returns a character vector listing the problems. ### Basics @@ -168,7 +168,7 @@ This makes it possible to use the same property definition for multiple properti ### Default value -The defaults of `new_class()` create an class that can be constructed with no arguments: +The defaults of `new_class()` create a class that can be constructed with no arguments: ```{r} Empty <- new_class("Empty", @@ -225,7 +225,7 @@ Range <- new_class("Range", start = class_double, end = class_double, length = new_property( - getter = function(self) self@end - self@start, + getter = function(self) self@end - self@start ) ) ) @@ -398,7 +398,7 @@ Range <- new_class("Range", } ) -range(c(10, 5, 0, 2, 5, 7)) +Range(c(10, 5, 0, 2, 5, 7)) ``` A constructor must always end with a call to `new_object()`. From c880d80fcf38f55f5b8c1aa9d840eeaf3d0803b8 Mon Sep 17 00:00:00 2001 From: Matt Heaphy Date: Sat, 9 Nov 2024 08:40:56 -0500 Subject: [PATCH 3/8] subtle bug fix - upon initialization the setter is called, which will override @end with num(0). Afterwards getter is called which will result in num(0) for @length too. --- vignettes/classes-objects.Rmd | 1 + 1 file changed, 1 insertion(+) diff --git a/vignettes/classes-objects.Rmd b/vignettes/classes-objects.Rmd index 9b4eccf4..4f41b3fb 100644 --- a/vignettes/classes-objects.Rmd +++ b/vignettes/classes-objects.Rmd @@ -257,6 +257,7 @@ Range <- new_class("Range", class = class_double, getter = function(self) self@end - self@start, setter = function(self, value) { + if (length(value) == 0) return(self) self@end <- self@start + value self } From 8a226858f9dbbfe0e2384bd5b2307e15180ea44e Mon Sep 17 00:00:00 2001 From: Matt Heaphy Date: Sat, 9 Nov 2024 09:22:12 -0500 Subject: [PATCH 4/8] S3/4 compatibility vignette - fixes to avoid overwriting rle() --- vignettes/compatibility.Rmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vignettes/compatibility.Rmd b/vignettes/compatibility.Rmd index cd4bc57d..47ebed6a 100644 --- a/vignettes/compatibility.Rmd +++ b/vignettes/compatibility.Rmd @@ -109,7 +109,7 @@ rle(1:10) Alternatively you could convert it to the most natural representation using S7: ```{r} -rle <- new_class("rle", properties = list( +new_rle <- new_class("rle", properties = list( lengths = class_integer, values = class_atomic )) @@ -118,7 +118,7 @@ rle <- new_class("rle", properties = list( To allow existing methods to work you'll need to override `$` to access properties instead of list elements: ```{r} -method(`$`, rle) <- prop +method(`$`, new_rle) <- prop rle(1:10) ``` From c68a17a19036cfd99eaf35659bca4cecfbcde7c5 Mon Sep 17 00:00:00 2001 From: Matt Heaphy <32379086+mattheaphy@users.noreply.github.com> Date: Wed, 13 Nov 2024 06:49:20 -0500 Subject: [PATCH 5/8] vignettes/generics-methods.Rmd - clearer explanation Co-authored-by: Hadley Wickham --- vignettes/generics-methods.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/generics-methods.Rmd b/vignettes/generics-methods.Rmd index 5f4d2e35..b5192379 100644 --- a/vignettes/generics-methods.Rmd +++ b/vignettes/generics-methods.Rmd @@ -213,7 +213,7 @@ The only downside to performing error checking is that you constraint the interf ## `super()` -Sometimes it's useful to define a method for a class that relies on its superclass. +Sometimes it's useful to define a method for a class that relies on the implementation for its superclass. A good example of this is computing the mean of a date --- since dates represent the number of days since 1970-01-01, computing the mean is just a matter of computing the mean of the underlying numeric vector and converting it back to a date. To demonstrate this idea, I'll first define a mean generic with a method for numbers: From 870524bf935c1ca2d5eee57aaa15caac611ad7c7 Mon Sep 17 00:00:00 2001 From: Matt Heaphy Date: Wed, 13 Nov 2024 07:11:41 -0500 Subject: [PATCH 6/8] suggested rename in compatibility.Rmd --- vignettes/compatibility.Rmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vignettes/compatibility.Rmd b/vignettes/compatibility.Rmd index 47ebed6a..c84c5557 100644 --- a/vignettes/compatibility.Rmd +++ b/vignettes/compatibility.Rmd @@ -75,14 +75,14 @@ rle <- function(x) { } n <- length(x) if (n == 0L) { - new_rle(integer(), x) + rle2(integer(), x) } else { y <- x[-1L] != x[-n] i <- c(which(y | is.na(y)), n) - new_rle(diff(c(0L, i)), x[i]) + rle2(diff(c(0L, i)), x[i]) } } -new_rle <- function(lengths, values) { +rle2 <- function(lengths, values) { structure( list( lengths = lengths, @@ -97,7 +97,7 @@ There are two ways to convert this to S7. You could keep the structure exactly the same, using a `list` as the underlying data structure and using a constructor to enforce the structure: ```{r} -new_rle <- new_class("rle", +rle2 <- new_class("rle", parent = class_list, constructor = function(lengths, values) { new_object(list(lengths = lengths, values = values)) @@ -109,7 +109,7 @@ rle(1:10) Alternatively you could convert it to the most natural representation using S7: ```{r} -new_rle <- new_class("rle", properties = list( +rle2 <- new_class("rle", properties = list( lengths = class_integer, values = class_atomic )) @@ -118,7 +118,7 @@ new_rle <- new_class("rle", properties = list( To allow existing methods to work you'll need to override `$` to access properties instead of list elements: ```{r} -method(`$`, new_rle) <- prop +method(`$`, rle2) <- prop rle(1:10) ``` From fc312c4736636d5ace996655206ba66b1001c0b2 Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Wed, 13 Nov 2024 12:12:51 -0500 Subject: [PATCH 7/8] Add comment --- vignettes/classes-objects.Rmd | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/vignettes/classes-objects.Rmd b/vignettes/classes-objects.Rmd index 4f41b3fb..7f5e28dd 100644 --- a/vignettes/classes-objects.Rmd +++ b/vignettes/classes-objects.Rmd @@ -257,7 +257,11 @@ Range <- new_class("Range", class = class_double, getter = function(self) self@end - self@start, setter = function(self, value) { - if (length(value) == 0) return(self) + if (!length(value)) { + # Do nothing if called with the constructor default + # value for this property, a zero-length double vector. + return(self) + } self@end <- self@start + value self } @@ -393,8 +397,8 @@ Range <- new_class("Range", end = class_numeric ), constructor = function(x) { - new_object(S7_object(), - start = min(x, na.rm = TRUE), + new_object(S7_object(), + start = min(x, na.rm = TRUE), end = max(x, na.rm = TRUE)) } ) From 5fb442f3a81cde90282d424baa130c11cb0402a5 Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Wed, 13 Nov 2024 12:13:33 -0500 Subject: [PATCH 8/8] return of `new_rle` --- vignettes/compatibility.Rmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vignettes/compatibility.Rmd b/vignettes/compatibility.Rmd index c84c5557..47ebed6a 100644 --- a/vignettes/compatibility.Rmd +++ b/vignettes/compatibility.Rmd @@ -75,14 +75,14 @@ rle <- function(x) { } n <- length(x) if (n == 0L) { - rle2(integer(), x) + new_rle(integer(), x) } else { y <- x[-1L] != x[-n] i <- c(which(y | is.na(y)), n) - rle2(diff(c(0L, i)), x[i]) + new_rle(diff(c(0L, i)), x[i]) } } -rle2 <- function(lengths, values) { +new_rle <- function(lengths, values) { structure( list( lengths = lengths, @@ -97,7 +97,7 @@ There are two ways to convert this to S7. You could keep the structure exactly the same, using a `list` as the underlying data structure and using a constructor to enforce the structure: ```{r} -rle2 <- new_class("rle", +new_rle <- new_class("rle", parent = class_list, constructor = function(lengths, values) { new_object(list(lengths = lengths, values = values)) @@ -109,7 +109,7 @@ rle(1:10) Alternatively you could convert it to the most natural representation using S7: ```{r} -rle2 <- new_class("rle", properties = list( +new_rle <- new_class("rle", properties = list( lengths = class_integer, values = class_atomic )) @@ -118,7 +118,7 @@ rle2 <- new_class("rle", properties = list( To allow existing methods to work you'll need to override `$` to access properties instead of list elements: ```{r} -method(`$`, rle2) <- prop +method(`$`, new_rle) <- prop rle(1:10) ```