Initial commit
[blog.git] / posts / 150.html
1 title: Programar macros en Lisp es como reprogramar Matrix
2 date: 2013-10-09 18:02
3 ---
4 <div>
5 <a href="http://www.jsancho.org/wp-content/uploads/2013/10/pyfry.jpg"><img src="http://www.jsancho.org/wp-content/uploads/2013/10/pyfry-300x225.jpg" alt="pyfry" width="300" height="225" class="alignright size-medium wp-image-581" /></a>
6
7 <p>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.</p>
8
9 <p>Además, ahora que se ponen de moda otra vez los <a href="http://en.wikipedia.org/wiki/Domain-specific_language">Domain Specific Languages (DSL)</a>, lo cierto es que tener el poder de las macros de tu parte te soluciona muchos problemas.</p>
10
11 <p>Vamos a probar con un problema fácil que resulta imposible de resolver en Python: implementar un <b>if-then-else</b> usando <b>if</b> y <b>else</b>. ¿Ein? :-D La respuesta rápida es:</p>
12
13 <pre>def if_then_else(cond, if_true, if_false):
14     if cond:
15         return if_true
16     else:
17         return if_false</pre>
18
19 <p>¡Vamos a probarlo!</p>
20
21 <pre>>>> if_then_else(True, "si", "no")
22 'si'
23 >>> if_then_else(False, "si", "no")
24 'no'
25 >>> if_then_else(True, "si", 1/0)
26 ZeroDivisionError: integer division or modulo by zero</pre>
27
28 <p>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 <b>else</b> y nuestra condición es <b>True</b>? 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.</p>
29
30 <p>Por lo tanto, resulta imposible crear estructuras tipo <b>if-then-else</b>. Y lo mismo ocurre con operadores como <b>and</b> (si el primer parámetro es falso no evaluamos el resto), <b>or</b> (si el primer parámetro es cierto no evaluamos el resto), etc.</p>
31
32 <p>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.</p>
33
34 <p>La solución en Lisp utiliza macros. Si usaramos funciones obtendríamos el mismo resultado que con Python.</p>
35
36 <pre>(defmacro if-then-else (cond if-true if-false)
37   `(if ,cond ,if-true ,if-false))</pre>
38
39 <p>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.</p>
40
41 <p>Para que nos entendamos, una macro es un generador de código. Si ahora ejecuto <pre>(if-then-else 'True "si" "no")</pre>, el intérprete evaluará la macro, que devolverá el resultado <pre>(if 'True "si" "no")</pre>, y ese resultado será evaluado a su vez proporcionando el resultado adecuado. Vamos a verlo.</p>
42
43 <pre>> (if-then-else 1 "si" "no")
44 "si"
45 > (if-then-else nil "si" "no")
46 "no"
47 > (if-then-else 1 "si" (/ 1 0))
48 "si"</pre>
49
50 <p>Ahora la división por cero ya no da error porque no llega a evaluarse</p>
51
52 <p>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.</p>
53
54 <p>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.</p>
55 </div>