forked from tidyverse/purrr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathas_mapper.R
151 lines (136 loc) · 4 KB
/
as_mapper.R
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
#' Convert an object into a mapper function
#'
#' `as_mapper` is the powerhouse behind the varied function
#' specifications that most purrr functions allow. It is an S3
#' generic. The default method forwards its arguments to
#' [rlang::as_function()].
#'
#' @param .f A function, formula, or vector (not necessarily atomic).
#'
#' If a __function__, it is used as is.
#'
#' If a __formula__, e.g. `~ .x + 2`, it is converted to a function. There
#' are three ways to refer to the arguments:
#'
#' * For a single argument function, use `.`
#' * For a two argument function, use `.x` and `.y`
#' * For more arguments, use `..1`, `..2`, `..3` etc
#'
#' This syntax allows you to create very compact anonymous functions.
#'
#' If __character vector__, __numeric vector__, or __list__, it is
#' converted to an extractor function. Character vectors index by
#' name and numeric vectors index by position; use a list to index
#' by position and name at different levels. If a component is not
#' present, the value of `.default` will be returned.
#' @param .default,.null Optional additional argument for extractor functions
#' (i.e. when `.f` is character, integer, or list). Returned when
#' value is absent (does not exist) or empty (has length 0).
#' `.null` is deprecated; please use `.default` instead.
#' @param ... Additional arguments passed on to methods.
#' @export
#' @examples
#' as_mapper(~ . + 1)
#' as_mapper(1)
#'
#' as_mapper(c("a", "b", "c"))
#' # Equivalent to function(x) x[["a"]][["b"]][["c"]]
#'
#' as_mapper(list(1, "a", 2))
#' # Equivalent to function(x) x[[1]][["a"]][[2]]
#'
#' as_mapper(list(1, attr_getter("a")))
#' # Equivalent to function(x) attr(x[[1]], "a")
#'
#' as_mapper(c("a", "b", "c"), .null = NA)
as_mapper <- function(.f, ...) {
UseMethod("as_mapper")
}
#' @export
#' @rdname as_mapper
#' @usage NULL
as_function <- function(...) {
stop_defunct(paste_line(
"`as_function()` is defunct as of purrr 0.3.0.",
"Please use `as_mapper()` or `rlang::as_function()` instead"
))
as_mapper(...)
}
#' @export
as_mapper.default <- function(.f, ...) {
if (typeof(.f) %in% c("special", "builtin")) {
.f <- rlang::as_closure(.f)
# Workaround until fixed in rlang
if (is_reference(fn_env(.f), base_env())) {
environment(.f) <- global_env()
}
.f
} else {
rlang::as_function(.f)
}
}
#' @export
#' @rdname as_mapper
as_mapper.character <- function(.f, ..., .null, .default = NULL) {
.default <- find_extract_default(.null, .default)
plucker(as.list(.f), .default)
}
#' @export
#' @rdname as_mapper
as_mapper.numeric <- function(.f, ..., .null, .default = NULL) {
.default <- find_extract_default(.null, .default)
plucker(as.list(.f), .default)
}
#' @export
#' @rdname as_mapper
as_mapper.list <- function(.f, ..., .null, .default = NULL) {
.default <- find_extract_default(.null, .default)
plucker(.f, .default)
}
find_extract_default <- function(.null, .default) {
if (!missing(.null)) {
# warning("`.null` is deprecated; please use `.default` instead", call. = FALSE)
.null
} else {
.default
}
}
plucker <- function(i, default) {
x <- NULL # supress global variables check NOTE
new_function(
exprs(x = , ... = ),
expr(pluck(x, !!!i, .default = !!default)),
env = caller_env()
)
}
as_predicate <- function(.fn, ..., .mapper, .deprecate = FALSE) {
if (.mapper) {
.fn <- as_mapper(.fn, ...)
}
function(...) {
out <- .fn(...)
if (!is_bool(out)) {
msg <- sprintf(
"Predicate functions must return a single `TRUE` or `FALSE`, not %s",
as_predicate_friendly_type_of(out)
)
if (.deprecate) {
msg <- paste_line(
"Returning complex values from a predicate function is soft-deprecated as of purrr 0.3.0.",
msg
)
signal_soft_deprecated(msg)
} else {
abort(msg)
}
}
out
}
}
as_predicate_friendly_type_of <- function(x) {
if (is_na(x)) {
"a missing value"
} else {
friendly_type_of(x, length = TRUE)
}
}