В Wed, 10 Dec 2025 22:40:29 -0800
Henrik Bengtsson <[email protected]> пишет:

> What's going on here:
> 
> $ R --vanilla --quiet
> > f <- function(n) rep("x", times = n)
> > f()  
> [1] "x"

Let's take these R functions:

not_rep <- compiler::cmpfun(\(x, times = 1) invisible(times))
wrap_not_rep <- compiler::cmpfun(\(times) not_rep(1, times = times))

In a call from wrap_not_rep() to not_rep(1, times = times), the thing
being matched against the formals is a promise to evaluate 'times' in
the wrap_not_rep()'s environment, so Rf_matchArgs_NR matches it
successfully and not_rep() eventually forces the promise and signals an
argument-is-missing exception.

What about the following function?

wrap_rep <- compiler::cmpfun(\(times) rep(1, times = times))

Being a pritmitive, rep() performs its own argument matching, so in a
call from wrap_rep() to rep(1, times = times), things happen
differently.

First, it receives the "raw" arguments, without them being wrapped into
promises:

(gdb) call R_inspect(args)
@5555585b2220 02 LISTSXP g1c0 [MARK,REF(2)]
  @5555585a5700 14 REALSXP g1c1 [MARK,REF(65535)] (len=1, tl=0) 1
  TAG: @555557d17458 01 SYMSXP g1c0 [MARK,REF(65535)] "times"
  @555557d17458 01 SYMSXP g1c0 [MARK,REF(65535)] "times"

Then it calls DispatchOrEval(), which evaluates the expressions in the
call, replacing the "times" symbol with R_MissingArg. Only then it
calls Rf_matchArgs_NR:

(gdb) call R_inspect(supplied)
@5555588f74a8 02 LISTSXP g0c0 [STP] 
  @5555585a5700 14 REALSXP g0c1 [REF(65535)] (len=1, tl=0) 1
  TAG: @555557d17458 01 SYMSXP g0c0 [REF(65535)] "times"
  @5555559c6320 01 SYMSXP g0c0 [MARK,REF(7008)] [missing argument]

Rf_matchArgs_NR then faithfully substitutes that missing argument into
the return value together with other missing arguments that it
substitutes for every value that wasn't passed at all:

(gdb) call R_inspect(args)
@5555588fc850 02 LISTSXP g0c0 [STP] 
  @5555585a5700 14 REALSXP g0c1 [REF(65535)] (len=1, tl=0) 1
  # this one was passed as missing
  @5555559c6320 01 SYMSXP g0c0 [MARK,REF(7010)] [missing argument]
  # the following ones weren't passed at all
  @5555559c6320 01 SYMSXP g0c0 [MARK,REF(7010)] [missing argument]
  @5555559c6320 01 SYMSXP g0c0 [MARK,REF(7010)] [missing argument]
  @5555559c6320 01 SYMSXP g0c0 [MARK,REF(7010)] [missing argument]

There doesn't seem to be a way to distinguish between the two kinds of
missing arguments in the return value.

In order to cause an argument-is-missing error, rep() would have to
walk the list of supplied arguments before matching them, replacing the
R_MissingArg with something else, then match the arguments, then
complain about the missing argument replacement being matched to a
non-dots formal argument. "To call this an ugly hack would be to insult
all existing ugly hacks at large in the world." Is there a better way?

-- 
Best regards,
Ivan

______________________________________________
[email protected] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel

Reply via email to