Существуют ли лучшие / рекомендуемые методы, которым необходимо следовать при переименовании функций в новой версии пакета?

Я обновляю старый пакет и сокращаю кучу действительно длинных имен функций. Как я могу сообщить пользователю, что старая функция устарела? Я документирую все с roxygen2 так что мне интересно, если #' @alias это то, что я должен использовать? Мысли?

4 ответа

Решение

Даже если вы просто сокращаете имена функций, я бы все равно отнесся к ним с той же фанфарой, что и к любым изменениям открытого API пакета: с этапами устаревания / отмены старых функций по мере поступления новых функций.

На первом этапе для каждой функции вы хотите сократить имя (давайте назовем его transmute_my_carefully_crafted_data_structure_into_gold), вы сохраняете функцию с этой сигнатурой, но перемещаете весь фактический код в свою вновь названную функцию (давайте назовем ее alchemy).

Первоначально:

transmute_my_carefully_crafted_data_structure_into_gold <- function(lead, alpha=NULL, beta=3) {
  # TODO: figure out how to create gold
  # look like we are doing something
  Sys.sleep(10)
  return("gold")
}

Первый выпуск с новыми именами:

transmute_my_carefully_crafted_data_structure_into_gold <- function(lead, alpha=NULL, beta=3) {
  .Deprecated("alchemy") #include a package argument, too
  alchemy(lead=lead, alpha=alpha, beta=beta)
}

alchemy <- function(lead, alpha=NULL, beta=3) {
  # TODO: figure out how to create gold
  # look like we are doing something
  Sys.sleep(10)
  return("gold")
}

Чтобы transmute_my_carefully_crafted_data_structure_into_gold начинается как тонкая оболочка вокруг alchemyс дополнительным .Deprecated вызов.

> transmute_my_carefully_crafted_data_structure_into_gold()
[1] "gold"
Warning message:
'transmute_my_carefully_crafted_data_structure_into_gold' is deprecated.
Use 'alchemy' instead.
See help("Deprecated") 
> alchemy()
[1] "gold"

Если вы внесете изменения в alchemyэто все еще осуществляется transmute_my_carefully_crafted_data_structure_into_gold так как это просто называет первое. Тем не менее, вы не меняете подпись transmute_my_carefully_crafted_data_structure_into_gold даже если alchemy делает; в этом случае вам необходимо как можно лучше отобразить старые аргументы в новые аргументы.

В более позднем выпуске вы можете изменить .Deprecated в .Defunct,

> transmute_my_carefully_crafted_data_structure_into_gold()
Error: 'transmute_my_carefully_crafted_data_structure_into_gold' is defunct.
Use 'alchemy' instead.
See help("Defunct")

Обратите внимание, что это ошибка и она останавливается; это не идти вперед и звонить alchemy,

В более позднем выпуске вы могли бы полностью удалить эту функцию, но я бы оставил ее в этом состоянии в качестве указателя.

Вы упомянули использование roxygen. Когда вы делаете первый переход на устаревший, вы можете изменить @rdname на пакетный устаревший, добавить строку в начале описания о том, что он устарел, добавить новую функцию в @seealso. Когда он изменится на несуществующий, измените @rdname на package-defunct.

Я думаю, что "правильный" ответ зависит от того, что вы хотите. С моей точки зрения:

  1. Проблема с подходом Джеффа и Брэндона состоит в том, что в вашем индексе будут перечислены оба имени функций и не будет указано, какое имя является предпочтительным. Более того, без какого-либо вызова.Deprecated пользователь еще менее вероятно узнает, какой предпочтительный способ вызова функции.
  2. Проблема с подходом Брайана состояла в том, что процесс перечисления более чем одной функции как устаревшей был мне неясен.

Итак, введите мой пример ниже. В другом месте я определяю "хорошие" версии функций (например, alchemy, latinSquareDigram). Здесь я определяю все старые "плохие" версии, для которых я хочу создавать предупреждения об устаревании. Я следовал подходу пакета автомобилей и изменил все свои вызовы функций для устаревшей версии, чтобы использовать... в качестве аргумента. Это помогло мне избежать кучки беспорядочных операторов @param. Я также использовал директивы @name и @docType для того, чтобы "yourPackageName-deprecated" появилось в индексе. Может быть, у кого-то есть лучший способ сделать это?

Теперь каждая из устаревших функций по-прежнему отображается в индексе, но рядом с ними написано "Устаревшие функции в пакете yourPackageName", и любые вызовы к ним вызывают предупреждение об устаревании. Чтобы удалить их из индекса, можно удалить директиву @aliases, но тогда у вас будут недокументированные объекты кода на уровне пользователя, что, как я понимаю, является плохой формой.

#' Deprecated function(s) in the yourPackageName package
#' 
#' These functions are provided for compatibility with older version of
#' the yourPackageName package.  They may eventually be completely
#' removed.
#' @rdname yourPackageName-deprecated
#' @name yourPackageName-deprecated
#' @param ... Parameters to be passed to the modern version of the function
#' @docType package
#' @export  latinsquare.digram Conv3Dto2D Conv2Dto3D dist3D.l
#' @aliases latinsquare.digram Conv3Dto2D Conv2Dto3D dist3D.l
#' @section Details:
#' \tabular{rl}{
#'   \code{latinsquare.digram} \tab now a synonym for \code{\link{latinSquareDigram}}\cr
#'   \code{Conv3Dto2D} \tab now a synonym for \code{\link{conv3Dto2D}}\cr
#'   \code{Conv2Dto3D} \tab now a synonym for \code{\link{conv2Dto3D}}\cr
#'   \code{dist3D.l} \tab now a synonym for \code{\link{dist3D}}\cr
#' }
#'  
latinsquare.digram <- function(...) {
  .Deprecated("latinSquareDigram",package="yourPackageName")
  latinSquareDigram(...)
}
Conv3Dto2D <- function(...) {
  .Deprecated("conv3Dto2D",package="yourPackageName")
  conv3Dto2D(...)
}
Conv2Dto3D <- function(...) {
  .Deprecated("conv2Dto3D",package="yourPackageName")
  conv2Dto3D(...)
}
dist3D.l <- function(...) {
  .Deprecated("dist3D",package="yourPackageName")
  dist3D(...)
}
NULL

У меня была эта проблема в течение некоторого времени, и я не смог найти хорошего решения. Тогда я нашел это. Тем не менее, приведенные выше ответы слишком сложны для простого случая, когда нужно просто: 1) добавить псевдоним, чтобы старый код не перестал работать, 2) псевдоним должен работать со встроенной документацией, и 3) его должно быть сделано с roxygen2.

Сначала добавьте копию функции:

old_function_name = new_function_name

Затем, где определено new_function_name(), добавьте в roxygen2:

#' @export new_function_name old_function_name
#' @aliases old_function_name

Теперь старая функция работает, потому что это просто копия новой функции, а документация работает, потому что вы создали псевдоним. Старая версия также экспортируется, потому что она включена в @export.

В случае преобразования слишком длинных имен функций в более короткие версии, я бы порекомендовал просто экспортировать оба имени в одну и ту же функцию (см. Комментарий @Brandon). Это позволит старому коду продолжать работать, предлагая новым пользователям более удобную альтернативу.

На мой взгляд, единственная причина помечать что-то как .Deprecated (см. @GSEE), если вы планируете кардинально изменить функциональность или прекратите поддержку какой-либо функции в следующем выпуске. Если это так, вы можете использовать .Defunct или же .Deprecated,

Другие вопросы по тегам