knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library(constructive)
We detail in this vignette how {constructive} works and how
you might extend it by defining custom .cstr_construct.<class>()
and
.cstr_construct.<class>.<constructor>()
methods.
The functions .cstr_new_class()
and .cstr_new_constructor()
can be used to
create custom constructors from templates. The easiest workflow is probably
to take a look at the
package {constructive.examples} which will guide you through the process, then call these functions with the argument commented = TRUE
.
If this is not enough, or if you want to know more, we describe below in more details how the package and its key functions work.
construct()
and .cstr_construct()
.cstr_construct()
builds code recursively, without checking the inputs or output
validity, without error handling, and without formatting.construct()
wraps .cstr_construct()
and does this extra work..cstr_construct()
is a generic and many methods are implemented in the package, for
instance construct(iris)
will call .cstr_construct.data.frame()
which down the line
will call .cstr_construct.numeric()
and .cstr_construct.factor()
to construct its columns..cstr_construct()
contains extra logic to :data
argument.classes
argument and the functions construct_base()
and
construct_dput()
can work..cstr_construct # a character vector .cstr_construct(letters) # a constructive object, construct(letters)
.cstr_construct.<class>()
methods.cstr_construct.<class>()
methods are generics themselves, they typically have this form:
.cstr_construct.Date <- function(x, ...) { opts <- list(...)$opts$Date %||% opts_Date() if (is_corrupted_Date(x) || opts$constructor == "next") return(NextMethod()) UseMethod(".cstr_construct.Date", structure(NA, class = opts$constructor)) }
opts
contains options passed to the ...
or template
arguments of
construct()
through the opts_*()
functions, we fall back to a default value if none were provided.
The list(...)$opts
idiom is used in various places, it allows us to forward
the dots conveniently. NextMethod()
to
forward all our inputs to a lower level constructor.UseMethod()
call above.opts_<class>()
functionWhen implementing a new method you'll need to define and export the corresponding
opts_<class>()
function. It provides to the user with a way to choose a constructor and
additional parameters, and sets the default behavior.
It should always have this form:
opts_Date <- function( constructor = c( "as.Date", "as_date", "date", "new_date", "as.Date.numeric", "as_date.numeric", "next", "double"), ..., origin = "1970-01-01") { .cstr_options("Date", constructor = constructor[[1]], ..., origin = origin) }
opts_<class>()
function and as the first
argument of .cstr_options()
.origin
match.arg()
here, because new constructors can be defined
outside of the package.is_corrupted_<class>()
functionis_corrupted_<class>()
checks if x
has the right internal type and attributes,
sometimes structure, so that it satisfies the expectations of a well formatted object
of a given class.
If an object is corrupted for a given class we cannot use constructors for this
class, so we move on to a lower level constructor by calling NextMethod()
in
.cstr_construct()
.
This is important so that {constructive}
doesn't choke on corrupted objects but
instead helps us understand them.
For instance in the following example x
prints like a date but it's corrupted,
a date should not be built on top of characters and this object cannot be built
with as.Date()
or other idiomatic date constructors.
x <- structure("12345", class = "Date") x x + 1
We have defined :
is_corrupted_Date <- function(x) { !is.double(x) }
And as a consequence the next method, .cstr_construct.default()
will be called
through NextMethod()
and will handle the object using an atomic vector constructor:
construct(x)
constructors are functions named as .cstr_construct.<class>.<constructor>
.
For instance the default constructor for "Date" is :
constructive:::.cstr_construct.Date.as.Date
Their arguments are x
and ...
, and not more. Additional parameters fed
to the opt_<class>()
function can be fetched from list(...)$opts$<class>
The function .cstr_apply()
is used to construct arguments recursively.
Sometimes a constructor cannot handle all cases and we need to fall back to
another constructor, it happens below because Inf
, NA
, or decimal dates
cannot be represented by a string wrapped by as.Date()
.
x <- structure(c(12345, 20000), class = "Date") y <- structure(c(12345, Inf), class = "Date") construct(x) construct(y)
That last line of the function is essential, it does the attribute repair.
Constructors should always end by a call to .cstr_repair_attributes()
or a function
that wraps it.
These are needed to adjust the attributes of an object after idiomatic constructors such as as.Date()
have defined their data and canonical attributes.
x <- structure(c(12345, 20000), class = "Date", some_attr = 42) # attributes are not visible due to "Date"'s printing method x construct(x)
.cstr_repair_attributes()
essentially sets attributes with exceptions :
idiomatic_class
argumentconstructive:::repair_attributes_Date constructive:::repair_attributes_factor constructive:::repair_attributes_tbl_df
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.