Существуют ли лучшие / рекомендуемые методы, которым необходимо следовать при переименовании функций в новой версии пакета?
Я обновляю старый пакет и сокращаю кучу действительно длинных имен функций. Как я могу сообщить пользователю, что старая функция устарела? Я документирую все с 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.
Я думаю, что "правильный" ответ зависит от того, что вы хотите. С моей точки зрения:
- Проблема с подходом Джеффа и Брэндона состоит в том, что в вашем индексе будут перечислены оба имени функций и не будет указано, какое имя является предпочтительным. Более того, без какого-либо вызова.Deprecated пользователь еще менее вероятно узнает, какой предпочтительный способ вызова функции.
- Проблема с подходом Брайана состояла в том, что процесс перечисления более чем одной функции как устаревшей был мне неясен.
Итак, введите мой пример ниже. В другом месте я определяю "хорошие" версии функций (например, 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
,