Macros sintácticas con R

Creo que muchos hemos tropezado con las macros alguna vez. Yo conocía las del preprocesador de C o el tinglado que tiene SAS. Y nunca fui muy amigo de ellas.

Pero el otro día leí Stop Writing JavaScript Compilers! Make Macros Instead y se me alargaron los dientes. Así que he buscado información adicional hasta hacerme una idea de la diferencia entre una macro que se limita a reemplazar texto, una macro procedural —como las del lenguaje PL/I, antecesor e inspirador de SAS— y las sintácticas, como las que tiene Lisp (¿cuándo tendré tiempo para aprenderlo en condiciones?).

No tengo todavía muy claro hasta dónde pueden llevarme y cuáles de sus usos son convenientes para mis fines. Porque no, yo no construyo sublenguajes o minilenguajes. Pero entiendo que pueden servir para construir código aún más breve y expresivo, como en el siguiente ejemplo:

library(gtools)
 
intercambia <- defmacro(a, b, expr = {
  tmp <- a
  a <- b
  b <- tmp
})
 
x <- 1
y <- 2
 
intercambia(x,y)
 
x
# [1] 2
y
# [1] 1

La función defmacro del paquete gtools permite definir macros sintácticas. Se parecen a funciones, pero no lo son: por ejemplo, el cuerpo de la macro no se ejecuta en un espacio de nombres propio, como las funciones, por lo que el efecto de la asignación de variables perdura más allá de la llamada a la función.

El ejemplo anterior sirve también para ilustrar algunos caveats de las macros (al menos de la actual implementación de R): que no son higiénicas:

tmp <- 7
intercambia(x,y)
tmp
# [1] 2

Como puede verse, la macro, interna y subrepticiamente, ha cambiado el valor de la variable tmp. Una implementación higiénica de defmacro (como la que existe en otros lenguajes) permitiría evitar ese tipo de efectos secundarios.

¿A alguien se le ocurren otros usos de las macros?

5 comentarios sobre “Macros sintácticas con R

  1. David 16 enero, 2014 13:10

    Antes que nada, gracias por mostrarnos el uso de defmacro (sinceramente no lo había visto antes).

    Yendo al tema de tu entrada, para evitar ese efecto «indeseado» (o efecto lateral, como nos enseñaban en la Escuela), debido a las restricciones en el ámbito de la macro, habría que «mejorar» la macro considerando dos principios:
    1.- El nombre de la variable temporal debe ser exclusivo (ej: intercambiaTmp)
    2.- Ejecutar rm(«intercambiaTmp») al final de la macro para no dejar el objeto definido dentro del espacio.

    La alternativa sería considerar si la macro debe ser una función y aprovechar las mejoras de rendimiento que puedan proporcionar sobre las macros.

    Buena entrada, como siempre.

  2. datanalytics 20 enero, 2014 0:57

    @David Complicado lo de conseguir un nombre exclusivo: habría que ver que las variables intermedias creadas no existiesen en el entorno original.

    Una alternativa sería la de operar con las variables intermedias en un entorno (env) nuevo y liquidarlo luego al salir de la macro.

    Advierte cómo el ejemplo de la entrada es un caso en el que una función «al uso» no funcionaría: cambias el valor de dos objetos. Eso sí, funcionaría, lo admito, usando el horrible operador <<-.

  3. Enrique Gabriel Baquela 25 enero, 2014 4:11

    Lo ideal sería poner un nombre muy ofuscado para las variales temporales, algo como «tmp_38snxw38». Así tenés muchas menos probabilidades de colisión de nombres.

Los comentarios están desabilitados.