-
Notifications
You must be signed in to change notification settings - Fork 6
/
README.Rmd
177 lines (127 loc) · 5.84 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
---
output: github_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
`r lifecycle::badge("experimental")`
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
```
# constructive <a href="https://cynkra.github.io/constructive/"><img src="man/figures/logo.png" align="right" height="139" alt="constructive website" /></a>
{constructive} prints code that can be used to recreate R objects. In a sense it
is similar to `base::dput()` or `base::deparse()` but {constructive} strives to use idiomatic constructors
(`factor` for factors, `as.Date()` for dates, `data.frame()` for data frames etc),
in order to get output readable by humans.
Some use cases are:
* Snapshot tests
* Exploring objects (alternative to `dput()` or `str()`)
* Creating reproducible examples from existing data
* Comparing two objects (using `construct_diff()`)
## Installation
Install the last stable version from CRAN:
```r
install.packages('constructive')
```
Or install the development version from [cynkra R-universe](https://cynkra.r-universe.dev):
```r
install.packages('constructive', repos = c('https://cynkra.r-universe.dev', 'https://cloud.r-project.org'))
```
Or directly from github:
```
pak::pak("cynkra/constructive")
```
## Comparison with `dput()`
A few examples compared to their `dput()` output.
```{r, error = TRUE}
library(constructive)
construct(head(iris, 2))
dput(head(iris, 2))
construct(.leap.seconds)
dput(.leap.seconds)
library(dplyr, warn.conflicts = FALSE)
grouped_band_members <- group_by(band_members, band)
dput(grouped_band_members)
construct(grouped_band_members)
```
We can provide to the `data` argument a named list, an environment, a package where to look
for data, or an unnamed list of such items, so we don't print more than necessary, for instance improving the previous example:
```{r}
construct(grouped_band_members, data = "dplyr")
```
## Customize the output using constructive options
Some objects can be constructed in several ways, for instance a tibble
might be constructed using `tibble::tibble()` or using `tibble::tribble()`.
The `opts_*()` family of functions provides ways to tweak the output code, namely
setting the constructor itself or options used by the constructor
```{r}
construct(band_members, opts_tbl_df("tribble"))
construct(band_members, opts_tbl_df("tribble", justify = "right"))
r <- as.raw(c(0x68, 0x65, 0x6c, 0x6c, 0x6f))
construct(r)
construct(r, opts_raw(representation = "decimal"))
construct(r, opts_raw("charToRaw"))
```
These functions have their own documentation page and are referenced in `?construct`.
For every class that doesn't refer to an internal type a "next" constructor is
available, so we can conveniently explore objects using lower level constructors.
```{r}
construct(band_members, opts_tbl_df("next"))
construct(band_members, opts_tbl_df("next"), opts_data.frame("next"))
```
## Other functions
* `construct_multi()` constructs several objects from a named list or an environment
* `construct_reprex()` wraps `construct_multi()` and constructs all
the objects of the local environment, or from the caller environments.
* `construct_dput()` constructs the objects using only low level constructors,
like `structure()`, `list()`, `c()`, very similarly to `base::dput()`
* `construct_base()` constructs the objects using only base R functions.
* `construct_clip()` writes to the clipboard, see also ?`constructive-global_options`
* `construct_diff()` highlights the differences in the code used to produce 2 objects,
it's an alternative to `waldo::compare()`.
* `construct_dump()` is similar to `base::dump()`, it's a wrapper around `construct_multi()`
that writes to a file.
* `construct_signature()` constructs a function signature such as the one we see in the
"usage" section of a function's help file.
outputs the code produced
* `construct_issues()` is used without arguments to check what were the issues encountered
with the last reconstructed object, it can also be provided a specific constructive object.
* `deparse_call()` is an alternative to `base::deparse()` and `rlang::expr_deparse()` that
handles additional corner cases and fails when encountering tokens other than symbols
and syntactic literals .
## Note about environments and external pointers
Environments use reference semantics, they cannot be copied.
An attempt to copy an environment would indeed yield a different environment and `identical(env, copy)` would be `FALSE`
(read more about it in `?opts_environment`).
In some case we can build code that points to a specific environment, for instance:
```{r}
construct(globalenv())
construct(environment(setNames))
```
When it's not possible we use `constructive::.env()` function for this purpose.
```{r, results="hide"}
e1 <- new.env(parent = .GlobalEnv)
e1$x <- 1
construct(e1)
#> constructive::.env("0x131515348", parents = "global")
```
`constructive::.env()` fetches the environment from its memory address. The `parents`
argument doesn't do anything, it provides as additional information the sequence
of parents until we reach a special environment.
This strategy is convenient because it always works, but it's not reproducible
between sessions as the memory address is not stable. Moreover it doesn't tell
us anything about the environment's content.
Depending on what compromise you're ready to make, you might use different
constructions in `opts_environment()`. For the case above, choosing `"list2env"`
works well :
```{r}
construct(e1, opts_environment("list2env"))
```
`constructive::.xptr()` is the counterpart of `constructive::.env()`
to construct `"externalptr"` objects from a memory address.
## Extending constructive
You can define your own constructors and methods!
For more information see `vignette("User-defined-methods-and-constructors", package = "constructive")`