Лучшее объяснение того, когда использовать импорт/зависимость

В руководстве " Writing R Extensions " содержатся следующие рекомендации о том, когда использовать импорт или зависимость:

Общие правила

  • Пакеты, чье пространство имен требуется только для загрузки пакета с использованием библиотеки (pkgname), должны быть указаны в поле "Импорт", а не в поле "Зависит".
  • Пакеты, которые необходимо подключить для успешной загрузки пакета с использованием библиотеки (pkgname), должны быть указаны только в поле "Зависит".

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

редактировать

Я написал сообщение в блоге с разделом по этой конкретной теме (поиск "Imports v Depends"). Визуальные эффекты облегчают понимание.

Ответ 1

"Imports" безопаснее, чем "Depends" (а также делает пакет, использующий его, "лучшим гражданином" по сравнению с другими пакетами, использующими "Depends").

Директива "Depends" пытается обеспечить доступность функции из другого пакета, присоединяя другой пакет к основному пути поиска (т.е. К списку сред, возвращаемых функцией search()). Эта стратегия, однако, может быть сорвана, если другой пакет, загруженный позже, поместит функцию с одинаковым именем ранее в путь поиска. Камеры (в соде) использует пример функции "gam", который находится в обоих gam и mgcv пакетов. Если были загружены два других пакета, один из которых зависит от gam а другой - от mgcv, функция, найденная при mgcv gam() будет зависеть от того, в каком порядке эти два пакета были присоединены. Нехорошо.

Директива "Imports" должна использоваться для любого вспомогательного пакета, функции которого должны быть помещены в <imports:packageName> (ищется сразу после <namespace:packageName>), а не в обычном пути поиска. Если какой- либо один из пакетов, в приведенном выше примере используется "Imports" механизм (который также требует import или importFrom директивы в NAMESPACE файле), вопросы можно было бы улучшить двумя способами. (1) Сам пакет получит контроль над тем, mgcv функция mgcv используется. (2) Если очистить основной путь поиска от импортированных объектов, это даже не нарушит зависимость другого пакета от другой функции mgcv.

Вот почему использование пространств имен является такой хорошей практикой, почему теперь оно применяется CRAN, и (в частности) почему использование "Imports" безопаснее, чем "Depends".


Отредактировано, чтобы добавить важное предупреждение:

К сожалению, из приведенного выше совета есть одно общее исключение: если ваш пакет зависит от пакета A который сам "Depends" от другого пакета B, вашему пакету, вероятно, потребуется присоединить A с директивой "Depends.

Это связано с тем, что функции в пакете A были написаны с ожиданием, что пакет B и его функции будут присоединены к пути search().

Директива "Depends" загрузит и присоединит пакет A, после чего собственная директива "Depends" пакета A в цепной реакции вызовет загрузку и присоединение пакета B Функции в пакете A смогут найти функции в пакете B на которые они полагаются.

Директива "Imports" загружает, но не присоединяет пакет A и не загружает и не присоединяет пакет B (В конце концов, "Imports" ожидает, что разработчики пакетов используют механизм пространства имен, и что пакет A будет использовать "Imports" для указания на любые функции в B которым ему необходим доступ.) Вызовы вашими функциями для любых функций в пакет A который полагается на функции в пакете B, следовательно, потерпит неудачу.

Единственные два решения:

  1. Приложите пакет A к пакету A используя директиву "Depends".
  2. Лучше в долгосрочной перспективе, свяжитесь с сопровождающим пакета A и попросите его сделать более осторожную работу по созданию своего пространства имен (по словам Мартина Моргана в этом связанном ответе).

Ответ 2

Хэдли Уикхэм дает легкое объяснение (http://r-pkgs.had.co.nz/namespace.html):

Листинг пакета в Depends или Imports гарантирует, что его если это необходимо. Основное отличие заключается в том, что где Imports просто загружает пакет, Depends присоединяет его. Других различия. [...]

Если в противном случае нет веской причины, вы всегда должны указывать пакеты в Imports не Depends. Это потому, что хороший пакет самодостаточно и минимизирует изменения в глобальной среде (включая путь поиска). Единственное исключение - если ваш пакет предназначенный для использования в сочетании с другим пакетом. Например, аналоговый пакет построен поверх веганов. Это не полезно без веган, поэтому у него веган в Depends вместо Imports. По аналогии, ggplot2 действительно должен зависеть от масштабов, а не от импорта.

Ответ 3

Камеры в SfDA говорят использовать "Импорт", когда этот пакет использует механизм "пространства имен", и поскольку теперь все пакеты должны иметь их, тогда ответ теперь может всегда использоваться "Импорт". В прошлом пакеты могли быть загружены без фактического пространства имен, и в этом случае вам нужно было бы использовать Depends.

Ответ 4

Вот простой вопрос, который поможет вам решить, что использовать:

Требуется ли вашему пакету конечный пользователь иметь прямой доступ к функциям другого пакета?

  • НЕТ → Импорт (наиболее общий ответ)
  • ДА → Зависит

Единственный раз, когда вы должны использовать "Зависимости", является то, что ваш пакет является дополнением или компаньоном к другому пакету, где ваш конечный пользователь будет использовать функции как вашего пакета, так и пакета "Зависимости" в своем коде. Если ваш конечный пользователь будет только взаимодействовать с вашими функциями, а другой пакет будет работать только за кулисами, тогда используйте вместо него "Импорт".

Предостережение заключается в том, что если вы добавите пакет в "Импорт", как обычно, ваш код должен будет ссылаться на функции из этого пакета, используя полный синтаксис пространства имен, например. dplyr::mutate(), а не просто mutate(). Это делает код немного clunkier, чтобы читать, но его небольшая цена, чтобы заплатить за лучшую гигиеничность упаковки.