Schreiben von robustem R-Code: Namespaces, Maskieren und Verwenden des Operators `::`

Kurze Version

Für diejenigen, die meinen "Fall" nicht durchlesen wollen, ist dies die Essenz:

Was ist die empfohlene Methode, um das Risiko zu minimieren, dass neue Pakete den vorhandenen Code beschädigen, d. H. Den von Ihnen geschriebenen Code erstellen?so robust wie möglich?

Was ist der empfohlene Weg, um dieNamespace-Mechanismus wann

a) einfachmit beitragte Pakete (sagen wir in nur einigen R-Analyseprojekten)?

b) in Bezug aufEntwicklung eigene Pakete?

Wie vermeide ich Konflikte in Bezug aufformelle Klassen (meistensReferenzklassen in meinem Fall), da es nicht einmal einen vergleichbaren Namespace-Mechanismus gibt:: für Klassen (AFAIU)?

Die Art und Weise, wie das R-Universum funktioniert

Das ist etwas, das mich seit ungefähr zwei Jahren beschäftigt, aber ich habe nicht das Gefühl, zu einer zufriedenstellenden Lösung gekommen zu sein. Außerdem fühle ich, dass es schlimmer wird.

Wir sehen immer mehr Pakete aufCRAN, Github, R-Forge und dergleichen, was einfach grandios ist.

In einer solchen dezentralen Umgebung ist es selbstverständlich, dass die Codebasis, aus der R besteht (sagen wir mal, das istBasis R undbeigetragen R(der Einfachheit halber) von einem Idealzustand in Bezug auf Robustheit abweichen: Die Menschen folgen unterschiedlichen Konventionen, es gibt S3-, S4-, S4-Referenzklassen usw. Die Dinge können nicht so "ausgerichtet" sein, wie sie wären, wenn es eine gäbe.zentrale Verrechnungsinstanz"Das hat Konventionen durchgesetzt. Das ist okay.

Das Problem

In Anbetracht dessen kann es sehr schwierig sein, mit R robusten Code zu schreiben. Nicht alles, was Sie brauchen, befindet sich in Basis R. Für bestimmte Projekte werden Sie am Ende einige beitragende Pakete laden.

Meiner Meinung nach ist das größte Problem in dieser Hinsicht die Art und Weise, wie das Namespace-Konzept in R verwendet wird: R ermöglicht es, einfach den Namen einer bestimmten Funktion / Methode zu schreiben, ohne ihren Namespace explizit zu fordern (d. H.foo gegennamespace::foo).

Der Einfachheit halber ist es das, was jeder tut. Auf diese Weise sind Namenskonflikte, fehlerhafter Code und die Notwendigkeit, Ihren Code neu zu schreiben / umzugestalten, nur eine Frage der Zeit (oder der Anzahl der geladenen Pakete).

Bestenfalls wirst dukennt darüber, welche vorhandenen Funktionen durch ein neu hinzugefügtes Paket maskiert / überladen werden. Im schlimmsten Fall haben Sie keine Ahnung, bis Ihr Code kaputt geht.

Einige Beispiele:

versuchen Sie es zu ladenRMySQL undRSQLite Gleichzeitig kommen sie nicht sehr gut zurechtebenfallsRMongo überschreibt bestimmte Funktionen vonRMySQLPrognose maskiert eine Menge Dinge in Bezug auf ARIMA-bezogene FunktionenR.utils maskiert sogar diebase::parse Routine

(Ich kann mich nicht erinnern, welche Funktionen die Probleme verursacht haben, bin aber bereit, sie bei Interesse erneut nachzuschlagen.)

Überraschenderweise scheint dies nicht viele Programmierer zu stören. Ich habe ein paar Mal versucht, Interesse zu weckenr-develohne nennenswerten Erfolg.

Nachteile der Verwendung der:: OperatorVerwendung der:: Der Bediener kann die Effizienz in bestimmten Situationen erheblich beeinträchtigen, wie beispielsweise bei Dominick Samperiwies darauf hin.WannEntwicklung Ihr eigenes Paket können Sie nicht einmal verwenden:: Operator in Ihrem eigenen Code, da Ihr Code noch kein echtes Paket ist und es daher auch noch keinen Namespace gibt. Also müsste ich mich erstmal an die haltenfoo Weise, baue, teste und gehe dann zurück, um alles zu ändernnamespace::foo. Nicht wirklich.Mögliche Lösungen zur Vermeidung dieser ProblemeNeu zuweisen jede Funktion von jedem Paket zu einer Variablen, die bestimmten Namenskonventionen folgt, z.namespace..foo um die damit verbundenen Ineffizienzen zu vermeidennamespace::foo (Ich habe es einmal skizziertHier). Vorteile: es funktioniert. Nachteile: Es ist unbeholfen und Sie verdoppeln den verwendeten Speicher.Simulieren ein Namespace bei der Entwicklung Ihres Pakets. AFAIU, das ist nicht wirklich möglich, zumindest war ich dasDas habe ich damals schon gesagt.Mach esverpflichtend benutzennamespace::foo. IMHO, das wäre das Beste. Sicher, wir würden etwas an Einfachheit verlieren, aber andererseits ist das R-Universum einfach nicht mehr einfach (zumindest nicht so einfach wie in den frühen 00ern).Und was ist mit (formellen) Klassen?

Abgesehen von den oben beschriebenen Aspekten:: funktioniert übrigens ganz gut für funktionen / methoden. Aber was ist mit Klassendefinitionen?

Paket nehmenZeit Datum mit seiner KlassetimeDate. Angenommen, es kommt ein anderes Paket, das ebenfalls eine Klasse hattimeDate. Ich verstehe nicht, wie ich ausdrücklich sagen könnte, dass ich eine neue Instanz der Klasse möchtetimeDate aus einem der beiden Pakete.

So etwas funktioniert nicht:

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

Dies kann ein großes Problem sein, da immer mehr Benutzer für ihre R-Pakete auf einen OOP-Stil wechseln, was zu einer Vielzahl von Klassendefinitionen führt. Wenn daist Um den Namespace einer Klassendefinition explizit ansprechen zu können, würde ich mich über einen Zeiger sehr freuen!

Fazit

Obwohl dies etwas langwierig war, hoffe ich, dass ich auf das Kernproblem / die Kernfrage hinweisen konnte und dass ich hier mehr Bewusstsein schaffen kann.

Meiner Ansicht nachDevtools undmvbutils Es gibt zwar einige Ansätze, die es wert sind, verbreitet zu werden, aber ich bin mir sicher, dass es noch mehr zu sagen gibt.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage