-
Notifications
You must be signed in to change notification settings - Fork 36
/
dotdotdot.R
90 lines (82 loc) · 2.8 KB
/
dotdotdot.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
#' list2 - one day like rlang
#' list2 placeholder for future rust-impl
#' @noRd
#' @return An R list
#' @details rlang has this wonderful list2 implemented in c/c++, that is agnostic about trailing
#' commas in ... params. One day r-polars will have a list2-impl written in rust, which also allows
#' trailing commas.
#' https://github.com/extendr/extendr/pull/578 see progress here.
list2 = function(..., .context = NULL, .call = sys.call(1L)) {
list(...) |>
result() |>
map_err(\(err) {
if (identical(err$contexts()$PlainErrorMessage, "argument is missing, with no default")) {
err$plain("trailing argument commas are not (yet) supported with polars")
} else {
err
}
}) |>
unwrap(context = .context, call = .call)
}
#' Internal unpack list
#' @noRd
#'
#' @param ... args to unwrap
#' @param .context a context passed to unwrap
#' @param .call a call passed to unwrap
#' @param skip_classes char vec, do not unpack list inherits skip_classes.
#'
#' @details py-polars syntax only allows e.g. `df.select([expr1, expr2,])` and not
#' `df.select(expr1, expr2,)`. r-polars also allows user to directly write
#' `df$select(expr1, expr2)` or `df$select(list(expr1,expr2))`. Unpack list
#' checks whether first and only arg is a list and unpacks it, to bridge the
#' allowed patterns of passing expr to methods with ... param input.
#' Will throw an error if trailing comma.
#' @return a list
#' @examples
#' f = \(...) unpack_list(list(...))
#' identical(f(list(1L, 2L, 3L)), f(1L, 2L, 3L)) # is TRUE
#' identical(f(list(1L, 2L), 3L), f(1L, 2L, 3L)) # is FALSE
unpack_list = function(..., .context = NULL, .call = sys.call(1L), skip_classes = NULL) {
l = list2(..., .context = .context, .call = .call)
if (
length(l) == 1L &&
is.list(l[[1L]]) &&
!(!is.null(skip_classes) && inherits(l[[1L]], skip_classes)) &&
is.null(names(l))
) {
l[[1L]]
} else {
l
}
}
#' Convert dot-dot-dot to bool expression
#' @noRd
#' @return Result, a list has `ok` (RPolarsExpr class) and `err` (RPolarsErr class)
#' @examples
#' unpack_bool_expr_result(pl$lit(TRUE), pl$lit(FALSE))
unpack_bool_expr_result = function(...) {
unpack_list(...) |>
result() |>
and_then(\(l) {
if (!is.null(names(l))) {
Err_plain(
"Detected a named input.",
"This usually means that you've used `=` instead of `==`."
)
} else {
l |>
Reduce(`&`, x = _) |>
result() |>
suppressWarnings()
}
})
}
#' Convert dots to a character vector of column names
#' @param .df [RPolarsDataFrame]
#' @param ... Arguments to pass to [`pl$col()`][pl_col]
#' @noRd
dots_to_colnames = function(.df, ..., .call = sys.call(1L)) {
result(pl$DataFrame(schema = .df$schema)$select(pl$col(...))$columns) |>
unwrap(call = .call)
}