Notxor tiene un blog

Defenestrando la vida

PicoLisp

Notxor
2023-09-01

Hoy traigo un poco de información sobre PicoLisp y por qué no lo puedo calificar o situar ni entre los Lisp ni entre los Scheme. Son unas primeras impresiones y por tanto puedo estar equivocado, pues me baso, fundamentalmente, en su documentación y lo que me han mostrado unos pocos ejemplos que he ido probando, con alguna dificultad, pero también con algún logro. En este artículo traigo lo más básico: instalación, entorno de programación y algunas pruebas de rendimiento.

Pero, antes de nada: ¿qué es PicoLisp? Después de unos días investigando y probando tampoco lo tengo yo muy claro, pero diría que es un framework para la creación de aplicaciones, a través de una máquina virtual programada en clang (LLVM) que proporciona un lenguaje interpretado de la familia de Lisp, bastante eficiente. Proporciona todas las características para poder montar cualquier tipo de aplicación:

Su máquina virtual está diseñada como un montón de células o celdas (cells), que consisten en dos palabras de 64bits, que pueden contener un valor o un puntero a otra célula:

celula.svg

Los tipos de datos complejos se montan en base a estas células o celdas. Por ejemplo, un bignum o una string se montan encadenando dígitos o caracteres en una lista de celdas enlazadas. En este aspecto, es un sistema muy congruente.

Instalación

En algunas distribuciones de GNU/Linux está el paquete de PicoLisp listo para ser instalado. Por lo que se dice en la documentación sólo en las derivadas de Debian: Ubuntu, Mint, ... En mi caso, utilizo OpenSuse Tumbleweed y no hay paquete PicoLisp ̣—ni se le espera—. La instalación, por tanto, pasa por el compilado desde el código fuente, que se distribuye con una licencia MIT/X11.

Compilado

Lo primero es comprobar que están todas las dependencias necesarias para la compilación. En las instrucciones de instalación pide las siguientes:

  • binutils
  • make
  • clang
  • llvm
  • libreadline-dev
  • libfii-dev
  • libssl-dev
  • pkg-config

Una vez tenemos esas dependencias instaladas, necesitaremos también el código fuente, que lo podemos descargar y descomprimir de la siguiente manera:

$ wget https://software-lab.de/pil21.tgz
$ tar xfz pil21.tgz

La compilación, también es sencilla:

$ cd pil21/src
$ make

Si lo has compilado sin errores la instalación manual consiste nada más que en dar la situación de los paquetes con enlaces:

# ln -s /<path completo>/pil21 /usr/lib/picolisp
# ln -s /usr/lib/picolisp/bin/picolisp /usr/bin
# ln -s /usr/lib/picolisp/bin/pil /usr/bin

# ln -s /<path completo>/pil21/man/man1/picolisp.1 /usr/share/man/man1
# ln -s /<path completo>/pil21/man/man1/pil.1 /usr/share/man/man1
# ln -s /<path completo>/pil21 /usr/share/picolisp
# ln -s /<path completo>/pil21/lib/bash_completion /usr/share/bash-completion/completions/pil

Obsérvese que los enlaces se deben crear con permisos de root.

Preparación del entorno: Emacs

Para Emacs he encontrado dos paquetes que pueden servir para configurar nuestro editor favorito para desarrollar código con PicoLisp. Uno es el recomendado en la documentación del sistema4 y otro es el que está en ELPA.

Por comodidad, y puesto que lo que estoy haciendo son pruebas para determinar si me gusta o no esta herramienta, he instalado el sistema que viene en la lista de paquetes y, además, añado un paquete de autocompletado.:

