Написание надежного кода R: пространства имен, маскирование и использование `::` оператор

Краткая версия

Для тех, кто не хочет читать мой "кейс", вот суть:

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

    а) просто с использованиемпредоставленных пакетов (скажем, только в каком-то R Analysis Project)?

    b) в отношении разработкисобственных пакетов?

  3. Как лучше всего избежать конфликтов в отношении формальных классов(в основном ссылочных классовв моем случае), поскольку нет даже механизма пространства имен, сравнимого с ::для классов (AFAIU)?


Как работает вселенная R

Это то, что не дает мне покоя уже около двух лет, но я не чувствую, что нашел удовлетворительное решение. К тому же я чувствую, что становится хуже.

Мы видим постоянно растущее количество пакетов на CRAN, github, R-Forgeи им подобных, что просто потрясающе.

В такой децентрализованной среде естественно, что кодовая база, составляющая R (скажем, базовая Rи добавленная R, для простоты) будет отклоняться от идеальной. состояние относительно надежности: люди следуют разным соглашениям, есть S3, S4, эталонные классы S4 и т. д. Вещи не могут быть такими «выровненными», как если бы существовал «центральный клиринговый экземпляр» которые навязывали соглашения. Это нормально.

Проблема

Учитывая вышеизложенное, может быть очень сложно использовать R для написания надежного кода. Не все, что вам нужно, будет в базе R.Для некоторых проектов вы в конечном итоге загрузите довольно много пакетов.

ИМХО, самая большая проблема в этом отношении заключается в том, как концепция пространства имен используется в R: R позволяет просто написать имя определенной функции/метода без явного требования его пространства имен (например, fooпротив namespace::foo).

Итак, для простоты все так и делают. Но в этом случае конфликты имен, сломанный код и необходимость переписывать/рефакторить ваш код — это просто вопрос времени (или количества загруженных пакетов).

В лучшем случае вы будете знатьо том, какие существующие функции маскируются/перегружаются новым добавленным пакетом. В худшем случае вы ничего не поймете, пока ваш код не сломается.

Несколько примеров:

  • попробуйте загрузить RMySQLи RSQLiteодновременно, они не очень хорошо работают
  • также RMongoперезапишет некоторые функции RMySQL
  • прогноз маскирует много вещей, связанных с функциями, связанными с ARIMA
  • R.utilsдаже маскирует base::parseподпрограмма

(я не могу вспомнить, какие именно функции вызывали проблемы, но я готов просмотреть их снова, если будет интерес)

Удивительно, но это, похоже, не беспокоит многих программистов. там. Я пару раз пытался поднять интерес на r-devel, но без особого успеха.

Недостатки использования оператора ::

  1. Использование оператора ::может значительно снизить эффективность в определенных контекстах, как указал Доминик Сампери .
  2. Когда разрабатываетесвой собственный пакет, вы даже не можете использовать оператор ::в своем собственном коде, поскольку ваш код еще не является настоящим пакетом и, следовательно, еще нет пространства имен. . Поэтому мне пришлось бы сначала придерживаться пути foo, собрать, протестировать, а затем вернуться к изменению всего на namespace::foo. Не совсем.

Возможные решения, позволяющие избежать этих проблем.

  1. Переназначитькаждую функцию из каждого пакета переменной, соответствующей определенным соглашениям об именах, например namespace..foo, чтобы избежать неэффективности, связанной с namespace::foo(я описал это однажды здесь). Плюсы: это работает. Минусы: это неуклюже, и вы удваиваете используемую память.
  2. Моделируйтепространство имен при разработке пакета. AFAIU, на самом деле это невозможно, по крайней мере, мне так сказали тогда.
  3. Сделать обязательнымиспользование namespace::foo. ИМХО, это было бы лучше всего. Конечно, мы потеряли бы некоторую степень простоты, но опять же, вселенная R просто больше не проста (по крайней мере, она не так проста, как в начале 00-х).

А как насчет (формальных) классов?

Помимо аспектов, описанных выше, способ ::достаточно хорошо работает для функций/методов. Но как насчет определений классов?

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

Что-то вроде этого не будет работать:

new(timeDate::timeDate)
new("timeDate::timeDate")
new("timeDate", ns="timeDate")

Это может быть огромной проблемой, так как все больше и больше людей переключаются на стиль ООП для своих пакетов R, что приводит к большому количеству определений классов. Если есть способ явного обращения к пространству имен определения класса, я был бы очень признателен за указатель!

Заключение

Несмотря на то, что это было немного длинно, я надеюсь, что смог указать на основную проблему/вопрос и что я могу привлечь больше внимания здесь.

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

45
задан Rappster 12 June 2012 в 11:56
поделиться