X-Git-Url: https://git.jsancho.org/?p=blog.git;a=blobdiff_plain;f=posts%2F150.html;fp=posts%2F150.html;h=0000000000000000000000000000000000000000;hp=595c42720f06894c2ad9c706e671ec405bea20bc;hb=e7b6ce04fe8fee9ae651f50b29f6c37fef87b41d;hpb=3e096e3d39e9924369271dade89f029c40c799b5 diff --git a/posts/150.html b/posts/150.html deleted file mode 100644 index 595c427..0000000 --- a/posts/150.html +++ /dev/null @@ -1,55 +0,0 @@ -title: Programar macros en Lisp es como reprogramar Matrix -date: 2013-10-09 18:02 ---- -
-pyfry - -

Si algo echo de menos cuando programo con Python, Ruby u otro lenguaje similar son las macros de Lisp. Programar con macros (no tienen nada que ver con las macros del preprocesador de C como piensan algunos) es como ser Neo y cambiar todo lo que quieras en Matrix.

- -

Además, ahora que se ponen de moda otra vez los Domain Specific Languages (DSL), lo cierto es que tener el poder de las macros de tu parte te soluciona muchos problemas.

- -

Vamos a probar con un problema fácil que resulta imposible de resolver en Python: implementar un if-then-else usando if y else. ¿Ein? :-D La respuesta rápida es:

- -
def if_then_else(cond, if_true, if_false):
-    if cond:
-        return if_true
-    else:
-        return if_false
- -

¡Vamos a probarlo!

- -
>>> if_then_else(True, "si", "no")
-'si'
->>> if_then_else(False, "si", "no")
-'no'
->>> if_then_else(True, "si", 1/0)
-ZeroDivisionError: integer division or modulo by zero
- -

Y aquí nos encontramos el principal problema de esta implementación. ¿Por qué se evalua la división por 0 si se encuentra en la parte else y nuestra condición es True? La respuesta es que Python, al no tener macros ni nada parecido, solamente nos proporciona funciones para este tipo de cosas y las funciones evaluan todos sus parámetros.

- -

Por lo tanto, resulta imposible crear estructuras tipo if-then-else. Y lo mismo ocurre con operadores como and (si el primer parámetro es falso no evaluamos el resto), or (si el primer parámetro es cierto no evaluamos el resto), etc.

- -

Algunos me han sugerido soluciones usando los decoradores de Python para capturar los errores de la evaluación, pero el verdadero problema reside en la evaluación en si misma.

- -

La solución en Lisp utiliza macros. Si usaramos funciones obtendríamos el mismo resultado que con Python.

- -
(defmacro if-then-else (cond if-true if-false)
-  `(if ,cond ,if-true ,if-false))
- -

Esta expresión es bastante legible incluso para los profanos. Se define una macro de nombre if-then-else que recibe tres parametros. La gran diferencia es que los parámetros no se evaluan cuando se evalua la macro y la evaluación de la macro nos desvuelve lo que especificamos en el cuerpo.

- -

Para que nos entendamos, una macro es un generador de código. Si ahora ejecuto

(if-then-else 'True "si" "no")
, el intérprete evaluará la macro, que devolverá el resultado
(if 'True "si" "no")
, y ese resultado será evaluado a su vez proporcionando el resultado adecuado. Vamos a verlo.

- -
> (if-then-else 1 "si" "no")
-"si"
-> (if-then-else nil "si" "no")
-"no"
-> (if-then-else 1 "si" (/ 1 0))
-"si"
- -

Ahora la división por cero ya no da error porque no llega a evaluarse

- -

Las macros nos dan el poder de crear programas que a su vez crean programas, ya que nos permiten jugar en el núcleo mismo del lenguaje, algo que no nos permiten los lenguajes convencionales.

- -

A pesar de su potencia, las macros tienen los llamados "problemas de higiene", algo que se ha solucionado en Scheme y que explicaré en el próximo artículo.

-