-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathREADME.Rmd
More file actions
174 lines (115 loc) · 6.18 KB
/
README.Rmd
File metadata and controls
174 lines (115 loc) · 6.18 KB
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
---
output: downlit::readme_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
pkgload::load_all()
```
# [rpp](https://rpp.q-lang.org/)
<!-- badges: start -->
[](https://lifecycle.r-lib.org/articles/stages.html#experimental)
<!-- badges: end -->
The goal of rpp is to provide a framework for preprocessing R code.
Ultimately, this package aims at supporting static type checking for R, among other applications.
At this time, dynamic type checking and zero-cost assertions are supported with the help of two other packages, {typed} and {chk}.
## Motivation
R is a weakly-typed interpreted language: variables can hold objects of any time, there is no explicit compilation stage, and no checks are carried out during run time.
This is great for interactive ad-hoc analysis, but can make complex projects more difficult to maintain and debug.
In contrast, in strongly-typed languages, the type of each variable is declared beforehand.
Adding a static type checking layer to R would make it easier to improve stability in complex projects.
With preprocessing, this can be done with no cost at runtime.
## Development vs. production
This package operates on the notion of different source code "modes":
- **Development** (or dev): the code the developer of the package works on
- **Production** (or prod): the code that is run by typical users
Expensive checks can be enabled in development mode, while production code is kept lean and fast.
In production mode, all checks are completely removed (elided) from the source code.
Only production code ends up in version control, this ensures compatibility with existing tooling.
Code can be quickly and losslessly converted between development and production modes with `rpp::rpp_to_dev()` and `rpp::rpp_to_prod()`.
## Plugins
The rpp package does not implement any code transformations.
Rather, it provides the infrastructure for plugins which are responsible for converting development code to production code and back.
Currently, two plugins exist (in forks of existing packages in this GitHub organization):
- {[typed](https://github.com/Q-language/typed)} provides dynamic type checking via the `typed::rpp_elide_types()` plugin
- {[chk](https://github.com/Q-language/chk)} provides zero-cost assertions via the `chk::rpp_elide_chk_calls()` plugin
Plugins are configured in the `DESCRIPTION` file.
## Installation
Install the development version of rpp and the associated packages from [GitHub](https://github.com/) with:
``` r
# install.packages("devtools")
devtools::install_github("Q-language/rpp")
devtools::install_github("Q-language/typed")
devtools::install_github("Q-language/chk")
```
Once on [CRAN](https://CRAN.R-project.org), you can also install the released version of rpp with:
``` r
install.packages("rpp")
```
## Example
```{r}
library(typed)
```
The following function makes use of dynamic type assertions provided by the {typed} package:
```{r}
foo <- Character()? function(x = ?Character()) {
Character()? out <- paste("foo:", x)
out
}
```
This is still valid R code, because {typed} overloads the `?` operator.
The function can only be called with a character vector, other types give an error:
```{r error = TRUE}
foo("bar")
foo(1)
foo(mean)
```
These checks are useful, but slow down the code.
If this function lives in a package that is configured with the `typed::rpp_elide_types()` plugin, running `rpp::rpp_to_prod()` results in the following code:
```{r}
foo <- function(x ) { # !q foo <- Character()? function(x = ?Character()) {
out <- paste("foo:", x) # !q Character()? out <- paste("foo:", x)
out
}
```
Running `rpp::rpp_to_dev()` brings back the original code with the checks.
The production version is not particularly pretty, but does the job.
The fork of the {[chk](https://github.com/Q-language/chk)} package in this organization is configured for use with rpp.
Clone the repository, start an R session, and run `rpp::rpp_to_dev()` and `rpp::rpp_to_prod()` to see rpp in action.
## Configure your own project for use with rpp
This example show how to configure your project with rpp and dynamic type checking via {typed}.
Note that this requires {typed} to be installed from [this organization's fork](https://github.com/Q-language/typed).
Adapt as necessary for use with other plugins.
### Basic configuration
rpp currently works only on projects with a `DESCRIPTION` file, and operates only on files in the `R/` directory.
Add the following line to `DESCRIPTION`:
```dcf
Config/rpp/plugins: list(typed::rpp_elide_types())
```
With this configuration, `rpp::rpp_to_dev()` and `rpp::rpp_to_prod()` will run the plugin implemented by {typed}.
### Configuring for {typed}
In the case of {typed}, two more tweaks are necessary:
1. The package must be listed in the `Imports` section of the `DESCRIPTION` file.
2. It should be imported to enable the overload of the `?` operator and to access the type declarations.
For the latter, add `import(typed)` to `NAMESPACE`, or `#' @import typed` to an `.R` source file if you are using roxygen2.
### {roxygen2} integration
A project can be automated to change to production mode when `devtools::document()` is run.
Add the `rpp::rpp_prod_roclet()` to the list of roclets:
```dcf
Roxygen: list(markdown = TRUE, roclets = c("collate", "namespace", "rd", "rpp::rpp_prod_roclet"))
```
RStudio users can also edit the `.Rproj` file to convert to prod when Ctrl+Shift+D is pressed:
```txt
PackageRoxygenize: rd,collate,namespace,rpp::rpp_prod_roclet
```
With this configuration, running `devtools::document()` includes the effect of calling `rpp::rpp_to_prod()`.
## Further reading
The creation of a new plugin and the integration with roxygen2 is described in `vignette("plugins", package = "rpp")`.
---
## Code of Conduct
Please note that the rpp project is released with a [Contributor Code of Conduct](https://rpp.q-lang.org/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms.