Could you give a little more detail on this [...]? [...] Typically, the
default scoping rules are sufficient to resolve these [...].
I agree these conflicts can be solved when spotted. And certainly more easily
so if there were a dedicated currying syntax in base R as Ivan mentioned.
However I think both users and package authors would benefit from being able
to prevent the collisions altogether.
To collect some data on the prevalence, I analyzed the 387 installed packages
on my machine, including 23 433 functions. Of those, 2 585 (11%) both accepted
... and had a "mangled" first argument name (one that did not start with a
lower case letter), indicating that the function might have benefited from
the availability of a positional-only parameter syntax.
This is realistic to implement. In addition to changes in gram.y (or,
perhaps, to the declare() special interface for giving extra instructions to
the parser that was suggested for declaring arguments for NSE) to mark the
formals as positional-only, the argument matching mechanism in
src/main/match.c:matchArgs_NR will need to be changed to take the flag
into account.
Thanks, Ivan, for the pointers. Following them I was able to put together a...
let's say proof of concept patch for this, included below. With the patch[1]
we indeed have for example:
g <- function(x, f, /, ...) match.call()
g(1, f, x = 2) == quote(g(1, f, x = 2))
Or:
my_lapply <- function(x, f, /, ...) {
res <- vector("list", length(x))
for (i in seq_along(x)) {
res[[i]] <- f(x[[i]], ...)
}
res
}
add <- function(x, y) x + y
my_lapply(1:5, add, x = 1)
Best wishes,
Mikko
[1]: Compiled with `RUN_BISON=1 make all recommended` on Windows, as it took
me a painful while to figure out.
Index: src/main/gram.y
===================================================================
--- src/main/gram.y (revision 85797)
+++ src/main/gram.y (working copy)
@@ -557,6 +557,7 @@
formlist: { $$ = xxnullformal(); }
| SYMBOL { $$ = xxfirstformal0($1); modif_token( &@1, SYMBOL_FORMALS ) ; }
| SYMBOL EQ_ASSIGN expr_or_help { $$ = xxfirstformal1($1,$3); modif_token( &@1, SYMBOL_FORMALS ) ; modif_token( &@2, EQ_FORMALS ) ; }
+ | formlist ',' '/' { $$ = xxaddformal0($1,$3, &@3); modif_token( &@3, SYMBOL_FORMALS ) ; }
| formlist ',' SYMBOL { $$ = xxaddformal0($1,$3, &@3); modif_token( &@3, SYMBOL_FORMALS ) ; }
| formlist ',' SYMBOL EQ_ASSIGN expr_or_help
{ $$ = xxaddformal1($1,$3,$5,&@3); modif_token( &@3, SYMBOL_FORMALS ) ; modif_token( &@4, EQ_FORMALS ) ;}
Index: src/main/match.c
===================================================================
--- src/main/match.c (revision 85797)
+++ src/main/match.c (working copy)
@@ -185,10 +185,13 @@
{
Rboolean seendots;
int i, arg_i = 0;
+ int nfargposonly = 0;
SEXP f, a, b, dots, actuals;
actuals = R_NilValue;
for (f = formals ; f != R_NilValue ; f = CDR(f), arg_i++) {
+ /* Get count of positional-only formal arguments */
+ if (TAG(f) == Rf_install("/")) nfargposonly = arg_i + 1;
/* CONS_NR is used since argument lists created here are only
used internally and so should not increment reference
counts */
@@ -218,6 +221,7 @@
a = actuals;
arg_i = 0;
while (f != R_NilValue) {
+ if (arg_i >= nfargposonly) {
SEXP ftag = TAG(f);
const char *ftag_name = CHAR(PRINTNAME(ftag));
if (ftag != R_DotsSymbol && ftag != R_NilValue) {
@@ -241,6 +245,7 @@
}
}
}
+ }
}
f = CDR(f);
a = CDR(a);
@@ -257,7 +262,7 @@
a = actuals;
arg_i = 0;
while (f != R_NilValue) {
- if (fargused[arg_i] == 0) {
+ if (fargused[arg_i] == 0 && arg_i >= nfargposonly) {
if (TAG(f) == R_DotsSymbol && !seendots) {
/* Record where ... value goes */
dots = a;
@@ -310,6 +315,10 @@
seendots = TRUE;
f = CDR(f);
a = CDR(a);
+ } else if (TAG(f) == Rf_install("/")) {
+ /* Ignore positional-only marker */
+ f = CDR(f);
+ a = CDR(a);
} else if (CAR(a) != R_MissingArg) {
/* Already matched by tag */
/* skip to next formal */
Index: src/main/unique.c
===================================================================
--- src/main/unique.c (revision 85797)
+++ src/main/unique.c (working copy)
@@ -1919,10 +1919,14 @@
/* Attach the argument names as tags */
- for (f = formals, b = rlist; b != R_NilValue; b = CDR(b), f = CDR(f)) {
- SET_TAG(b, TAG(f));
+ int nfargposonly = 0, arg_i = 0;
+ for (f = formals ; f != R_NilValue ; f = CDR(f), arg_i++) {
+ if (TAG(f) == Rf_install("/")) nfargposonly = arg_i + 1;
}
+ for (f = formals, b = rlist, arg_i = 0; b != R_NilValue; b = CDR(b), f = CDR(f), arg_i++) {
+ if (arg_i >= nfargposonly) SET_TAG(b, TAG(f));
+ }
/* Handle the dots */