Skip to content

Example of a package defining a class and methods for 'constructive'

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE
MIT
LICENSE.md
Notifications You must be signed in to change notification settings

cynkra/constructive.example

Repository files navigation

constructive.example

This package serves as an example on how to extend {constructive}. For some users it will be enough to call constructive::.cstr_new_class() and constructive::.cstr_new_constructor() with commented = TRUE and figure it out from there. This is a more detailed walkthrough. Follow hyperlinks to inspect relevant commits.

Implement a constructor for a new class

Let’s add support for a new class : “qr”. This is the class of the object we get after applying base::qr() to get the QR decomposition of a matrix.

The reconstruction of qr objects is not perfect due to rounding errors so the feature is not a good fit for {constructive} but is a good use case for an example.

First note that {constructive} works even if it doesn’t support directly a class, it will then use the next relevant constructor:

library(constructive)

# will work but use the list constructor
A <- matrix(1:6, nrow = 3)
qr_A <- qr(A)
construct(qr_A)
#> list(
#>   qr = matrix(
#>     c(
#>       -0x1.deeea11683f49p+1, 0.5345224838248488, 0.8017837257372732,
#>       -8.55235974119758, 1.9639610121239324, 0x1.fa35f928a0dfap-1
#>     ),
#>     nrow = 3L,
#>     ncol = 2L
#>   ),
#>   rank = 2L,
#>   qraux = c(1.26726124191242429, 1.1499536117281517),
#>   pivot = 1:2
#> ) |>
#>   structure(class = "qr")

We used the following workflow:

By this time we can already use our new constructor, it doesn’t quite work yet though:

constructive::construct(qr_A)
#> ! The code built by {constructive} could not be evaluated.
#> ! Due to error: argument "x" is missing, with no default
#> qr()

This is because we haven’t construct the argument to qr()!

Let’s fix this:

After this we have:

library(constructive)
construct(qr_A)
#> {constructive} couldn't create code that reproduces perfectly the input
#> ℹ Call `construct_issues()` to inspect the last issues
#> 
#> qr(
#>   matrix(
#>     c(0.99999999999999967, 2, 3, 4.000000000000002, 5.000000000000001, 6.000000000000001),
#>     nrow = 3L,
#>     ncol = 2L
#>   )
#> )

The recreation was not perfect due to rounding errors but we’re pretty close

construct_issues()
#> NULL

We still get the previous behavior if we use the "next" constructor.

# fall back on the next method, which is for lists
construct(qr_A, opts_qr("next"))
#> list(
#>   qr = matrix(
#>     c(
#>       -0x1.deeea11683f49p+1, 0.5345224838248488, 0.8017837257372732,
#>       -8.55235974119758, 1.9639610121239324, 0x1.fa35f928a0dfap-1
#>     ),
#>     nrow = 3L,
#>     ncol = 2L
#>   ),
#>   rank = 2L,
#>   qraux = c(1.26726124191242429, 1.1499536117281517),
#>   pivot = 1:2
#> ) |>
#>   structure(class = "qr")

We still fail on corrupted objects though:

corrupted <- structure(1:3, class = "qr")
construct(corrupted)
#> Error in `construct()`:
#> ! {constructive} could not build the requested code.
#> Caused by error in `qr.X()`:
#> ! argument is not a QR decomposition
#> Run `rlang::last_trace()` to see where the error occurred.

Let’s fix this:

Now we won’t fail on corrupted objects but fall back to the next method.

corrupted <- structure(1:3, class = "qr")
construct(corrupted)
#> 1:3 |>
#>   structure(class = "qr")

Implement a new constructor for a supported class

We build a new constructor for the class “tbl_df” (tibbles), using the deprecated constructor tibble::data_frame(), equivalent to tibble::tibble().

We used the following workflow:

By this time we can already use our new constructor, it doesn’t quite work yet though:

construct(dplyr::band_members, opts_tbl_df("data_frame"))
#> {constructive} couldn't create code that reproduces perfectly the input
#> ℹ Call `construct_issues()` to inspect the last issues
#> 
#> tibble::data_frame() |>
#>   structure(row.names = c(NA, -3L))

We see that we don’t construct yet the arguments to data_frame and we repair the row.names attribute that the constructor should take care of. Let’s fix this:

After this we have:

construct(dplyr::band_members, opts_tbl_df("data_frame"))
#> tibble::data_frame(name = c("Mick", "John", "Paul"), band = c("Stones", "Beatles", "Beatles"))

our implementation of the “tibble” constructor in {constructive} is very similar but handles some corner cases that don’t apply to data_frame().

About

Example of a package defining a class and methods for 'constructive'

Resources

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE
MIT
LICENSE.md

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages