По умолчанию аргумент функции, имя которой соответствует имени существующей функции - PullRequest
0 голосов
/ 11 января 2019

Есть ли способ предотвратить ошибку promise already under evaluation, когда

  1. вы хотите, чтобы имя аргумента функции было именем существующей функции
  2. и вы хотите установить значение по умолчанию для этого конкретного аргумента
  3. и вы хотите иметь возможность вызывать внешнюю функцию только с ее значениями по умолчанию (т.е. без необходимости передавать явное значение каждому аргументу)?

В моем примере ниже, пока foo(1:5, bar) работает, foo(1:5) выдает такую ​​ошибку.

Конечно, я мог бы пойти и изменить имя аргумента с bar, скажем, на bar_fun, но я бы предпочел придерживаться фактического имени функции, если это возможно.

foo <- function(x, bar = bar) {
  bar(x)
}
bar <- function(x) {
  UseMethod("bar")
}
bar.default <- function(x) {
  sum(x)
}
foo(1:5)
#> Error in foo(1:5): promise already under evaluation: recursive default argument reference or earlier problems?
foo(1:5, bar)
#> [1] 15

Мотивация (первый заказ)

В реальных условиях использования bar() на самом деле settings(), функция, которая возвращает список настроек. Я хотел бы изменить эти настройки. Так будет, например, такие методы, как settings.v1, settings.v2, ... и settings.default. И я подумал об использовании settings.default для определения «используемой версии» используемых настроек, например ::

settings <- function(x) {
  UseMethod("settings")
}

settings.v1 <- function(x) {
  list(system = "dev")
}

settings.v2 <- function(x) {
  list(system = "production")
}

settings.default <- function(x) {
  settings.v2(
}

foo <- function(x, settings = settings) {
  settings()
}

foo()
#> Error in foo(): promise already under evaluation: recursive default argument reference or earlier problems?
foo(settings = settings)
#> $system
#> [1] "production"

Поскольку settings.default() вызывает метод настроек, который я хочу использовать, было бы замечательно, если бы я мог просто вызвать foo() с его настройками по умолчанию (который затем всегда выбирал бы метод settings.default()).

Мотивация (второй заказ)

Я экспериментирую с более строгим соблюдением принципов функционального программирования (см., Например, главу из Advanced R или ссылка на википедию ) и ее различие pure и эффективных / побочных эффектов функций.

Раньше я, вероятно, реализовывал бы настройки через какую-то глобальную переменную 1054 *, к которой, таким образом, каждый foo() имел доступ, поэтому я мог быть ленивым и не определять его как аргумент функции foo(), но foo() тогда будет зависеть от вещей за пределами его области видимости - что очень плохо для FP.

Теперь я хочу по крайней мере заявить о зависимости foo() от моих настроек, передав ей функцию, которая возвращает значения настроек - что является своего рода ленивостью, по крайней мере, в какой-то степени отвечающей принципам FP верхнего уровня.

Конечно, не ленивое (и, возможно, лучшее) решение состояло бы в том, чтобы аккуратно указывать все фактические зависимости настроек один за другим в качестве аргументов функции в foo(), например. foo(settings_system = settings()$system); -)

1 Ответ

0 голосов
/ 11 января 2019

1) Попробуйте явно получить его от родителя:

foo <- function(x, bar = get("bar", 1)) {
  bar(x)
}

2) Другая возможность - использовать имя аргумента, например bar.. Пользователь все еще может написать foo(1:15, bar = whatever), например, любой из этих трех вызовов работает:

foo <- function(x, bar. = bar) {
  bar.(x)
}

foo(1:5)
foo(1:5, bar) 
foo(1:5, bar = bar)
...