
R/05_new_type.R
121 | get_assertion.RdFetch assertion from active binding function
126 |get_assertion(x)129 | 130 |
| x | 135 |obect |
136 |
|---|

Build a new type
53 |
R/05_new_type.R
48 | process_assertion_factory_dots.RdThis needs to be exported, but shouldn't be called by the user
53 |
22 |
23 | *{typed}* implements a type system for R, it has 3 main features:
24 |
25 | * set variable types in a script or the body of a function, so they can't be
26 | assigned illegal values
27 | * set argument types in a function definition
28 | * set return type of a function
29 |
30 | The user can define their own types, or leverage assertions from
31 | other packages.
32 |
33 | Under the hood variable types use active bindings, so once a variable is restricted
34 | by an assertion, it cannot be modified in a way that would
35 | not satisfy it.
36 |
37 | ## Installation
38 |
39 | Install CRAN version with:
40 |
41 | ``` r
42 | install.packages("typed")
43 | ```
44 |
45 | or development version with :
46 |
47 | ``` r
48 | remotes::install_github("moodymudskipper/typed")
49 | ```
50 |
51 | And attach with :
52 |
53 | ```{r}
54 | # masking warning about overriding `?`
55 | library(typed, warn.conflicts = FALSE)
56 | ```
57 |
58 | ## Set variable type
59 |
60 | ### Question mark notation and `declare`
61 |
62 | Here are examples on how we would set types
63 |
64 | ```{r}
65 | Character() ? x # restrict x to "character" type
66 | x <- "a"
67 | x
68 |
69 | Integer(3) ? y <- 1:3 # restrict y to "integer" type of length 3
70 | y
71 | ```
72 |
73 | We cannot assign values of the wrong type to `x` and `y` anymore.
74 |
75 | ```{r}
76 | x <- 2
77 |
78 | y <- 4:5
79 | ```
80 |
81 | But the right type will work.
82 |
83 | ```{r}
84 | x <- c("b", "c")
85 |
86 | y <- c(1L, 10L, 100L)
87 | ```
88 |
89 | `declare` is a strict equivalent, slightly more efficient, which looks like `base::assign`.
90 |
91 | ```{r}
92 | declare("x", Character())
93 | x <- "a"
94 | x
95 |
96 | declare("y", Integer(3), 1:3)
97 | y
98 | ```
99 |
100 | ### Assertion factories and assertions
101 |
102 | `Integer` and `Character` are function factories (functions that return functions),
103 | thus `Integer(3)` and `Character()` are functions.
104 |
105 | The latter functions operate checks on a value and in case of success
106 | return this value, generally unmodified. For instance :
107 |
108 | ```{r}
109 | Integer(3)(1:2)
110 |
111 | Character()(3)
112 | ```
113 |
114 | We call `Integer(3)` and `Character()` assertions, and we call
115 | `Integer` and `Character` assertion factories (or just types, with then we must
116 | be careful not to confuse them with atomic types returned by the `typeof` function).
117 |
118 | The package contains many assertion factories (see `?assertion_factories`),
119 | the main ones are:
120 |
121 | * `Any` (No default restriction)
122 | * `Logical`
123 | * `Integer`
124 | * `Double`
125 | * `Character`
126 | * `List`
127 | * `Environment`
128 | * `Factor`
129 | * `Matrix`
130 | * `Data.frame`
131 | * `Date`
132 | * `Time` (POSIXct)
133 |
134 | ### Advanced type restriction using arguments
135 |
136 | As we've seen with `Integer(3)`, passing arguments to a
137 | assertion factory restricts the type.
138 |
139 | For instance `Integer` has arguments `length` `null_ok` and
140 | `...`. We already used `length`, `null_ok` is convenient to allow a default `NULL`
141 | value in addition to the `"integer"` type.
142 |
143 | The arguments can differ between assertion factories, for instance `Data.frame`
144 | has `nrow`, `ncol`, `each`, `null_ok` and `...`
145 |
146 | ```{r}
147 | Data.frame() ? x <- iris
148 | Data.frame(ncol = 2) ? x <- iris
149 | Data.frame(each = Double()) ? x <- iris
150 | ```
151 |
152 | In the dots we can use arguments named as functions and with the value of
153 | the expected result.
154 |
155 | ```{r}
156 | # Integer has no anyNA arg but we can still use it because a function named
157 | # this way exists
158 | Integer(anyNA = FALSE) ? x <- c(1L, 2L, NA)
159 | ```
160 |
161 | Useful arguments might be for instance, `anyDuplicated = 0L`, `names = NULL`,
162 | `attributes = NULL`... Any available function can be used.
163 |
164 | That makes assertion factories very flexible! If it is still not flexible enough,
165 | we can provide arguments arguments named `...` to functional factories to add
166 | a custom restriction, this is usually better done by defining a wrapper.
167 |
168 | ```{r}
169 | Character(1, ... = "`value` is not a fruit!" ~ . %in% c("apple", "pear", "cherry")) ?
170 | x <- "potatoe"
171 | ```
172 |
173 | This is often better done by defining a wrapper as shown below.
174 |
175 | ### Constants
176 |
177 | To define a constant, we just surround the variable by parentheses (think of
178 | them as a protection)
179 |
180 | ```{r}
181 | Double() ? (x) <- 1
182 | x <- 2
183 |
184 | # defining a type is optional
185 | ? (y) <- 1
186 | y <- 2
187 | ```
188 |
189 | ### Set a function's argument type
190 |
191 | We can set argument types this way :
192 |
193 |
194 | ```{r}
195 | add <- ? function (x= ? Double(), y= 1 ? Double()) {
196 | x + y
197 | }
198 | ```
199 |
200 | Note that we started the definition with a `?`, and that we gave a default to
201 | `y`, but not `x`. Note also the `=` sign next to `x`, necessary even when we
202 | have no default value. If you forget it you'll have an error "unexpected `?` in ...".
203 |
204 | This created the following function, by adding checks at the top of the
205 | body
206 |
207 | ```{r}
208 | add
209 | ```
210 |
211 | Let's test it by providing a right and wrong type.
212 |
213 | ```{r}
214 | add(2, 3)
215 | add(2, 3L)
216 | ```
217 |
218 | If we want to restrict `x` and `y` to the type "integer" in the rest of
219 | the body, so they cannot be overwritten by character for instance,we can use
220 | the `?+` notation :
221 |
222 | ```{r}
223 | add <- ? function (x= ?+ Double(), y= 1 ?+ Double()) {
224 | x + y
225 | }
226 |
227 | add
228 | ```
229 |
230 | We see that it is translated into a `check_arg` call containing a `.bind = TRUE`
231 | argument.
232 |
233 | ## Set a function's return type
234 |
235 | To set a return type we use `?` before the function definition as in the previous section,
236 | but we type an assertion on the left hand side.
237 |
238 | ```{r}
239 | add_or_subtract <- Double() ? function (x, y, subtract = FALSE) {
240 | if(subtract) return(x - y)
241 | x + y
242 | }
243 | add_or_subtract
244 | ```
245 |
246 | We see that the returned values have been wrapped inside `check_output` calls.
247 |
248 | # Use type in a package and define your own types
249 |
250 | See `vignette("typed-in-packaged", "typed")` or the Article section if you're
251 | browsing the pkgdown website.
252 |
253 | ## Acknowledgements
254 |
255 | This is inspired in good part by Jim Hester and Gabor Csardi's work and many
256 | great efforts on static typing, assertions, or annotations in R, in particular:
257 |
258 | * Gabor Csardy's [*argufy*](https://github.com/gaborcsardi/argufy)
259 | * Richie Cotton's [*assertive*](https://bitbucket.org/richierocks/assertive/)
260 | * Tony Fishettti's [*assertr*](https://github.com/tonyfischetti/assertr)
261 | * Hadley Wickham's [*assertthat*](https://github.com/hadley/assertthat)
262 | * Michel Lang's [*checkmate*](https://github.com/mllg/checkmate)
263 | * Joe Thorley's [*checkr*](https://github.com/poissonconsulting/checkr)
264 | * Joe Thorley's [*chk*](https://github.com/poissonconsulting/chk/)
265 | * Aviral Goel's [*contractr*](https://github.com/aviralg/contractr)
266 | * Stefan Bache's [*ensurer*](https://github.com/smbache/ensurer)
267 | * Brian Lee Yung Rowe's [*lambda.r*](https://github.com/zatonovo/lambda.r)
268 | * Kun Ren's [*rtype*](https://github.com/renkun-ken/rtype)
269 | * Duncan Temple Lang's [*TypeInfo*](https://bioconductor.org/packages/TypeInfo/)
270 | * Jim Hester's [*types*](https://github.com/jimhester/types)
271 |
--------------------------------------------------------------------------------
/tests/testthat/test-native_types.R:
--------------------------------------------------------------------------------
1 |
2 | test_that("`Any` works", {
3 | expect_equal(Any()(1), 1)
4 |
5 | expect_equal(Any(1)(1), 1)
6 | expect_error(Any(2)(1))
7 | })
8 |
9 | test_that("`Logical` works", {
10 | expect_equal(Logical()(TRUE), TRUE)
11 | expect_error(Logical()(1))
12 |
13 | expect_equal(Logical(1)(TRUE), TRUE)
14 | expect_error(Logical(2)(TRUE))
15 |
16 | expect_equal(Logical(null_ok = TRUE)(NULL), NULL)
17 | expect_error(Logical(null_ok = FALSE)(NULL))
18 | })
19 |
20 | test_that("`Integer` works", {
21 | expect_equal(Integer()(1L), 1L)
22 | expect_error(Integer()(1))
23 |
24 | expect_equal(Integer(1)(1L), 1L)
25 | expect_error(Integer(2)(1L))
26 |
27 | expect_equal(Integer(null_ok = TRUE)(NULL), NULL)
28 | expect_error(Integer(null_ok = FALSE)(NULL))
29 | })
30 |
31 | test_that("`Double` works", {
32 | expect_equal(Double()(1), 1)
33 | expect_error(Double()(1L))
34 |
35 | expect_equal(Double(1)(1), 1)
36 | expect_error(Double(2)(1))
37 |
38 | expect_equal(Double(null_ok = TRUE)(NULL), NULL)
39 | expect_error(Double(null_ok = FALSE)(NULL))
40 | })
41 |
42 | test_that("`Character` works", {
43 | expect_equal(Character()("a"), "a")
44 | expect_error(Character()(1))
45 |
46 | expect_equal(Character(1)("a"), "a")
47 | expect_error(Character(2)("a"))
48 |
49 | expect_equal(Character(null_ok = TRUE)(NULL), NULL)
50 | expect_error(Character(null_ok = FALSE)(NULL))
51 | })
52 |
53 | test_that("`Raw` works", {
54 | expect_equal(Raw()(as.raw("1")), as.raw("1"))
55 | expect_error(Raw()(1))
56 |
57 | expect_equal(Raw(1)(as.raw("1")), as.raw("1"))
58 | expect_error(Raw(2)(as.raw("1")))
59 |
60 | expect_equal(Raw(null_ok = TRUE)(NULL), NULL)
61 | expect_error(Raw(null_ok = FALSE)(NULL))
62 | })
63 |
64 | test_that("`List` works", {
65 | x <- list(a=1, b=2)
66 | y <- list(1, 2)
67 | df <- as.data.frame(x)
68 | expect_equal(List()(x), x)
69 | expect_error(List()(1))
70 |
71 | expect_equal(List(2)(x), x)
72 | expect_error(List(1)(x))
73 |
74 | expect_equal(List(each=Double())(x), x)
75 | expect_error(List(each=Integer())(x))
76 |
77 | expect_equal(List(each=Double())(y), y)
78 | expect_error(List(each=Integer())(y))
79 |
80 | expect_equal(List()(df), df)
81 | expect_error(List(data_frame_ok = FALSE)(df))
82 |
83 | expect_equal(List(null_ok = TRUE)(NULL), NULL)
84 | expect_error(List(null_ok = FALSE)(NULL))
85 | })
86 |
87 | test_that("`Null` works", {
88 | expect_equal(Null()(NULL), NULL)
89 | expect_error(Null()(1))
90 | })
91 |
92 | test_that("`Closure` works", {
93 | expect_equal(Closure()(mean), mean)
94 | expect_error(Closure()(1))
95 |
96 | expect_equal(Closure(null_ok = TRUE)(NULL), NULL)
97 | expect_error(Closure(null_ok = FALSE)(NULL))
98 | })
99 |
100 | test_that("`Special` works", {
101 | expect_equal(Special()(`<-`), `<-`)
102 | expect_error(Special()(1))
103 |
104 | expect_equal(Special(null_ok = TRUE)(NULL), NULL)
105 | expect_error(Special(null_ok = FALSE)(NULL))
106 | })
107 |
108 | test_that("`Builtin` works", {
109 | expect_equal(Builtin()(max), max)
110 | expect_error(Builtin()(1))
111 |
112 | expect_equal(Builtin(null_ok = TRUE)(NULL), NULL)
113 | expect_error(Builtin(null_ok = FALSE)(NULL))
114 | })
115 |
116 | test_that("`Environment` works", {
117 | expect_equal(Environment()(.GlobalEnv), .GlobalEnv)
118 | expect_error(Environment()(1))
119 |
120 | expect_equal(Environment(null_ok = TRUE)(NULL), NULL)
121 | expect_error(Environment(null_ok = FALSE)(NULL))
122 | })
123 |
124 | test_that("`Symbol` works", {
125 | expect_equal(Symbol()(quote(a)), quote(a))
126 | expect_error(Symbol()(1))
127 |
128 | expect_equal(Symbol(null_ok = TRUE)(NULL), NULL)
129 | expect_error(Symbol(null_ok = FALSE)(NULL))
130 | })
131 |
132 | test_that("`Pairlist` works", {
133 | x <- pairlist(a=1, b=2)
134 | y <- pairlist(1, 2)
135 | expect_equal(Pairlist()(x), x)
136 | expect_error(Pairlist()(1))
137 |
138 | expect_equal(Pairlist(2)(x), x)
139 | expect_error(Pairlist(1)(x))
140 |
141 | expect_equal(Pairlist(each=Double())(x), x)
142 | expect_error(Pairlist(each=Integer())(x))
143 |
144 | expect_equal(Pairlist(each=Double())(y), y)
145 | expect_error(Pairlist(each=Integer())(y))
146 |
147 | expect_equal(Pairlist(null_ok = TRUE)(NULL), NULL)
148 | expect_error(Pairlist(null_ok = FALSE)(NULL))
149 | })
150 |
151 | test_that("`Language` works", {
152 | expect_equal(Language()(quote(a + b)), quote(a + b))
153 | expect_error(Language()(1))
154 |
155 | expect_equal(Language(null_ok = TRUE)(NULL), NULL)
156 | expect_error(Language(null_ok = FALSE)(NULL))
157 | })
158 |
159 | test_that("`Expression` works", {
160 | expect_equal(Expression()(expression(a)), expression(a))
161 | expect_error(Expression()(1))
162 |
163 | expect_equal(Expression(1)(expression(a)), expression(a))
164 | expect_error(Expression(2)(expression(a)))
165 |
166 | expect_equal(Expression(null_ok = TRUE)(NULL), NULL)
167 | expect_error(Expression(null_ok = FALSE)(NULL))
168 | })
169 |
170 | test_that("`Function` works", {
171 | expect_equal(Function()(mean), mean)
172 | expect_error(Function()(1))
173 |
174 | expect_equal(Function(null_ok = TRUE)(NULL), NULL)
175 | expect_error(Function(null_ok = FALSE)(NULL))
176 | })
177 |
178 | test_that("`Factor` works", {
179 | expect_equal(Factor()(factor("a")), factor("a"))
180 | expect_error(Factor()(1))
181 |
182 | expect_equal(Factor(1)(factor("a")), factor("a"))
183 | expect_error(Factor(2)(factor("a")))
184 |
185 | expect_equal(Factor(levels = "a")(factor("a")), factor("a"))
186 | expect_error(Factor(levels = "a")(factor("b")))
187 |
188 | expect_equal(Factor(null_ok = TRUE)(NULL), NULL)
189 | expect_error(Factor(null_ok = FALSE)(NULL))
190 | })
191 |
192 | test_that("`Matrix` works", {
193 | expect_equal(Matrix()(matrix(1)), matrix(1))
194 | expect_error(Matrix()(1))
195 |
196 | expect_equal(Matrix(1)(matrix(1)), matrix(1))
197 | expect_error(Matrix(2)(matrix(1)))
198 |
199 | expect_equal(Matrix(,1)(matrix(1)), matrix(1))
200 | expect_error(Matrix(,2)(matrix(1)))
201 |
202 | expect_equal(Matrix(null_ok = TRUE)(NULL), NULL)
203 | expect_error(Matrix(null_ok = FALSE)(NULL))
204 | })
205 |
206 | test_that("`Array` works", {
207 | expect_equal(Array()(matrix(1)), matrix(1))
208 | expect_error(Array()(1))
209 |
210 | expect_equal(Array(c(1,1))(matrix(1)), matrix(1))
211 | expect_error(Array(c(1,2))(matrix(1)))
212 |
213 | expect_equal(Array(null_ok = TRUE)(NULL), NULL)
214 | expect_error(Array(null_ok = FALSE)(NULL))
215 | })
216 |
217 | test_that("`Data.frame` works", {
218 | expect_equal(Data.frame()(data.frame(a=1, b=2L)), data.frame(a=1, b=2L))
219 | expect_error(Data.frame()(1))
220 |
221 | expect_equal(Data.frame(1)(data.frame(a=1, b=2L)), data.frame(a=1, b=2L))
222 | expect_error(Data.frame(2)(data.frame(a=1, b=2L)))
223 |
224 | expect_equal(Data.frame(,2)(data.frame(a=1, b=2L)), data.frame(a=1, b=2L))
225 | expect_error(Data.frame(,1)(data.frame(a=1, b=2L)))
226 |
227 | expect_equal(Data.frame(each = Double())(data.frame(a=1, b=2)), data.frame(a=1, b=2))
228 | expect_error(Data.frame(each = Double())(data.frame(a=1, b=2L)))
229 |
230 | expect_equal(Data.frame(null_ok = TRUE)(NULL), NULL)
231 | expect_error(Data.frame(null_ok = FALSE)(NULL))
232 | })
233 |
234 | test_that("`Date` works", {
235 | x <- Sys.Date()
236 | expect_equal(Date()(x), x)
237 | expect_error(Date()(1))
238 |
239 | expect_equal(Date(1)(x), x)
240 | expect_error(Date(2)(x))
241 |
242 | expect_equal(Date(null_ok = TRUE)(NULL), NULL)
243 | expect_error(Date(null_ok = FALSE)(NULL))
244 | })
245 |
246 | test_that("`Time` works", {
247 | x <- Sys.time()
248 | expect_equal(Time()(x), x)
249 | expect_error(Time()(1))
250 |
251 | expect_equal(Time(1)(x), x)
252 | expect_error(Time(2)(x))
253 |
254 | expect_equal(Time(null_ok = TRUE)(NULL), NULL)
255 | expect_error(Time(null_ok = FALSE)(NULL))
256 | })
257 |
258 | test_that("`Dots` works", {
259 | x <- list(a=1, b=2)
260 | y <- list(1, 2)
261 |
262 | expect_equal(Dots(2)(x), x)
263 | expect_error(Dots(1)(x))
264 |
265 | expect_equal(Dots(each=Double())(x), x)
266 | expect_error(Dots(each=Integer())(x))
267 |
268 | expect_equal(Dots(each=Double())(y), y)
269 | expect_error(Dots(each=Integer())(y))
270 | })
271 |
--------------------------------------------------------------------------------
/.github/workflows/revdep.yaml:
--------------------------------------------------------------------------------
1 | # This workflow creates many jobs, run only when a branch is created
2 | on:
3 | push:
4 | branches:
5 | - "revdep*" # never run automatically on main branch
6 |
7 | name: revdep
8 |
9 | jobs:
10 | matrix:
11 | runs-on: ubuntu-22.04
12 | outputs:
13 | matrix: ${{ steps.set-matrix.outputs.matrix }}
14 |
15 | name: Collect revdeps
16 |
17 | env:
18 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
19 | RSPM: https://packagemanager.rstudio.com/cran/__linux__/bionic/latest
20 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
21 | # prevent rgl issues because no X11 display is available
22 | RGL_USE_NULL: true
23 | # Begin custom: env vars
24 | # End custom: env vars
25 |
26 | steps:
27 | - name: Check rate limits
28 | run: |
29 | curl -s --header "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/rate_limit
30 | shell: bash
31 |
32 | - uses: actions/checkout@v4
33 |
34 | # FIXME: Avoid reissuing succesful jobs
35 | # https://docs.github.com/en/free-pro-team@latest/rest/reference/actions#list-jobs-for-a-workflow-run
36 | # https://docs.github.com/en/free-pro-team@latest/rest/reference/actions#workflow-runs
37 | - id: set-matrix
38 | run: |
39 | package <- read.dcf("DESCRIPTION")[, "Package"][[1]]
40 | deps <- tools:::package_dependencies(package, reverse = TRUE, which = c("Depends", "Imports", "LinkingTo", "Suggests"))[[1]]
41 | json <- paste0(
42 | '{"package":[',
43 | paste0('"', deps, '"', collapse = ","),
44 | ']}'
45 | )
46 | writeLines(json)
47 | writeLines(paste0("matrix=", json), Sys.getenv("GITHUB_OUTPUT"))
48 | shell: Rscript {0}
49 |
50 | check-matrix:
51 | runs-on: ubuntu-22.04
52 | needs: matrix
53 | steps:
54 | - name: Install json2yaml
55 | run: |
56 | sudo npm install -g json2yaml
57 |
58 | - name: Check matrix definition
59 | run: |
60 | matrix='${{ needs.matrix.outputs.matrix }}'
61 | echo $matrix
62 | echo $matrix | jq .
63 | echo $matrix | json2yaml
64 |
65 | R-CMD-check:
66 | needs: matrix
67 |
68 | runs-on: ubuntu-22.04
69 |
70 | name: 'revdep: ${{ matrix.package }}'
71 |
72 | # Begin custom: services
73 | # End custom: services
74 |
75 | strategy:
76 | fail-fast: false
77 | matrix: ${{fromJson(needs.matrix.outputs.matrix)}}
78 |
79 | env:
80 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
81 | RSPM: https://packagemanager.rstudio.com/cran/__linux__/bionic/latest
82 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
83 | # prevent rgl issues because no X11 display is available
84 | RGL_USE_NULL: true
85 | # Begin custom: env vars
86 | # End custom: env vars
87 |
88 | steps:
89 | - name: Check rate limits
90 | run: |
91 | curl -s --header "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/rate_limit
92 | shell: bash
93 |
94 | - uses: actions/checkout@v4
95 |
96 | # Begin custom: before install
97 | # End custom: before install
98 |
99 | - name: Use RSPM
100 | run: |
101 | mkdir -p /home/runner/work/_temp/Library
102 | echo 'local({release <- system2("lsb_release", "-sc", stdout = TRUE); options(repos=c(CRAN = paste0("https://packagemanager.rstudio.com/all/__linux__/", release, "/latest")), HTTPUserAgent = sprintf("R/%s R (%s)", getRversion(), paste(getRversion(), R.version$platform, R.version$arch, R.version$os)))}); .libPaths("/home/runner/work/_temp/Library")' | sudo tee /etc/R/Rprofile.site
103 |
104 | - name: Install remotes
105 | run: |
106 | if (!requireNamespace("curl", quietly = TRUE)) install.packages("curl")
107 | if (!requireNamespace("remotes", quietly = TRUE)) install.packages("remotes")
108 | shell: Rscript {0}
109 |
110 | - uses: r-lib/actions/setup-pandoc@v2
111 |
112 | - name: Install system dependencies
113 | if: runner.os == 'Linux'
114 | run: |
115 | sudo apt-get update -y
116 | Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "22.04")); package <- "${{ matrix.package }}"; deps <- tools::package_dependencies(package, which = "Suggests")[[1]]; lapply(c(package, deps), function(x) { writeLines(remotes::system_requirements("ubuntu", "22.04", package = x)) })' | sort | uniq > .github/deps.sh
117 | cat .github/deps.sh
118 | sudo sh < .github/deps.sh
119 |
120 | - name: Install package
121 | run: |
122 | package <- "${{ matrix.package }}"
123 | install.packages(package, dependencies = TRUE)
124 | remotes::install_cran("rcmdcheck")
125 | shell: Rscript {0}
126 |
127 | - name: Session info old
128 | run: |
129 | options(width = 100)
130 | if (!requireNamespace("sessioninfo", quietly = TRUE)) install.packages("sessioninfo")
131 | pkgs <- installed.packages()[, "Package"]
132 | sessioninfo::session_info(pkgs, include_base = TRUE)
133 | shell: Rscript {0}
134 |
135 | # Begin custom: after install
136 | # End custom: after install
137 |
138 | - name: Check old
139 | env:
140 | _R_CHECK_CRAN_INCOMING_: false
141 | _R_CHECK_SYSTEM_CLOCK_: false
142 | _R_CHECK_FUTURE_FILE_TIMESTAMPS_: false
143 | # Avoid downloading binary package from RSPM
144 | run: |
145 | package <- "${{ matrix.package }}"
146 | options(HTTPUserAgent = "gha")
147 | path <- download.packages(package, destdir = ".github")[, 2]
148 | print(path)
149 |
150 | dir <- file.path("revdep", package)
151 | dir.create(dir, showWarnings = FALSE, recursive = TRUE)
152 | check <- rcmdcheck::rcmdcheck(path, args = c("--no-manual", "--as-cran"), error_on = "never", check_dir = file.path(dir, "check"))
153 | file.rename(file.path(dir, "check"), file.path(dir, "old"))
154 | saveRDS(check, file.path(dir, "old.rds"))
155 | shell: Rscript {0}
156 |
157 | - name: Install local package
158 | run: |
159 | remotes::install_local(".", force = TRUE)
160 | shell: Rscript {0}
161 |
162 | - name: Session info new
163 | run: |
164 | options(width = 100)
165 | pkgs <- installed.packages()[, "Package"]
166 | sessioninfo::session_info(pkgs, include_base = TRUE)
167 | shell: Rscript {0}
168 |
169 | - name: Check new
170 | env:
171 | _R_CHECK_CRAN_INCOMING_: false
172 | _R_CHECK_SYSTEM_CLOCK_: false
173 | _R_CHECK_FUTURE_FILE_TIMESTAMPS_: false
174 | run: |
175 | package <- "${{ matrix.package }}"
176 | path <- dir(".github", pattern = paste0("^", package), full.names = TRUE)[[1]]
177 | print(path)
178 |
179 | dir <- file.path("revdep", package)
180 | check <- rcmdcheck::rcmdcheck(path, args = c("--no-manual", "--as-cran"), error_on = "never", check_dir = file.path(dir, "check"))
181 | file.rename(file.path(dir, "check"), file.path(dir, "new"))
182 | saveRDS(check, file.path(dir, "new.rds"))
183 | shell: Rscript {0}
184 |
185 | - name: Compare
186 | run: |
187 | package <- "${{ matrix.package }}"
188 | dir <- file.path("revdep", package)
189 | old <- readRDS(file.path(dir, "old.rds"))
190 | new <- readRDS(file.path(dir, "new.rds"))
191 | compare <- rcmdcheck::compare_checks(old, new)
192 | compare
193 | cmp <- compare$cmp
194 | if (!identical(cmp[cmp$which == "old", "output"], cmp[cmp$which == "new", "output"])) {
195 | if (!requireNamespace("waldo", quietly = TRUE)) install.packages("waldo")
196 | print(waldo::compare(old, new))
197 |
198 | stop("Check output differs.")
199 | }
200 | shell: Rscript {0}
201 |
202 | - name: Upload check results
203 | if: failure()
204 | uses: actions/upload-artifact@main
205 | with:
206 | name: ${{ matrix.package }}-results
207 | path: revdep/${{ matrix.package }}
208 |
209 | - name: Check rate limits
210 | if: always()
211 | run: |
212 | curl -s --header "authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/rate_limit
213 | shell: bash
214 |
--------------------------------------------------------------------------------
/docs/pkgdown.css:
--------------------------------------------------------------------------------
1 | /* Sticky footer */
2 |
3 | /**
4 | * Basic idea: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/
5 | * Details: https://github.com/philipwalton/solved-by-flexbox/blob/master/assets/css/components/site.css
6 | *
7 | * .Site -> body > .container
8 | * .Site-content -> body > .container .row
9 | * .footer -> footer
10 | *
11 | * Key idea seems to be to ensure that .container and __all its parents__
12 | * have height set to 100%
13 | *
14 | */
15 |
16 | html, body {
17 | height: 100%;
18 | }
19 |
20 | body {
21 | position: relative;
22 | }
23 |
24 | body > .container {
25 | display: flex;
26 | height: 100%;
27 | flex-direction: column;
28 | }
29 |
30 | body > .container .row {
31 | flex: 1 0 auto;
32 | }
33 |
34 | footer {
35 | margin-top: 45px;
36 | padding: 35px 0 36px;
37 | border-top: 1px solid #e5e5e5;
38 | color: #666;
39 | display: flex;
40 | flex-shrink: 0;
41 | }
42 | footer p {
43 | margin-bottom: 0;
44 | }
45 | footer div {
46 | flex: 1;
47 | }
48 | footer .pkgdown {
49 | text-align: right;
50 | }
51 | footer p {
52 | margin-bottom: 0;
53 | }
54 |
55 | img.icon {
56 | float: right;
57 | }
58 |
59 | /* Ensure in-page images don't run outside their container */
60 | .contents img {
61 | max-width: 100%;
62 | height: auto;
63 | }
64 |
65 | /* Fix bug in bootstrap (only seen in firefox) */
66 | summary {
67 | display: list-item;
68 | }
69 |
70 | /* Typographic tweaking ---------------------------------*/
71 |
72 | .contents .page-header {
73 | margin-top: calc(-60px + 1em);
74 | }
75 |
76 | dd {
77 | margin-left: 3em;
78 | }
79 |
80 | /* Section anchors ---------------------------------*/
81 |
82 | a.anchor {
83 | display: none;
84 | margin-left: 5px;
85 | width: 20px;
86 | height: 20px;
87 |
88 | background-image: url(./link.svg);
89 | background-repeat: no-repeat;
90 | background-size: 20px 20px;
91 | background-position: center center;
92 | }
93 |
94 | h1:hover .anchor,
95 | h2:hover .anchor,
96 | h3:hover .anchor,
97 | h4:hover .anchor,
98 | h5:hover .anchor,
99 | h6:hover .anchor {
100 | display: inline-block;
101 | }
102 |
103 | /* Fixes for fixed navbar --------------------------*/
104 |
105 | .contents h1, .contents h2, .contents h3, .contents h4 {
106 | padding-top: 60px;
107 | margin-top: -40px;
108 | }
109 |
110 | /* Navbar submenu --------------------------*/
111 |
112 | .dropdown-submenu {
113 | position: relative;
114 | }
115 |
116 | .dropdown-submenu>.dropdown-menu {
117 | top: 0;
118 | left: 100%;
119 | margin-top: -6px;
120 | margin-left: -1px;
121 | border-radius: 0 6px 6px 6px;
122 | }
123 |
124 | .dropdown-submenu:hover>.dropdown-menu {
125 | display: block;
126 | }
127 |
128 | .dropdown-submenu>a:after {
129 | display: block;
130 | content: " ";
131 | float: right;
132 | width: 0;
133 | height: 0;
134 | border-color: transparent;
135 | border-style: solid;
136 | border-width: 5px 0 5px 5px;
137 | border-left-color: #cccccc;
138 | margin-top: 5px;
139 | margin-right: -10px;
140 | }
141 |
142 | .dropdown-submenu:hover>a:after {
143 | border-left-color: #ffffff;
144 | }
145 |
146 | .dropdown-submenu.pull-left {
147 | float: none;
148 | }
149 |
150 | .dropdown-submenu.pull-left>.dropdown-menu {
151 | left: -100%;
152 | margin-left: 10px;
153 | border-radius: 6px 0 6px 6px;
154 | }
155 |
156 | /* Sidebar --------------------------*/
157 |
158 | #pkgdown-sidebar {
159 | margin-top: 30px;
160 | position: -webkit-sticky;
161 | position: sticky;
162 | top: 70px;
163 | }
164 |
165 | #pkgdown-sidebar h2 {
166 | font-size: 1.5em;
167 | margin-top: 1em;
168 | }
169 |
170 | #pkgdown-sidebar h2:first-child {
171 | margin-top: 0;
172 | }
173 |
174 | #pkgdown-sidebar .list-unstyled li {
175 | margin-bottom: 0.5em;
176 | }
177 |
178 | /* bootstrap-toc tweaks ------------------------------------------------------*/
179 |
180 | /* All levels of nav */
181 |
182 | nav[data-toggle='toc'] .nav > li > a {
183 | padding: 4px 20px 4px 6px;
184 | font-size: 1.5rem;
185 | font-weight: 400;
186 | color: inherit;
187 | }
188 |
189 | nav[data-toggle='toc'] .nav > li > a:hover,
190 | nav[data-toggle='toc'] .nav > li > a:focus {
191 | padding-left: 5px;
192 | color: inherit;
193 | border-left: 1px solid #878787;
194 | }
195 |
196 | nav[data-toggle='toc'] .nav > .active > a,
197 | nav[data-toggle='toc'] .nav > .active:hover > a,
198 | nav[data-toggle='toc'] .nav > .active:focus > a {
199 | padding-left: 5px;
200 | font-size: 1.5rem;
201 | font-weight: 400;
202 | color: inherit;
203 | border-left: 2px solid #878787;
204 | }
205 |
206 | /* Nav: second level (shown on .active) */
207 |
208 | nav[data-toggle='toc'] .nav .nav {
209 | display: none; /* Hide by default, but at >768px, show it */
210 | padding-bottom: 10px;
211 | }
212 |
213 | nav[data-toggle='toc'] .nav .nav > li > a {
214 | padding-left: 16px;
215 | font-size: 1.35rem;
216 | }
217 |
218 | nav[data-toggle='toc'] .nav .nav > li > a:hover,
219 | nav[data-toggle='toc'] .nav .nav > li > a:focus {
220 | padding-left: 15px;
221 | }
222 |
223 | nav[data-toggle='toc'] .nav .nav > .active > a,
224 | nav[data-toggle='toc'] .nav .nav > .active:hover > a,
225 | nav[data-toggle='toc'] .nav .nav > .active:focus > a {
226 | padding-left: 15px;
227 | font-weight: 500;
228 | font-size: 1.35rem;
229 | }
230 |
231 | /* orcid ------------------------------------------------------------------- */
232 |
233 | .orcid {
234 | font-size: 16px;
235 | color: #A6CE39;
236 | /* margins are required by official ORCID trademark and display guidelines */
237 | margin-left:4px;
238 | margin-right:4px;
239 | vertical-align: middle;
240 | }
241 |
242 | /* Reference index & topics ----------------------------------------------- */
243 |
244 | .ref-index th {font-weight: normal;}
245 |
246 | .ref-index td {vertical-align: top; min-width: 100px}
247 | .ref-index .icon {width: 40px;}
248 | .ref-index .alias {width: 40%;}
249 | .ref-index-icons .alias {width: calc(40% - 40px);}
250 | .ref-index .title {width: 60%;}
251 |
252 | .ref-arguments th {text-align: right; padding-right: 10px;}
253 | .ref-arguments th, .ref-arguments td {vertical-align: top; min-width: 100px}
254 | .ref-arguments .name {width: 20%;}
255 | .ref-arguments .desc {width: 80%;}
256 |
257 | /* Nice scrolling for wide elements --------------------------------------- */
258 |
259 | table {
260 | display: block;
261 | overflow: auto;
262 | }
263 |
264 | /* Syntax highlighting ---------------------------------------------------- */
265 |
266 | pre, code, pre code {
267 | background-color: #f8f8f8;
268 | color: #333;
269 | }
270 | pre, pre code {
271 | white-space: pre-wrap;
272 | word-break: break-all;
273 | overflow-wrap: break-word;
274 | }
275 |
276 | pre {
277 | border: 1px solid #eee;
278 | }
279 |
280 | pre .img, pre .r-plt {
281 | margin: 5px 0;
282 | }
283 |
284 | pre .img img, pre .r-plt img {
285 | background-color: #fff;
286 | }
287 |
288 | code a, pre a {
289 | color: #375f84;
290 | }
291 |
292 | a.sourceLine:hover {
293 | text-decoration: none;
294 | }
295 |
296 | .fl {color: #1514b5;}
297 | .fu {color: #000000;} /* function */
298 | .ch,.st {color: #036a07;} /* string */
299 | .kw {color: #264D66;} /* keyword */
300 | .co {color: #888888;} /* comment */
301 |
302 | .error {font-weight: bolder;}
303 | .warning {font-weight: bolder;}
304 |
305 | /* Clipboard --------------------------*/
306 |
307 | .hasCopyButton {
308 | position: relative;
309 | }
310 |
311 | .btn-copy-ex {
312 | position: absolute;
313 | right: 0;
314 | top: 0;
315 | visibility: hidden;
316 | }
317 |
318 | .hasCopyButton:hover button.btn-copy-ex {
319 | visibility: visible;
320 | }
321 |
322 | /* headroom.js ------------------------ */
323 |
324 | .headroom {
325 | will-change: transform;
326 | transition: transform 200ms linear;
327 | }
328 | .headroom--pinned {
329 | transform: translateY(0%);
330 | }
331 | .headroom--unpinned {
332 | transform: translateY(-100%);
333 | }
334 |
335 | /* mark.js ----------------------------*/
336 |
337 | mark {
338 | background-color: rgba(255, 255, 51, 0.5);
339 | border-bottom: 2px solid rgba(255, 153, 51, 0.3);
340 | padding: 1px;
341 | }
342 |
343 | /* vertical spacing after htmlwidgets */
344 | .html-widget {
345 | margin-bottom: 10px;
346 | }
347 |
348 | /* fontawesome ------------------------ */
349 |
350 | .fab {
351 | font-family: "Font Awesome 5 Brands" !important;
352 | }
353 |
354 | /* don't display links in code chunks when printing */
355 | /* source: https://stackoverflow.com/a/10781533 */
356 | @media print {
357 | code a:link:after, code a:visited:after {
358 | content: "";
359 | }
360 | }
361 |
362 | /* Section anchors ---------------------------------
363 | Added in pandoc 2.11: https://github.com/jgm/pandoc-templates/commit/9904bf71
364 | */
365 |
366 | div.csl-bib-body { }
367 | div.csl-entry {
368 | clear: both;
369 | }
370 | .hanging-indent div.csl-entry {
371 | margin-left:2em;
372 | text-indent:-2em;
373 | }
374 | div.csl-left-margin {
375 | min-width:2em;
376 | float:left;
377 | }
378 | div.csl-right-inline {
379 | margin-left:2em;
380 | padding-left:1em;
381 | }
382 | div.csl-indent {
383 | margin-left: 2em;
384 | }
385 |
--------------------------------------------------------------------------------
/docs/reference/index.html:
--------------------------------------------------------------------------------
1 |
2 | 
as_assertion_factory()
63 | Any() Logical() Integer() Double() Character() Raw() List() Null() Closure() Special() Builtin() Environment() Symbol() Pairlist() Language() Expression() Function() Factor() Matrix() Array() Data.frame() Date() Time() Dots()
68 | check_output() check_arg()
73 | process_assertion_factory_dots()
83 | use_typed()
88 | Use ? to set a function's return type, argument types, or variable types
127 | in the body of the function.
`?`(lhs, rhs) 131 | 132 | check_output(.output, .assertion, ...) 133 | 134 | check_arg(.arg, .assertion, ..., .bind = FALSE) 135 | 136 | declare(x, assertion, value, const = FALSE)137 | 138 |
| lhs | 143 |lhs |
144 |
|---|---|
| rhs | 147 |rhs |
148 |
| .output | 151 |function output |
152 |
| .assertion | 155 |an assertion |
156 |
| ... | 159 |additional arguments passed to assertion |
160 |
| .arg | 163 |function argument |
164 |
| .bind | 167 |whether to actively bind the argument so it cannot be modified 168 | unless it satisfies the assertion |
169 |
| x | 172 |variable name as a string |
173 |
| assertion | 176 |a function |
177 |
| value | 180 |an optional value |
181 |
| const | 184 |whether to declare |
185 |
206 |numeric ? function (x= ?numeric) { 191 | numeric ? y <- 2 192 | res <- x + y 193 | res 194 | }#> # typed function 195 | #> function (x) 196 | #> { 197 | #> check_arg(x, numeric) 198 | #> declare("y", numeric, value = 2) 199 | #> res <- x + y 200 | #> check_output(res, numeric) 201 | #> } 202 | #> <environment: 0x000000002b89e240> 203 | #> # Return type: numeric 204 | #> # Arg types: 205 | #> # x: numeric
11 |
12 | *{typed}* implements a type system for R, it has 3 main features:
13 |
14 | - set variable types in a script or the body of a function, so they
15 | can’t be assigned illegal values
16 | - set argument types in a function definition
17 | - set return type of a function
18 |
19 | The user can define their own types, or leverage assertions from other
20 | packages.
21 |
22 | Under the hood variable types use active bindings, so once a variable is
23 | restricted by an assertion, it cannot be modified in a way that would
24 | not satisfy it.
25 |
26 | ## Installation
27 |
28 | Install CRAN version with:
29 |
30 | ``` r
31 | install.packages("typed")
32 | ```
33 |
34 | or development version with :
35 |
36 | ``` r
37 | remotes::install_github("moodymudskipper/typed")
38 | ```
39 |
40 | And attach with :
41 |
42 | ``` r
43 | # masking warning about overriding `?`
44 | library(typed, warn.conflicts = FALSE)
45 | ```
46 |
47 | ## Set variable type
48 |
49 | ### Question mark notation and `declare`
50 |
51 | Here are examples on how we would set types
52 |
53 | ``` r
54 | Character() ? x # restrict x to "character" type
55 | x <- "a"
56 | x
57 | #> [1] "a"
58 |
59 | Integer(3) ? y <- 1:3 # restrict y to "integer" type of length 3
60 | y
61 | #> [1] 1 2 3
62 | ```
63 |
64 | We cannot assign values of the wrong type to `x` and `y` anymore.
65 |
66 | ``` r
67 | x <- 2
68 | #> Error: type mismatch
69 | #> `typeof(value)`: "double"
70 | #> `expected`: "character"
71 |
72 | y <- 4:5
73 | #> Error: length mismatch
74 | #> `length(value)`: 2
75 | #> `expected`: 3
76 | ```
77 |
78 | But the right type will work.
79 |
80 | ``` r
81 | x <- c("b", "c")
82 |
83 | y <- c(1L, 10L, 100L)
84 | ```
85 |
86 | `declare` is a strict equivalent, slightly more efficient, which looks
87 | like `base::assign`.
88 |
89 | ``` r
90 | declare("x", Character())
91 | x <- "a"
92 | x
93 | #> [1] "a"
94 |
95 | declare("y", Integer(3), 1:3)
96 | y
97 | #> [1] 1 2 3
98 | ```
99 |
100 | ### Assertion factories and assertions
101 |
102 | `Integer` and `Character` are function factories (functions that return
103 | functions), thus `Integer(3)` and `Character()` are functions.
104 |
105 | The latter functions operate checks on a value and in case of success
106 | return this value, generally unmodified. For instance :
107 |
108 | ``` r
109 | Integer(3)(1:2)
110 | #> Error: length mismatch
111 | #> `length(value)`: 2
112 | #> `expected`: 3
113 |
114 | Character()(3)
115 | #> Error: type mismatch
116 | #> `typeof(value)`: "double"
117 | #> `expected`: "character"
118 | ```
119 |
120 | We call `Integer(3)` and `Character()` assertions, and we call `Integer`
121 | and `Character` assertion factories (or just types, with then we must be
122 | careful not to confuse them with atomic types returned by the `typeof`
123 | function).
124 |
125 | The package contains many assertion factories (see
126 | `?assertion_factories`), the main ones are:
127 |
128 | - `Any` (No default restriction)
129 | - `Logical`
130 | - `Integer`
131 | - `Double`
132 | - `Character`
133 | - `List`
134 | - `Environment`
135 | - `Factor`
136 | - `Matrix`
137 | - `Data.frame`
138 | - `Date`
139 | - `Time` (POSIXct)
140 |
141 | ### Advanced type restriction using arguments
142 |
143 | As we’ve seen with `Integer(3)`, passing arguments to a assertion
144 | factory restricts the type.
145 |
146 | For instance `Integer` has arguments `length` `null_ok` and `...`. We
147 | already used `length`, `null_ok` is convenient to allow a default `NULL`
148 | value in addition to the `"integer"` type.
149 |
150 | The arguments can differ between assertion factories, for instance
151 | `Data.frame` has `nrow`, `ncol`, `each`, `null_ok` and `...`
152 |
153 | ``` r
154 | Data.frame() ? x <- iris
155 | Data.frame(ncol = 2) ? x <- iris
156 | #> Error: Column number mismatch
157 | #> `ncol(value)`: 5
158 | #> `expected`: 2
159 | Data.frame(each = Double()) ? x <- iris
160 | #> Error: column 5 ("Species") type mismatch
161 | #> `typeof(value)`: "integer"
162 | #> `expected`: "double"
163 | ```
164 |
165 | In the dots we can use arguments named as functions and with the value
166 | of the expected result.
167 |
168 | ``` r
169 | # Integer has no anyNA arg but we can still use it because a function named
170 | # this way exists
171 | Integer(anyNA = FALSE) ? x <- c(1L, 2L, NA)
172 | #> Error: `anyNA` mismatch
173 | #> `anyNA(value)`: TRUE
174 | #> `expected`: FALSE
175 | ```
176 |
177 | Useful arguments might be for instance, `anyDuplicated = 0L`,
178 | `names = NULL`, `attributes = NULL`… Any available function can be used.
179 |
180 | That makes assertion factories very flexible! If it is still not
181 | flexible enough, we can provide arguments arguments named `...` to
182 | functional factories to add a custom restriction, this is usually better
183 | done by defining a wrapper.
184 |
185 | ``` r
186 | Character(1, ... = "`value` is not a fruit!" ~ . %in% c("apple", "pear", "cherry")) ?
187 | x <- "potatoe"
188 | #> Error: `value` is not a fruit!
189 | #> `value %in% c("apple", "pear", "cherry")`: FALSE
190 | #> `expected`: TRUE
191 | ```
192 |
193 | This is often better done by defining a wrapper as shown below.
194 |
195 | ### Constants
196 |
197 | To define a constant, we just surround the variable by parentheses
198 | (think of them as a protection)
199 |
200 | ``` r
201 | Double() ? (x) <- 1
202 | x <- 2
203 | #> Error: Can't assign to a constant
204 |
205 | # defining a type is optional
206 | ? (y) <- 1
207 | y <- 2
208 | #> Error: Can't assign to a constant
209 | ```
210 |
211 | ### Set a function’s argument type
212 |
213 | We can set argument types this way :
214 |
215 | ``` r
216 | add <- ? function (x= ? Double(), y= 1 ? Double()) {
217 | x + y
218 | }
219 | ```
220 |
221 | Note that we started the definition with a `?`, and that we gave a
222 | default to `y`, but not `x`. Note also the `=` sign next to `x`,
223 | necessary even when we have no default value. If you forget it you’ll
224 | have an error “unexpected `?` in …”.
225 |
226 | This created the following function, by adding checks at the top of the
227 | body
228 |
229 | ``` r
230 | add
231 | #> # typed function
232 | #> function (x, y = 1)
233 | #> {
234 | #> check_arg(x, Double())
235 | #> check_arg(y, Double())
236 | #> x + y
237 | #> }
238 | #> # Arg types:
239 | #> # x: Double()
240 | #> # y: Double()
241 | ```
242 |
243 | Let’s test it by providing a right and wrong type.
244 |
245 | ``` r
246 | add(2, 3)
247 | #> [1] 5
248 | add(2, 3L)
249 | #> Error: In `add(2, 3L)` at `check_arg(y, Double())`:
250 | #> wrong argument to function, type mismatch
251 | #> `typeof(value)`: "integer"
252 | #> `expected`: "double"
253 | ```
254 |
255 | If we want to restrict `x` and `y` to the type “integer” in the rest of
256 | the body, so they cannot be overwritten by character for instance,we can
257 | use the `?+` notation :
258 |
259 | ``` r
260 | add <- ? function (x= ?+ Double(), y= 1 ?+ Double()) {
261 | x + y
262 | }
263 |
264 | add
265 | #> # typed function
266 | #> function (x, y = 1)
267 | #> {
268 | #> check_arg(x, Double(), .bind = TRUE)
269 | #> check_arg(y, Double(), .bind = TRUE)
270 | #> x + y
271 | #> }
272 | #> # Arg types:
273 | #> # x: Double()
274 | #> # y: Double()
275 | ```
276 |
277 | We see that it is translated into a `check_arg` call containing a
278 | `.bind = TRUE` argument.
279 |
280 | ## Set a function’s return type
281 |
282 | To set a return type we use `?` before the function definition as in the
283 | previous section, but we type an assertion on the left hand side.
284 |
285 | ``` r
286 | add_or_subtract <- Double() ? function (x, y, subtract = FALSE) {
287 | if(subtract) return(x - y)
288 | x + y
289 | }
290 | add_or_subtract
291 | #> # typed function
292 | #> function (x, y, subtract = FALSE)
293 | #> {
294 | #> if (subtract)
295 | #> return(check_output(x - y, Double()))
296 | #> check_output(x + y, Double())
297 | #> }
298 | #> # Return type: Double()
299 | ```
300 |
301 | We see that the returned values have been wrapped inside `check_output`
302 | calls.
303 |
304 | # Use type in a package and define your own types
305 |
306 | See `vignette("typed-in-packaged", "typed")` or the Article section if
307 | you’re browsing the pkgdown website.
308 |
309 | ## Acknowledgements
310 |
311 | This is inspired in good part by Jim Hester and Gabor Csardi’s work and
312 | many great efforts on static typing, assertions, or annotations in R, in
313 | particular:
314 |
315 | - Gabor Csardy’s [*argufy*](https://github.com/gaborcsardi/argufy)
316 | - Richie Cotton’s
317 | [*assertive*](https://bitbucket.org/richierocks/assertive/)
318 | - Tony Fishettti’s [*assertr*](https://github.com/tonyfischetti/assertr)
319 | - Hadley Wickham’s [*assertthat*](https://github.com/hadley/assertthat)
320 | - Michel Lang’s [*checkmate*](https://github.com/mllg/checkmate)
321 | - Joe Thorley’s [*checkr*](https://github.com/poissonconsulting/checkr)
322 | - Joe Thorley’s [*chk*](https://github.com/poissonconsulting/chk/)
323 | - Aviral Goel’s [*contractr*](https://github.com/aviralg/contractr)
324 | - Stefan Bache’s [*ensurer*](https://github.com/smbache/ensurer)
325 | - Brian Lee Yung Rowe’s
326 | [*lambda.r*](https://github.com/zatonovo/lambda.r)
327 | - Kun Ren’s [*rtype*](https://github.com/renkun-ken/rtype)
328 | - Duncan Temple Lang’s
329 | [*TypeInfo*](https://bioconductor.org/packages/TypeInfo/)
330 | - Jim Hester’s [*types*](https://github.com/jimhester/types)
331 |
--------------------------------------------------------------------------------