diff --git a/R/S3.R b/R/S3.R index e399b1eb..dcfbe0ff 100644 --- a/R/S3.R +++ b/R/S3.R @@ -2,6 +2,7 @@ #' #' To use an S3 class with S7, you must explicitly declare it using #' `new_S3_class()` because S3 lacks a formal class definition. +#' (Unless it's an important base class already defined in [base_s3_classes].) #' #' # Method dispatch, properties, and unions #' There are three ways of using S3 with S7 that only require the S3 class @@ -180,8 +181,19 @@ validate_data.frame <- function(self) { } } +#' S7 wrappers for key S3 classes +#' +#' @description +#' S7 bundles [S3 definitions][new_S3_class] for key S3 classes provided by +#' the base package: +#' +#' * `class_data.frame` for data frames. +#' * `class_Date` for dates. +#' * `class_factor` for factors. +#' * `class_POSIXct` for `POSIXct` date-times. +#' #' @export -#' @rdname base_classes +#' @name base_s3_classes #' @format NULL #' @order 3 class_factor <- new_S3_class("factor", @@ -192,7 +204,7 @@ class_factor <- new_S3_class("factor", ) #' @export -#' @rdname base_classes +#' @rdname base_s3_classes #' @format NULL #' @order 3 class_Date <- new_S3_class("Date", @@ -203,7 +215,7 @@ class_Date <- new_S3_class("Date", ) #' @export -#' @rdname base_classes +#' @rdname base_s3_classes #' @format NULL #' @order 3 class_POSIXct <- new_S3_class("POSIXct", @@ -214,7 +226,7 @@ class_POSIXct <- new_S3_class("POSIXct", ) #' @export -#' @rdname base_classes +#' @rdname base_s3_classes #' @format NULL #' @order 3 class_data.frame <- new_S3_class("data.frame", diff --git a/R/base.R b/R/base.R index 729775c6..753da654 100644 --- a/R/base.R +++ b/R/base.R @@ -54,13 +54,11 @@ str.S7_base_class <- function(object, ..., nest.lev = 0) { print(object, ..., nest.lev = nest.lev) } -#' Base classes +#' S7 wrappers for base types #' #' @description -#' These classes represent base types allowing them to be used within S7. -#' There are three categories: base types, unions types, and key S3 classes. -#' -#' Base types: +#' The following S7 classes represent base types allowing them to be used +#' within S7: #' #' * `class_logical` #' * `class_integer` @@ -73,7 +71,8 @@ str.S7_base_class <- function(object, ..., nest.lev = 0) { #' * `class_function` #' * `class_environment` (can only be used for properties) #' -#' Union types: +#' We also include three union types to model numerics, atomics, and vectors +#' respectively: #' #' * `class_numeric` is a union of `class_integer` and `class_double`. #' * `class_atomic` is a union of `class_logical`, `class_numeric`, @@ -81,13 +80,6 @@ str.S7_base_class <- function(object, ..., nest.lev = 0) { #' * `class_vector` is a union of `class_atomic`, `class_list`, and #' `class_expression`. #' -#' Key S3 classes: -#' -#' * `class_data.frame` -#' * `class_Date` -#' * `class_factor` -#' * `class_POSIXct` -#' #' @order 0 #' @name base_classes #' @return S7 classes wrapping around common base types and S3 classes. diff --git a/R/class-spec.R b/R/class-spec.R index d3237383..525766f6 100644 --- a/R/class-spec.R +++ b/R/class-spec.R @@ -12,6 +12,7 @@ #' * A base class, like [class_logical], [class_integer], or [class_double]. #' * A "special", either [class_missing] or [class_any]. #' @param arg Argument name used when generating errors. +#' @keywords internal #' @export #' @return A standardised class: either `NULL`, an S7 class, an S7 union, #' as [new_S3_class], or a S4 class. diff --git a/R/method-introspect.R b/R/method-introspect.R index fbaf6200..6011875f 100644 --- a/R/method-introspect.R +++ b/R/method-introspect.R @@ -1,14 +1,15 @@ -#' Retrieve a method for an S7 generic +#' Find a method for an S7 generic #' -#' `method()` takes a generic and signature and retrieves the corresponding -#' method. This is rarely needed because most of the time you'll rely on the -#' the generic, via [S7_dispatch()], to find and call the method for you. -#' However, this introspection is useful if you want to see the implementation -#' of a specific method. +#' `method()` takes a generic and class signature and performs method dispatch +#' to find the corresponding method implementation. This is rarely needed +#' because you'll usually rely on the the generic to do dispatch for you (via +#' [S7_dispatch()]). However, this introspection is useful if you want to see +#' the implementation of a specific method. #' #' @seealso [method_explain()] to explain why a specific method was picked. #' @inheritParams method<- -#' @returns A function with class . +#' @returns Either a function with class `S7_method` or an error if no +#' matching method is found. #' @param class,object Perform introspection either with a `class` #' (processed with [as_class()]) or a concrete `object`. If `generic` uses #' multiple dispatch then both `object` and `class` must be a list of diff --git a/R/method-register.R b/R/method-register.R index ba0a535c..dbd22c0a 100644 --- a/R/method-register.R +++ b/R/method-register.R @@ -1,4 +1,4 @@ -#' Register a S7 method for a generic +#' Register an S7 method for a generic #' #' @description #' A generic defines the interface of a function. Once you have created a diff --git a/R/special.R b/R/special.R index a812e6c1..1afbae03 100644 --- a/R/special.R +++ b/R/special.R @@ -1,20 +1,19 @@ -#' Special dispatch types +#' Dispatch on a missing argument #' -#' * Use `class_missing` when the user has not supplied an argument -#' * Use `class_any` for a default method that is called only if no other -#' methods are matched +#' Use `class_missing` to dispatch when the user has not supplied an argument, +#' i.e. it's missing in the sense of [missing()], not in the sense of +#' [is.na()]. #' #' @export #' @return Sentinel objects used for special types of dispatch. +#' @format NULL #' @examples #' foo <- new_generic("foo", "x") -#' method(foo, class_integer) <- function(x) "integer" -#' method(foo, class_missing) <- function(x) "missing" +#' method(foo, class_numeric) <- function(x) "number" #' method(foo, class_any) <- function(x) "fallback" #' #' foo(1) #' foo() -#' foo("x") class_missing <- structure(list(), class = "S7_missing") is_class_missing <- function(x) inherits(x, "S7_missing") @@ -30,8 +29,20 @@ str.S7_missing <- function(object, ..., nest.lev = 0) { print(object) } +#' Dispatch on any class +#' +#' Use `class_any` to register a default method that is called when no other +#' methods are matched. +#' #' @export -#' @rdname class_missing +#' @format NULL +#' @examples +#' foo <- new_generic("foo", "x") +#' method(foo, class_numeric) <- function(x) "number" +#' method(foo, class_any) <- function(x) "fallback" +#' +#' foo(1) +#' foo("x") class_any <- structure(list(), class = "S7_any") is_class_any <- function(x) inherits(x, "S7_any") diff --git a/_pkgdown.yml b/_pkgdown.yml index 3a0e795e..9406eeef 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -24,17 +24,30 @@ reference: - title: Method dispatch contents: - convert - - super - - as_class + - class_missing + - class_any - method - method_explain + - super + - S7_class + +- title: Packages + desc: > + Functions needed when using S7 within a package. See `vignette("packages")` + for more details. + contents: - methods_register - new_external_generic + +- title: Compatibility + desc: > + These tools provide a layer of compatibility between S7 and S3 classes, S4 + classes, and base types. See `vignette("compatibility")` for more details. + contents: + - base_classes + - base_s3_classes - new_S3_class - S4_register - - S7_class - - base_classes - - class_missing articles: - title: Learn S7 diff --git a/man/as_class.Rd b/man/as_class.Rd index 0d8d6f1a..9864f4a3 100644 --- a/man/as_class.Rd +++ b/man/as_class.Rd @@ -32,3 +32,4 @@ formal S4 classes. as_class(class_logical) as_class(new_S3_class("factor")) } +\keyword{internal} diff --git a/man/base_classes.Rd b/man/base_classes.Rd index 8c7da50c..6411af27 100644 --- a/man/base_classes.Rd +++ b/man/base_classes.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/base.R, R/S3.R +% Please edit documentation in R/base.R \docType{data} \name{base_classes} \alias{base_classes} @@ -16,11 +16,7 @@ \alias{class_numeric} \alias{class_atomic} \alias{class_vector} -\alias{class_factor} -\alias{class_Date} -\alias{class_POSIXct} -\alias{class_data.frame} -\title{Base classes} +\title{S7 wrappers for base types} \usage{ class_logical @@ -47,23 +43,13 @@ class_numeric class_atomic class_vector - -class_factor - -class_Date - -class_POSIXct - -class_data.frame } \value{ S7 classes wrapping around common base types and S3 classes. } \description{ -These classes represent base types allowing them to be used within S7. -There are three categories: base types, unions types, and key S3 classes. - -Base types: +The following S7 classes represent base types allowing them to be used +within S7: \itemize{ \item \code{class_logical} \item \code{class_integer} @@ -77,7 +63,8 @@ Base types: \item \code{class_environment} (can only be used for properties) } -Union types: +We also include three union types to model numerics, atomics, and vectors +respectively: \itemize{ \item \code{class_numeric} is a union of \code{class_integer} and \code{class_double}. \item \code{class_atomic} is a union of \code{class_logical}, \code{class_numeric}, @@ -85,14 +72,6 @@ Union types: \item \code{class_vector} is a union of \code{class_atomic}, \code{class_list}, and \code{class_expression}. } - -Key S3 classes: -\itemize{ -\item \code{class_data.frame} -\item \code{class_Date} -\item \code{class_factor} -\item \code{class_POSIXct} -} } \examples{ diff --git a/man/base_s3_classes.Rd b/man/base_s3_classes.Rd new file mode 100644 index 00000000..efd2ab93 --- /dev/null +++ b/man/base_s3_classes.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/S3.R +\docType{data} +\name{base_s3_classes} +\alias{base_s3_classes} +\alias{class_factor} +\alias{class_Date} +\alias{class_POSIXct} +\alias{class_data.frame} +\title{S7 wrappers for key S3 classes} +\usage{ +class_factor + +class_Date + +class_POSIXct + +class_data.frame +} +\description{ +S7 bundles \link[=new_S3_class]{S3 definitions} for key S3 classes provided by +the base package: +\itemize{ +\item \code{class_data.frame} for data frames. +\item \code{class_Date} for dates. +\item \code{class_factor} for factors. +\item \code{class_POSIXct} for \code{POSIXct} date-times. +} +} +\keyword{datasets} diff --git a/man/class_any.Rd b/man/class_any.Rd new file mode 100644 index 00000000..7c9afad1 --- /dev/null +++ b/man/class_any.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/special.R +\docType{data} +\name{class_any} +\alias{class_any} +\title{Dispatch on any class} +\usage{ +class_any +} +\description{ +Use \code{class_any} to register a default method that is called when no other +methods are matched. +} +\examples{ +foo <- new_generic("foo", "x") +method(foo, class_numeric) <- function(x) "number" +method(foo, class_any) <- function(x) "fallback" + +foo(1) +foo("x") +} +\keyword{datasets} diff --git a/man/class_missing.Rd b/man/class_missing.Rd index e0f40e4e..058aa187 100644 --- a/man/class_missing.Rd +++ b/man/class_missing.Rd @@ -3,36 +3,24 @@ \docType{data} \name{class_missing} \alias{class_missing} -\alias{class_any} -\title{Special dispatch types} -\format{ -An object of class \code{S7_missing} of length 0. - -An object of class \code{S7_any} of length 0. -} +\title{Dispatch on a missing argument} \usage{ class_missing - -class_any } \value{ Sentinel objects used for special types of dispatch. } \description{ -\itemize{ -\item Use \code{class_missing} when the user has not supplied an argument -\item Use \code{class_any} for a default method that is called only if no other -methods are matched -} +Use \code{class_missing} to dispatch when the user has not supplied an argument, +i.e. it's missing in the sense of \code{\link[=missing]{missing()}}, not in the sense of +\code{\link[=is.na]{is.na()}}. } \examples{ foo <- new_generic("foo", "x") -method(foo, class_integer) <- function(x) "integer" -method(foo, class_missing) <- function(x) "missing" +method(foo, class_numeric) <- function(x) "number" method(foo, class_any) <- function(x) "fallback" foo(1) foo() -foo("x") } \keyword{datasets} diff --git a/man/method-set.Rd b/man/method-set.Rd index aadf6ec4..102855db 100644 --- a/man/method-set.Rd +++ b/man/method-set.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/method-register.R \name{method<-} \alias{method<-} -\title{Register a S7 method for a generic} +\title{Register an S7 method for a generic} \usage{ method(generic, signature) <- value } diff --git a/man/method.Rd b/man/method.Rd index a767d4a9..02fce239 100644 --- a/man/method.Rd +++ b/man/method.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/method-introspect.R \name{method} \alias{method} -\title{Retrieve a method for an S7 generic} +\title{Find a method for an S7 generic} \usage{ method(generic, class = NULL, object = NULL) } @@ -17,14 +17,15 @@ multiple dispatch then both \code{object} and \code{class} must be a list of classes/objects.} } \value{ -A function with class . +Either a function with class \code{S7_method} or an error if no +matching method is found. } \description{ -\code{method()} takes a generic and signature and retrieves the corresponding -method. This is rarely needed because most of the time you'll rely on the -the generic, via \code{\link[=S7_dispatch]{S7_dispatch()}}, to find and call the method for you. -However, this introspection is useful if you want to see the implementation -of a specific method. +\code{method()} takes a generic and class signature and performs method dispatch +to find the corresponding method implementation. This is rarely needed +because you'll usually rely on the the generic to do dispatch for you (via +\code{\link[=S7_dispatch]{S7_dispatch()}}). However, this introspection is useful if you want to see +the implementation of a specific method. } \examples{ # Create a generic and register some methods diff --git a/man/new_S3_class.Rd b/man/new_S3_class.Rd index 6e059543..47f5cd66 100644 --- a/man/new_S3_class.Rd +++ b/man/new_S3_class.Rd @@ -35,6 +35,7 @@ An S7 definition of an S3 class, i.e. a list with class \description{ To use an S3 class with S7, you must explicitly declare it using \code{new_S3_class()} because S3 lacks a formal class definition. +(Unless it's an important base class already defined in \link{base_s3_classes}.) } \section{Method dispatch, properties, and unions}{ There are three ways of using S3 with S7 that only require the S3 class diff --git a/vignettes/compatibility.Rmd b/vignettes/compatibility.Rmd index ad370d73..f5382cca 100644 --- a/vignettes/compatibility.Rmd +++ b/vignettes/compatibility.Rmd @@ -1,8 +1,8 @@ --- -title: "Compatibility" +title: "Compatibility with S3 and S4" output: rmarkdown::html_vignette vignette: > - %\VignetteIndexEntry{Compatibility} + %\VignetteIndexEntry{Compatibility with S3 and S4} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} ---