Escribir código R robusto: espacios de nombres, enmascarar y usar el operador `::`

Version corta

Para aquellos que no quieren leer mi "caso", esta es la esencia:

¿Cuál es la forma recomendada de minimizar las posibilidades de que nuevos paquetes rompan el código existente, es decir, de hacer el código que usted escribe?lo mas robusto posible?

¿Cuál es la forma recomendada de hacer el mejor uso de lamecanismo de espacio de nombres cuando

a) soloutilizando paquetes contribuidos (digamos en solo un Proyecto de Análisis R)?

b) con respecto adesarrollando propios paquetes?

La mejor manera de evitar conflictos con respecto aclases formales (principalmenteClases de referencia en mi caso) ya que ni siquiera hay un mecanismo de espacio de nombres comparable a:: para las clases (AFAIU)?

La forma en que funciona el universo R

Esto es algo que me ha estado molestando en el fondo de mi mente por cerca de dos años, pero no siento que haya llegado a una solución satisfactoria. Además siento que está empeorando.

Vemos un número cada vez mayor de paquetes enCRAN, github, R-Forge y similares, que es simplemente genial.

En un entorno tan descentralizado, es natural que la base del código que compone la R (digamos que esbase R ycontribuido R, por simplicidad) se desviará de un estado ideal con respecto a la robustez: las personas siguen diferentes convenciones, hay clases de referencia S3, S4, S4, etc. Las cosas no pueden estar tan "alineadas" como lo estarían si hubiera un "instancia de compensación central"Eso hizo cumplir las convenciones. Está bien.

El problema

Dado lo anterior, puede ser muy difícil usar R para escribir código robusto. No todo lo que necesita estará en la base R. Para ciertos proyectos, terminará cargando unos cuantos paquetes contribuidos.

En mi humilde opinión, el mayor problema a este respecto es la forma en que el concepto de espacio de nombres se utiliza en R: R permite simplemente escribir el nombre de una determinada función / método sin requerir explícitamente su espacio de nombres (es decir,foo contranamespace::foo).

Entonces, para simplificar, eso es lo que todos están haciendo. Pero de esa manera, los choques de nombres, el código roto y la necesidad de volver a escribir / refactorizar su código son solo cuestión de tiempo (o del número de paquetes diferentes cargados).

En el mejor de los casos, lo harássaber sobre qué funciones existentes están enmascaradas / sobrecargadas por un paquete recién agregado. En el peor de los casos, no tendrá idea hasta que se rompa su código.

Un par de ejemplos:

intenta cargarRMySQL yRSQLite al mismo tiempo, no van muy bienademásRMongo sobrescribirá ciertas funciones deRMySQLpronóstico Enmascara muchas cosas con respecto a las funciones relacionadas con ARIMA.R.utils incluso enmascara elbase::parse rutina

(No puedo recordar qué funciones en particular estaban causando los problemas, pero estoy dispuesto a buscarlo nuevamente si hay interés)

Sorprendentemente, esto no parece molestar a muchos programadores. Traté de aumentar el interés un par de veces enr-devel, sin ningún provecho significativo.

Desventajas de usar el:: operadorUtilizando la:: El operador podría dañar significativamente la eficiencia en ciertos contextos como Dominick Samperiseñalado.Cuandodesarrollando tu propio paquete, ni siquiera puedes usar el:: Operador en todo su propio código, ya que su código aún no es un paquete real y, por lo tanto, tampoco hay un espacio de nombres. Así que inicialmente tendría que atenerme a lafoo forma, construir, probar y luego volver a cambiar todo anamespace::foo. Realmente no.Posibles soluciones para evitar estos problemas.Reasignar cada función de cada paquete a una variable que sigue ciertas convenciones de denominación, por ejemplo,namespace..foo para evitar las ineficiencias asociadas anamespace::foo (Lo describí una vezaquí). Pros: funciona. Contras: es torpe y duplicas la memoria utilizada.Simular un espacio de nombres al desarrollar su paquete. AFAIU, esto no es realmente posible, al menos yo estabalo dijo entonces.Hazloobligatorio usarnamespace::foo. En mi humilde opinión, eso sería lo mejor que se puede hacer. Claro, perderíamos algo de simplicidad, pero nuevamente, el universo R ya no es tan simple (al menos no es tan simple como en los primeros 00).¿Y qué hay de las clases (formales)?

Aparte de los aspectos descritos anteriormente,:: Manera funciona bastante bien para funciones / métodos. Pero ¿qué pasa con las definiciones de clase?

Tomar paquetehora Fecha con su clasetimeDate. Digamos que viene otro paquete que también tiene una clase.timeDate. No veo cómo podría declarar explícitamente que me gustaría una nueva instancia de clasetimeDate de cualquiera de los dos paquetes.

Algo como esto no funcionará:

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

Esto puede ser un gran problema a medida que más y más personas cambian a un estilo OOP para sus paquetes R, lo que lleva a muchas definiciones de clase. Sí hayes una forma de abordar explícitamente el espacio de nombres de una definición de clase, apreciaría mucho un puntero!

Conclusión

A pesar de que esto fue un poco largo, espero poder señalar el problema / la pregunta central y que puedo crear más conciencia aquí.

Yo creo quedevtools ymvbutils tengo algunos enfoques que podrían valer la pena difundir, pero estoy seguro de que hay más que decir.

Respuestas a la pregunta(2)

Su respuesta a la pregunta