File: README.md

package info (click to toggle)
r-cran-bindr 0.1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 132 kB
  • sloc: sh: 13; makefile: 2
file content (220 lines) | stat: -rw-r--r-- 6,227 bytes parent folder | download
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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220

<!-- README.md is generated from README.Rmd. Please edit that file -->

# bindr

<!-- badges: start -->

[![Lifecycle:
stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html)
[![R build
status](https://github.com/krlmlr/bindr/workflows/rcc/badge.svg)](https://github.com/krlmlr/bindr/actions)
[![Coverage
Status](https://img.shields.io/codecov/c/github/krlmlr/bindr/master.svg)](https://app.codecov.io/github/krlmlr/bindr?branch=master)
[![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/bindr)](https://cran.r-project.org/package=bindr)
<!-- badges: end -->

Active bindings in R are much like properties in other languages: They
look like a variable, but querying or setting the value triggers a
function call. They can be created in R via
[`makeActiveBinding()`](https://www.rdocumentation.org/packages/base/versions/3.3.1/topics/bindenv),
but with this API the function used to compute or change the value of a
binding cannot take additional arguments. The `bindr` package faciliates
the creation of active bindings that are linked to a function that
receives the binding name, and an arbitrary number of additional
arguments.

## Installation

You can install `bindr` from GitHub with:

``` r
# install.packages("devtools")
devtools::install_github("krlmlr/bindr")
```

## Getting started

For illustration, the `append_random()` function is used. This function
appends a separator (a dash by default) and a random letter to its
input, and talks about it, too.

``` r
set.seed(20161510)
append_random <- function(x, sep = "-") {
  message("Evaluating append_random(sep = ", deparse(sep), ")")
  paste(x, sample(letters, 1), sep = sep)
}

append_random("a")
#> Evaluating append_random(sep = "-")
#> [1] "a-h"
append_random("X", sep = "+")
#> Evaluating append_random(sep = "+")
#> [1] "X+k"
```

In this example, we create an environment that contains bindings for all
lowercase letters, which are evaluated with `append_random()`. As a
result, a dash and a random letter are appended to the name of the
binding:

``` r
library(bindr)
env <- create_env(letters, append_random)
ls(env)
#>  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
#> [20] "t" "u" "v" "w" "x" "y" "z"
env$a
#> Evaluating append_random(sep = "-")
#> [1] "a-s"
env$a
#> Evaluating append_random(sep = "-")
#> [1] "a-h"
env$a
#> Evaluating append_random(sep = "-")
#> [1] "a-c"
env$c
#> Evaluating append_random(sep = "-")
#> [1] "c-o"
env$Z
#> NULL
```

Bindings can also be added to existing environments:

``` r
populate_env(env, LETTERS, append_random, "+")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
#> Evaluating append_random(sep = "-")
env$a
#> Evaluating append_random(sep = "-")
#> [1] "a-q"
env$Z
#> Evaluating append_random(sep = "+")
#> [1] "Z+c"
```

## Further properties

Both named and unnamed arguments are supported:

``` r
create_env("binding", paste, "value", sep = "-")$binding
#> [1] "binding-value"
```

A parent environment can be specified for creation:

``` r
env2 <- create_env("a", identity, .enclos = env)
env2$a
#> a
env2$b
#> NULL
get("b", env2)
#> Evaluating append_random(sep = "-")
#> [1] "b-t"
```

The bindings by default have access to the calling environment:

``` r
create_local_env <- function(names) {
  paste_with_dash <- function(...) paste(..., sep = "-")
  binder <- function(name, append) paste_with_dash(name, append)
  create_env(names, binder, append = "appending")
}

env3 <- create_local_env("a")
env3$a
#> [1] "a-appending"
```

All bindings are read-only:

``` r
env3$a <- NA
#> Error: Binding is read-only.
env3$a <- NULL
#> Error: Binding is read-only.
```

Existing variables or bindings are not overwritten:

``` r
env4 <- as.environment(list(a = 5))
populate_env(env4, list(quote(b)), identity)
ls(env4)
#> [1] "a" "b"
populate_env(env4, letters, identity)
#> Error in populate_env(env4, letters, identity): Not creating bindings for existing variables: b, a
```

## Active bindings and C++

Active bindings must be R functions. To interface with C++ code, one
must bind against an exported Rcpp function, possibly with `rng = false`
if performance matters. The
[`bindrcpp`](https://github.com/krlmlr/bindrcpp#readme) package uses
`bindr` to provide an easy-to-use C++ interface for parametrized active
bindings, and is the recommended way to interface with C++ code. In the
remainder of this section, an alternative using an exported C++ function
is shown.

The following C++ module exports a function
`change_case(to_upper = FALSE)`, which is bound against in R code later.

``` cpp
#include <Rcpp.h>

#include <algorithm>
#include <string>

using namespace Rcpp;

// [[Rcpp::export(rng = FALSE)]]
SEXP change_case(Symbol name, bool to_upper = false) {
  std::string name_string = name.c_str();
  std::transform(name_string.begin(), name_string.end(),
                 name_string.begin(), to_upper ? ::toupper : ::tolower);
  return CharacterVector(name_string);
}
```

Binding from R:

``` r
env <- create_env(list(as.name("__ToLower__")), change_case)
populate_env(env, list(as.name("__tOuPPER__")), change_case, TRUE)
ls(env)
#> [1] "__ToLower__" "__tOuPPER__"
env$`__ToLower__`
#> [1] "__tolower__"
get("__tOuPPER__", env)
#> [1] "__TOUPPER__"
```