Почему некоторые примитивы имеют байтовые коды, а некоторые нет?

Я заметил, что когда я вызываю args в некоторые из примитивных функций, появляются также байт-коды. Но на других примитивах не появляется байт-код. Например

args(length)
# function (x) 
# NULL
args(list)
# function (...) 
# NULL
# <bytecode: 0x44a0f38>

Почему это?

Сначала я думал, что это может быть связано с аргументом ..., но следующее опровергает эту теорию.

args(dim)
# function (x) 
# NULL
args(unclass)
# function (x) 
# NULL
# <bytecode: 0x44a0450>

Мне кажется, что байт-код появляется только в некоторых из них, а не в других. У меня всегда создавалось впечатление, что все примитивы являются особенными и что все они имеют одни и те же "атрибуты" (из-за отсутствия лучшего слова, а не фактических атрибутов R).

Ответ 1

Как отмечалось, это странность, связанная с тем, как args печатает вещи. То есть, args включает строку байт-кода в своем выводе, не является надежным индикатором того, была ли функция скомпилирована. Для сравнения:

args(writeLines)
## function (text, con = stdout(), sep = "\n", useBytes = FALSE) 
##   NULL

writeLines
## function (text, con = stdout(), sep = "\n", useBytes = FALSE) 
## {
##   if (is.character(con)) {
##     con <- file(con, "w")
##     on.exit(close(con))
##   }
##   .Internal(writeLines(text, con, sep, useBytes))
## }
## <bytecode: 0x000000001bf3aeb0>

Мы можем сравнить печать строки байт-кода для args и стандартной печати функций.

arg_shows_bytecode <- function(fn)
{
  output <- capture.output(args(fn))
  grepl("^<bytecode", output[length(output)])
}

printing_shows_bytecode <- function(fn)
{
  output <- capture.output(print(fn))
  length(output) > 1 && grepl("^<bytecode", output[length(output) - 1])   
}

base_fns <- Filter(is.function, mget(ls(baseenv()), baseenv()))
yn_args <- vapply(base_fns, arg_shows_bytecode, logical(1))
yn_print <- vapply(base_fns, printing_shows_bytecode, logical(1))

Стоит отметить, что все функции, в которых args показывает информацию о байт-коде, являются примитивами.

head(base_fns[yn_args])
## $`%*%`
## function (x, y)  .Primitive("%*%")
## 
## $as.call
## function (x)  .Primitive("as.call")
## 
## $attr
## function (x, which, exact = FALSE)  .Primitive("attr")
## 
## $`attr<-`
## function (x, which, value)  .Primitive("attr<-")
## 
## $attributes
## function (obj)  .Primitive("attributes")
## 
## $`attributes<-`
## function (obj, value)  .Primitive("attributes<-")

Обратное неверно: некоторые базовые функции, в которых args не отображает информацию о байт-коде, являются примитивами; другие - нет.

yn_prim <- vapply(base_fns, is.primitive, logical(1))
table(yn_args, yn_print, yn_prim)
## , , yn_prim = FALSE
## 
##        yn_print
## yn_args FALSE TRUE
## FALSE       0  988
## TRUE        0    0
## 
## , , yn_prim = TRUE
## 
##        yn_print
## yn_args FALSE TRUE
## FALSE     119    0
## TRUE       63    0

Таким образом, не примитивные функции в базовом пакете компилируются, но args не упоминает об этом. Примитивные функции не отображают сообщение с байт-кодом при печати и иногда иногда вызывают сообщение байт-кода при вызове с помощью args.

Ответ 2

Спасибо за отчет. Такое поведение непреднамеренно (ошибка, как говорит Хэдли), он не является внутренне непротиворечивым, поскольку адрес байт-кода отображается только для встроенных и специальных функций и только тогда, когда их форматы находятся в .ArgsEnv (они также могут быть в .GenericArgsEnv). Теперь исправлено в R-devel. Отчеты об ошибках лучше всего направляются прямо в R bugzilla (также работает список рассылки R-devel).