Краткая версия
Для тех, кто не хочет читать мой "случай", в этом суть:
- Каков рекомендуемый способ минимизации возможностей новых пакетов, нарушающих существующий код, т.е. создания кода, который вы пишете как можно более надежным?
-
Каков рекомендуемый способ наилучшего использования механизма пространства имен, когда
a) просто используя внесенные пакеты (скажем, только в каком-то R-аналитическом проекте)?
b) в отношении разработки собственных пакетов?
-
Как лучше избегать конфликтов в отношении формальных классов (в основном Reference Classes в моем случае) поскольку нет даже механизма пространства имен, сравнимого с
::
для классов (AFAIU)?
Как работает вселенная R
Это то, что было во рту в моей голове около двух лет, но я не чувствую, что я пришел к удовлетворительному решению. Плюс я чувствую, что все хуже.
Мы видим все большее количество пакетов на CRAN, github, R-Forge и тому подобное, что просто потрясающе.
В такой децентрализованной среде естественно, что база кода, которая составляет R (скажем, что база R и способствовала R, для простоты) будет отклоняться от идеального состояния в отношении надежности: люди следуют различным соглашениям, там S3, S4, S4 и т.д. Вещи не могут быть "выровнены" так, как они были бы, если бы существовал "центральный клиринговый экземпляр", который применял соглашения. Это хорошо.
Проблема
Учитывая вышеизложенное, очень сложно использовать R для написания надежного кода. Не все, что вам нужно, будет в базе R. Для некоторых проектов вы загрузите немало пакетов.
IMHO, самая большая проблема в этом отношении - это то, как концепция пространства имен используется для использования в R: R, позволяет просто писать имя определенной функции/метода, явно не требуя этого пространства имен (т.е. foo
vs. namespace::foo
).
Итак, ради простоты, что все делают. Но таким образом, столкновения имен, сломанный код и необходимость перезаписи/реорганизации кода - это всего лишь вопрос времени (или количества загруженных пакетов).
В лучшем случае вы знаете, о том, какие существующие функции маскируются/перегружаются недавно добавленным пакетом. В худшем случае у вас не будет никакой подсказки, пока ваш код не сломается.
Несколько примеров:
- попробуйте загрузить RMySQL и RSQLite в то же время, они не идут очень хорошо
- также RMongo перезапишет некоторые функции RMySQL
- forecast маскирует много вещей в отношении связанных с ARIMA функций
- R.utils даже маскирует процедуру
base::parse
(Я не могу вспомнить, какие функции, в частности, вызывали проблемы, но я хочу снова просмотреть его, если есть интерес)
Удивительно, но это, похоже, не беспокоит многих программистов. Я пытался несколько раз поднять интерес к r-devel, без каких-либо существенных преимуществ.
Недостатки использования оператора ::
- Использование оператора
::
может значительно снизить эффективность в определенных контекстах, поскольку указал Dominick Samperi . - Когда разрабатывает собственный пакет, вы даже не можете использовать оператор
::
на своем собственном коде, так как ваш код еще не является реальным пакетом и, тем самым, еще нет пространства имен. Поэтому мне пришлось бы сначала придерживаться методаfoo
, строить, тестировать, а затем вернуться к изменению всего наnamespace::foo
. Не совсем.
Возможные решения для устранения этих проблем
- Переназначить каждую функцию из каждого пакета в переменную, которая следует за определенными соглашениями об именах, например.
namespace..foo
, чтобы избежать неэффективности, связанной сnamespace::foo
(я изложил ее один раз здесь). Плюсы: он работает. Минусы: он неуклюжий, и вы удваиваете используемую память. - Имитировать пространство имен при разработке пакета. AFAIU, это на самом деле невозможно, по крайней мере, я был так сказал тогда.
- Сделать обязательным для использования
namespace::foo
. ИМХО, это было бы лучше всего. Конечно, мы потеряли бы некоторую часть простоты, но опять же вселенная R теперь просто не проста (по крайней мере, это не так просто, как в начале 00-х годов).
А как насчет (формальных) классов?
Помимо аспектов, описанных выше, способ ::
отлично работает для функций/методов. Но как насчет определений классов?
Возьмите пакет timeDate с его классом timeDate
. Произнесите еще один пакет, который также имеет класс timeDate
. Я не вижу, как я могу явно указать, что мне нужен новый экземпляр класса timeDate
из любого из двух пакетов.
Что-то вроде этого не будет работать:
new(timeDate::timeDate)
new("timeDate::timeDate")
new("timeDate", ns="timeDate")
Это может быть огромной проблемой, поскольку все больше и больше людей переключаются на OOP-стиль для своих R-пакетов, что приводит к множеству определений классов. Если есть способ явного обращения к пространству имен определения класса, я бы очень признателен за указатель!
Заключение
Несмотря на то, что это было немного долго, я надеюсь, что смогу указать основную проблему/вопрос и что я могу повысить понимание здесь.
Я думаю, devtools и mvbutils есть некоторые подходы, которые могут стоить распространения, но я уверен, что еще можно сказать.