lisp, slime y emacs
Este es otro artículo que intenta responder preguntas que me hace la gente. Concretamente hay alguien que me ha preguntado cómo tengo configurado mi entorno de trabajo para trabajar con lisp. Dicho así en genérico sin especificar ninguno de sus sabores. Por las dudas que me planteado, tampoco tiene muy claro si tirar por lisp o seguir por scheme, y para eso yo no tengo respuesta, porque depende de los gustos de cada quien. Él ya ha estado mirando algo de scheme, aunque se declara novato, pero le interesa también saber algo más sobre lisp para comparar y decidir. Intentaré contestar lo mejor que pueda.
Lisp
Lisp no es un único lenguaje o compilador. Ya hay toda una familia
de lenguajes como son scheme, clojure, lisp... Además de estos
grandes grupos con distintas características, cada uno tiene distintos
compiladores o sistemas. Entre los scheme
podemos encontrar guile
o chicken
, por ejemplo, entre los lisp
tienes para elegir varios
common-lisp
, emacs-lisp
y otros. Además, entre los common-lisp
hay varios sistemas distintos. Puedes leer sus licencias y/o sus
características y elegir el que más rabia te dé. Pero como también me
preguntas cuál uso yo, pues te lo simplifico: yo utilizo SBCL
1.
Las diferencias fundamentales entre scheme
y lisp
son que scheme
sólo tiene una tabla de asignación, y por tanto no puedes llamar de la
misma manera a una variable y a una función, y que es case sensitive
mientras que lisp
no diferencia entre mayúsculas y minúsculas. En
cambio, tiene dos tablas de asignación y por tanto, tienes que emplear
las instrucciones defvar
y defun
, para definir variables y
funciones. Pero ambos son muy similares y el problema de los
paréntesis lo tienes en ambos, pero sólo hasta que te acostumbras a
esa sintaxis, con el tiempo casi ni te fijas en ellos.
En mi caso, la instalación de sbcl
no tiene ningún misterio. En
OpenSuse hay un paquete con dicho nombre y se instala como cualquier
paquete de la distro. Según qué sistema o distro uses, seguro que
hay algún paquete que te facilite la vida. En mi caso:
sudo zypper install sbcl
En el caso de las derivadas de Debian supongo que la instrucción
será alto estilo apt-get install sbcl
. Comprueba el nombre del
paquete en cuestión.
Libros
Para la documentación que pides te recomiendo dos libros que puedes encontrar por Internet:
- Practical Common Lisp de Peter Seibel
- On Lisp de Paul Graham
Además, si instalas sbcl
, al menos en OpenSuse
, viene con un
manual info
que se integra perfectamente con el sistema de ayuda de
Emacs, así que allí puedes encontrar también más información sobre
cómo funciona sbcl
de la que puedo dar yo en un pequeño artículo.
Librerías de quicklisp
Si ya estás trabajando con Emacs, ya conocerás su sistema de
paquetes y lo tendrás configurado. Para lisp
existe algo similar que
se llama quicklisp
. Si ya tienes instalado sbcl
en el sistema, lo
único que tienes que hacer es lo siguiente:
curl -O https://beta.quicklisp.org/quicklisp.lisp sbcl --load quicklisp.lisp --eval '(quicklisp-quickstart:install)' --quit sbcl --load ~/quicklisp/setup.lisp --eval '(ql:add-to-init-file)' --quit
La primera línea obtiene el código de quicklisp.lisp
de su sitio de
Internet. La segunda línea obtiene los paquetes e información que
necesita y lo guarda en el directorio ~/quicklisp
. Por último, la
tercera línea ejecuta el código de configuración para añadirse al
fichero ~/.sbclrc
.
Esto es totalmente optativo, pero si vas a desarrollar con lisp
te
puede facilitar un poco la vida, ya decides tú si quieres instalarlo o
no. Como digo para empezar no es necesario, pero más adelante es
posible que lo eches en falta.
Slime y otros paquetes de Emacs
SLIME
2 es una herramienta de Emacs que facilita la vida a los
programadores de lisp
. Instalarlo es tan fácil como cualquier otro
paquete de nuestro editor favorito:
package-install RET slime
Una vez que lo tenemos instalado en nuestro Emacs, tenemos que
configurarlo. Es algo sencillo, pues sólo necesita que añadamos una
línea en nuestro init.el
que le informe qué lisp
tiene que correr:
(setq inferior-lisp-program "/usr/bin/sbcl")
Comprueba que en tu caso el path coincide antes de hacer el corta-pega.
Si lo hemos instalado y configurado, podremos utilizarlo directamente
si cargamos de nuevo el fichero de configuración con load-file
o
bien tras reiniciar Emacs. El paquete proporciona un entorno
interactivo de lisp
y funciones que cargan a dicho entrono con el
código que vayamos escribiendo. Nos permite trazar y depurar código,
etc.
El paquete paredit
Dices que, en scheme
, te atascas con tanto paréntesis y pierdes la
cuenta de los que abres y los que debes cerrar. Lo siento, pero en
lisp
no notarás demasiada mejora, pero no desesperes en ninguno de
los dos casos. La solución más rápida y relajada es que actives
show-paren-mode
, bien en el apartado custom
de tu init.el
o que
en cualquier sitio de ese fichero de configuración tengas algo como:
(setq show-paren-mode t)
Esa opción lo que hace es mostrar el paréntesis pareja del
paréntesis sobre el que está situado el cursor. Bueno, no sobre el
que está situado, eso es así cuando te sitúas en el de apertura. Para
ver el que corresponde a uno de cierre tienes que situarte justo un
carácter a la derecha... pero vamos, esto es más fácil de entender
viéndolo que de explicarlo así con palabras: activa el modo y
observa cómo se comporta. Si no quieres tenerlo activado de manera
constante, también lo puedes activar con algún hook
para los modos
que quieras, o llamando a show-paren-mode
cuando lo quieras
activar.
Por otro lado, hay otros paquetes que te pueden echar una mano. Por
ejemplo, también uso el paquete paredit
. Lo que hace este paquete es
que te escribe directamente la pareja de cierre de todo paréntesis o
corchete que abras. Hay a quien le puede molestar este comportamiento,
pero a lo mejor te facilita la vida. En todo caso, es recomendable
activarlo sólo cuando programes, principalmente con lisp
, scheme
o
lenguajes similares, aunque nada te impide usarlo siempre. Lo puedes
activar mediante hooks
:
(add-hook 'emacs-lisp-mode-hook 'enable-paredit-mode) (add-hook 'lisp-mode-hook 'enable-paredit-mode)
Recuerda poner el hook
para cada uno de los modos, porque la de
lisp-mode-hook
no incluye a emacs-lisp-mode-hook
. Son listas y
modos independientes y, por tanto, tienes que activar el modo en los
dos. Otra opción sería que se activara en prog-mode-hook
y así se
cargaría en todos los modos de programación, que es otra opción
aceptable.
Echa un ojo a la lista de paquetes que hay disponibles porque hay muchos que sirven para destacar los pares de paréntesis. Algunos los colorean, otros los hacen parpadear... en fin, hay muchos paquetes que te pueden ser útiles, elige el que más te guste.
Usando slime
Bien, ya tienes todo instalado y tienes ganas de probarlo escribiendo un poco de código. Pues vamos a ello... prometo ser original con los ejemplos:
Abre Emacs y crea un buffer para los ejemplos:
C-x C-f ejemplos.lisp
Escribe el código del primer ejemplo3:
(defun hola-mundo () "Escribe un mensaje de «¡Hola Mundo!»" (format t "¡Hola mundo!"))
Lanza
slime
con el comando:M-x slime
Si tenemos todo bien configurado, debería aparecer un buffer con nombre
*slime-repl sbcl*
con un mensaje de prompt tal queCL-USER>
. Si intentas ejecutar ahí(hola-mundo)
te lanzará un error. Vuelve al buffer donde está el código, no te precipites. Primero tienes que cargarle el código que has escrito.- Evaluamos el código:
slime
tiene varias funciones de evaluación de código, en nuestro caso vamos utilizarM-x slime-eval-buffer
, pero también se puede hacer para una función o para una región de texto seleccionado. Vuelve al buffer de
slime
y prueba a ejecutar(hola-mundo)
CL-USER> (hola-mundo) ¡Hola mundo! NIL CL-USER> _
Bien ya tienes el sistema funcionando... Ahora a escribir código.
Bueno, vale, lo mismo quieres algún ejemplo más antes de lanzarte tú solo a la aventura. Prueba con el siguiente:
(defun factorial (n) "Calcula el factorial del número n." (if (= n 0) 1 (* n (factorial (- n 1)))))
Vale, ya sé que no estoy siendo muy original con los ejemplos, pero
tampoco te pongas muy estupendo, que sólo intento explicarte las
herramientas, la programación ya la tienes que poner tú. Bien, al
caso. Si has evaluado esta función cargando el buffer completo con
slime-eval-buffer
, como hicimos antes, es posible (seguro) que te
diga que ya existía una función hola-mundo
y te lance un warning.
En ese caso, lo puedes ignorar, porque estamos trabajando en el mismo
código. Si aún no lo has cargado y quieres evitar el warning sitúa
el cursor en cualquier línea de la definición de la función y utiliza
el comando M-x slime-eval-defun
. Como ves, era sólo por mostrar otra
función que carga código. A partir de ese momento, ya estará la
función factorial
también disponible en slime
:
CL-USER> (factorial 6) 720 CL-USER> _
Por darle otra vuelta, prueba a hacer un trazado de la función
factorial
, por ejemplo:
CL-USER> (trace factorial) (FACTORIAL) CL-USER> (factorial 5) 0: (FACTORIAL 5) 1: (FACTORIAL 4) 2: (FACTORIAL 3) 3: (FACTORIAL 2) 4: (FACTORIAL 1) 5: (FACTORIAL 0) 5: FACTORIAL returned 1 4: FACTORIAL returned 1 3: FACTORIAL returned 2 2: FACTORIAL returned 6 1: FACTORIAL returned 24 0: FACTORIAL returned 120 120 CL-USER>
Queda bonito ver cómo se desarrolla ─y desenrrolla─ la llamada a una función recursiva y lo que va devolviendo.
Paquetes externos
Si antes de llegar hasta aquí hiciste la instalación de quicklisp
y
quieres probar cómo va esto de los paquetes externos. Vamos con un
ejemplo básico también para que puedas comprobar cómo funcionan. Por
ejemplo, hunchentoot
es un servidor web hecho enteramente con
lisp
y queremos ponerlo en marcha en nuestro sistema. Vamos por
pasos:
- Crea un buffer nuevo con
C-x C-f servidor.lisp
En ese buffer teclea el siguiente código:
(ql:quickload "hunchentoot") (hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 6969))
Evalúa el código anterior con
slime
. Después de unos segundos bajando y preparando paquetes aparecerá el mensaje de que el servidor está funcionando:To load "hunchentoot": Load 1 ASDF system: hunchentoot ; Loading "hunchentoot" .....
Si apuntamos cualquier navegador a la dirección localhost:6969
obtendremos una ventana como la siguiente:
Y ya vemos cómo de fácil es obtener todo un servidor
Conclusiones
Estas herramientas están hechas para funcionar conjuntamente, puedes
ponerle más cosas: configurarle el modo lsp
, o lo que más rabia te
dé, pero esto es lo básico, básico, y un poco más que aporta
quicklisp
.
Si has utilizado Emacs para trastear con scheme
verás que slime
es a lisp
lo que geiser
es a scheme
. No se trabaja de forma
idéntica en ambos, pero sí muy similar.
Footnotes:
Steel Bank Common Lisp http://www.sbcl.org
Superior Lisp Interaction Mode for Emacs
No esperabas tanta originalidad, seguro.
Comentarios