(use-package plisp-mode
  :defer t
  :init
  (require 'inferior-plisp))
(use-package company-plisp
  :defer t
  :after plisp-mode)
(add-to-list 'auto-mode-alist '("\\.l$" . plisp-mode))

Quizá sea más efectivo el paquete recomendado en la documentación, pero hay que descargar los fuentes, añadir los directorios a la lista de directorios fuente y es más lioso que utilizar el paquete del repositorio.

Comparación entre hermanos

Dicen que comparar entre hermanos no debería hacerse por aquello de que cada uno somos un mundo, pero esto no se aplica a herramientas de trabajo. En este caso, la familia Lisp se encuentra dividida en un par de ramas principales Common Lisp y Scheme con algunas ovejas descarriadas como elisp o PicoLisp. El primero más cercano a la rama CL y el segundo, desde mi punto de vista, más cercano a la rama Scheme.

CL proporciona una estandarización compleja, mientras que la de Scheme es mucho más sencilla, lo que hace que no todos los scheme sean compatibles aunque van apareciendo aspectos que estandarizar cada poco tiempo y evolucionan R5RS, R7RS ... Los que yo llamo descarriados son sistemas relacionados que no cumplen los estándares.

Entre los Lisp es algo habitual saltarse los estándares por algún motivo. Este motivo suele ser adaptar el lenguaje a una tarea concreta, algo que hacen Maxima, AutoCAD y emacs-lisp. Entre los Scheme es más pronunciado, porque al final cada uno va a su rollo, aunque tiendan a la estandarización. En algunos casos, como Racket, se puede forzar utilizando # Lang r7rs como primera línea del script. Con otros las cosas no son tan sencillas.

La diferencia fundamental entre los Lisp y los Scheme se basa en las tablas de símbolos internas. Lisp cuenta con dos y por tanto las variables y las funciones tienen cada una su tabla y puede ocurrir, y ocurre, que una variable y una función compartan nombre. A cambio, deben tener dos palabras distintas para definir unas u otras. Por el contrario, los Scheme tienen una sola tabla y por tanto, no pueden compartir nombre las variables y las funciones. A cambio, sólo hay una clase de define. En este sentido, PicoLisp es de la familia de los Scheme y sólo tiene una tabla interna.

Un aspecto común a todos es el modo repelente... digo REPL. Todos ellos pueden funcionar con la secuencia read, eval, print, loop. En el paso de eval, tanto los Lisp como los Scheme realizan en realidad dos: 1) convierte las macros definidas en instrucciones ejecutables y 2) ejecuta dichas instrucciones. En este caso PicoLisp se desmarca de todos ellos y no hay un paso macro en el proceso. En cambio, se pueden realizar las mismas cosas, o muy parecidas, con un poco de sintaxis alienígena en el paso read.

En este sentido tengo que recordar que me costó, al principio, comprender las macros en Lisp, que permiten redefinir el sistema para adaptarlo a problema creando, en la práctica, un lenguaje propio para el mismo. Le he echado un vistazo al modo en que se haría en PicoLisp y no termino de pillar toda la funcionalidad. Supongo que es una de esas cosas que las vas pillando a base de usarlas. Para los más puristas el resumen es que no hay macros como en Lisp, pero sí mecanismos que proporcionan una funcionalidad equivalente.

Una característica única que he podido apreciar en PicoLisp es la capacidad de que las variables tengan propiedades. Pongo un ejemplo para no hablar en vacío:

: (setq variable "Este es el valor")
-> "Este es el valor"
: (put 'variable 'propiedad1 "Una propiedad")
-> "Una propiedad"
: (put 'variable 'propiedad2 27)
-> 27
: (show 'variable)
variable "Este es el valor"
   propiedad2 27
   propiedad1 "Una propiedad"
-> variable
: (get 'variable 'propiedad2)
-> 27
: 

Se puede apreciar que las propiedades son pares key-value y que una variable puede tener tantas como hagan falta sin límite a priori.

Eficiencia

En la mayoría de los casos la eficiencia, entendida como la velocidad de ejecución, no es uno de nuestros problemas centrales. Sin embargo, para la ocasión he preparado una pequeña prueba de velocidades, comparando el tiempo de ejecución.

El código es equivalente en los tres sistemas:

  1. Lisp:

    ;;; archivo factorial.lisp
    
    (defun factorial (x)
      (if (= x 0)
          1
          (* x (factorial (- x 1)))))
    
    (defun main ()
      (print (factorial 2000)))
    
    (main)
    

    Para llamarlo interpretado desde la línea de comandos se utiliza:

    sbcl --script factorial.lisp
    

    Para compilar en un ejecutable el anterior archivo abro una sesión SBCL y hago lo siguiente:

    * (load "./factorial.lisp")
    [...]
    * (sb-ext:save-lisp-and-die "factorial" :toplevel #'main :executable t)
    [...]
    $
    
  2. Scheme:

    ;;; fichero factorial.scm
    
    (define (factorial x)
      (if (= x 0)
          1
          (* x (factorial (- x 1)))))
    
    (define (main)
      (print (factorial 2000)))
    
    (main)
    

    Para llamarlo interpretado desde la línea de comandos utilizo:

    csi -s factorial.scm
    

    Para compilar el archivo en un ejecutable desde línea de comandos:

    csc -o fact-scheme factorial.scm
    
  3. PicoLisp:

    # fichero factorial.l
    
    (de factorial (N)
        (if (=0 N)
            1
            (* N (factorial (dec N)))))
    
    (de main ()
        (prinl (factorial 2000)))
    
    (main)
    

    Para llamarlo desde la línea de comandos utilizo:

    pil factorial.l -bye
    

Al hacer las llamadas anteriores con la instrucción time delante, me arrojó los siguientes resultados

Herramienta Interpretado Compilado
SBCL 28,89ms 10,16ms
Chicken 11,50ms 8,12ms
PicoLisp 14,13ms ----

PicoLisp no tiene modo compilado, sólo interpretado, por lo que no se puede comparar. En realidad no debería poderse comparar de ningún modo o habría que hacer muchos otros bancos de pruebas con ello para poder determinar en qué aspectos unos superan a otros. Sin embargo, se puede afirmar que PicoLisp se desempeña bastante bien, comparado con el Lisp y el Scheme más rápidos.

Conclusiones

PicoLisp es un framework digno de tener en cuenta y al que, después de haberle dedicado unos buenos ratos estos días para escribir este artículo, puedo afirmar que le he ido cogiendo rápido las maneras. Aunque hay que admitir, que también tengo ya un cierto bagaje con las herramientas de la familia Lisp. Sin embargo, apenas he podido arañar un poco la superficie de PicoLisp.

Como resumen puedo decir que me ha gustado lo que he visto hasta ahora, pero tampoco es una sorpresa que me guste un chismático de la familia Lisp.

Notas al pie de página:

1

También he visto en la documentación que se puede establecer el editor en modo Emacs, pero he sido incapaz de hacerlo. No sé si falta alguna librería o se tiene que hacer alguna otra cosa que no estoy haciendo.

2

Hay que recordar que tanto en Lisp (CLOS) como en Scheme (SOS) hay que cargar librerías externas.

3

En la documentación más antigua he visto alguna captura de pantalla con los widgets clásicos de las X11, pero parece que en la actualidad han migrado hacia una GUI basada en html.

4

No está en ELPA como paquete.

Categoría: lisp pico-lisp scheme emacs

Comentarios

Debido a algunos ataques mailintencionados a través de la herramienta de comentarios, he decidido no proporcionar dicha opción en el Blog. Si alguien quiere comentar algo, me puede encontrar en esta cuenta de Mastodon, también en esta otra cuenta de Mastodon y en Diaspora con el nick de Notxor.

Si usas habitualmente XMPP (si no, te recomiendo que lo hagas), puedes encontrar también un pequeño grupo en el siguiente enlace: notxor-tiene-un-blog@salas.suchat.org

Disculpen las molestias.