¿Qué debe saber todo programador sobre Lisp?

Aquí están los aspectos más destacados, desde mi perspectiva:

  • Common Lisp tiene la capacidad de hacer cualquier cosa que sea teóricamente posible en cualquier otro idioma que pueda existir, excepto las continuaciones multidisparo.
  • El lenguaje central y la biblioteca, junto con las extensiones definidas por la implementación, se pueden redefinir en cada capa si es necesario. Puede cambiar la sintaxis de entrada para que se vea como C (p. Ej., La sintaxis de la expresión M), la forma en que funciona el polimorfismo (combinaciones de métodos), la evaluación o no evaluación del código, y más.
  • Todas las implementaciones “principales” son compatibles con casi todo el estándar básico 1989/1990 y proporcionan un cierto número de extensiones “esperadas” para cosas como redes, subprocesos múltiples y los protocolos de metaobjetos; Existen bibliotecas estándar que envuelven estos detalles específicos de la implementación, lo que permite una programación muy portátil incluso en implementaciones de compilador / biblioteca.
  • La sintaxis de expresión S canónica: (función arg1 arg2 …) no es exactamente la forma real de “código fuente” de un programa Lisp; más bien, es una representación impresa del árbol de sintaxis abstracta real en el que opera el compilador. La mayoría de los otros idiomas se traducen de un formulario fuente textual a un AST, pero rara vez “llega a ver” el AST. En Lisp, edita efectivamente el AST directamente.
  • Debido a que el AST está “justo allí”, puede modificarlo directamente usando las macros de Lisp, que son casi completamente diferentes, por ejemplo, las macros C. (Se parecen más a las plantillas de C ++, pero más). Una macro Lisp es una función que toma un fragmento AST como entrada y devuelve un nuevo fragmento AST como salida. Es un programa de redacción de programas. Esta no es una “técnica avanzada”. Si desea crear una nueva sintaxis o factorizar código común, simplemente hágalo, con muy poca diferencia con respecto a cualquier otra función.
  • Debido a esto, su programa se parece a su problema . Puede escribir pseudocódigo en sintaxis S-exp y escribir una macro que lo convierta en código real. Puede crear un “lenguaje infantil” dentro de su programa que lo describa de una manera que le resulte más fácil de explorar.
  • También hay muy poca distinción entre el comportamiento de “tiempo de compilación” y “tiempo de ejecución” , o incluso “tiempo de lectura”. Contraste con, digamos, borrado de tipo en C y Java, o tener que saltar a través de aros para “rastrear” un símbolo en tiempo de ejecución de C de cuál había sido su representación textual en código fuente.
  • Es extremadamente fácil programar en Lisp, y relativamente fácil mejorar el rendimiento a nivel mecánico. También puede ampliar el compilador con macros de compilación para mejorar el rendimiento. El rendimiento ejecutable es al menos tan bueno como cualquier lenguaje interpretado y, a menudo, puede competir con C ++ al menos.
  • Common Lisp’s Object System ( CLOS ) es uno de los pocos sistemas de programación orientados a objetos “totalmente” en cualquier lenguaje, y es mucho más completo y flexible que, por ejemplo, C ++ o Java. (El término “objeto” en realidad proviene del antepasado de CL, Lisp 1.5 atrás ~ 1960). Los modelos de funciones genéricas y el Protocolo Meta-Objeto (MOP) son mucho más avanzados y flexibles que cualquier otro sistema OOP en uso.
  • El sistema de condición y reinicio de Lisp, y el sistema de señal en general, es mucho más potente y flexible que las excepciones en la mayoría de los idiomas. Un objeto Condición, por ejemplo, advertencia, error u otro, puede llevar información rica sobre una condición en tiempo de ejecución, pero los controladores pueden interactuar no solo con ese objeto condición, sino que no necesitan desenrollar la pila para manejarla. El ejemplo más cercano en otro idioma podría ser la función “REANUDAR SIGUIENTE” en los antiguos controladores BÁSICOS “EN ERROR GOSUB”. Estos controladores de condiciones, casos de reinicio y todo lo demás, se pueden combinar para permitir de manera efectiva que las rutinas de la biblioteca presenten posibles soluciones a los problemas de sus llamantes, incluso muchas capas arriba de la pila, y permitan al llamador manejar la situación y luego aconsejar a la rutina cómo proceder. Un ejemplo clásico: lleva cinco horas ejecutar un informe en un archivo, pero a las 4 horas, el disco se queda sin espacio. La función de redacción de informes señala un error; En algún lugar de la pila, algún otro programa determina lo que se debe hacer para liberar espacio en el disco, y luego invoca el reinicio “inténtelo de nuevo”. El redactor de informes se reanuda sin tener que comenzar de nuevo, ya que sabe cómo rebobinar “lo suficiente” reiniciar en el registro anterior.
  • Diseñado para desarrolladores, no compiladores. Common Lisp le permite adjuntar documentación a funciones, métodos, variables globales e incluso símbolos arbitrarios. Puede inspeccionar las relaciones entre las cosas de forma interactiva, de una manera que los depuradores de pocos idiomas puedan reunir, y no requiere mantener alguna “base de datos del proyecto” especial o un archivo TAGS o un archivo de datos de depuración. El sistema de condición / reinicio se extiende al depurador; Se puede detectar un compilador o un error en tiempo de ejecución, manejarlo de forma interactiva e incluso puede reiniciar partes del programa o proceso de compilación a medida que avanza. Naturalmente, también puede inspeccionar el objeto de condición, examinar la pila de llamadas y ¿mencioné que Reiniciar también puede solicitar argumentos? No conozco ningún otro idioma que tenga este nivel de facilidad de desarrollo, aunque Smalltalk se acerca.
  • Operadores del sistema, también. Sin mucho trabajo, las mismas condiciones y reinicios que sus desarrolladores escribieron para su propia conveniencia se pueden usar para interactuar con un sistema en ejecución para recuperarse de los errores también.
  • El tipo Número . Existe. Common Lisp puede manejar “-⅓” o “1.33333333” o “3 + 5 × √-1” o 10⁹⁹: todos son números. Los escribimos como “-1/3 1.33333333 #c (3 5)” y un 1 con 99 ceros después. No tiene que preocuparse por desbordamientos envolventes, etc. También puede restringir su rango precisamente cuando le importa; por ejemplo, (check-type x (entero 0 *)) será un entero positivo, (check-type x (real 0 (100))) lo limita a valores positivos reales (no complejos) de 0 a (infinitesimalmente) menos de 100. Obtendrá advertencias en tiempo de compilación y errores en tiempo de ejecución si pasa algo fuera de rango, e incluso un caso de reinicio para proporcionar un valor de reemplazo para x si desea manejarlo en otro lugar. El compilador incluso puede generar un código de máquina más estricto si ve que, por ejemplo, no necesita estar atento a la división por cero o siempre obtendrá enteros pequeños (fijos) como entrada. Y, por cierto, 1/3 y 1/5 … no pueden existir precisamente en coma flotante … pero los números racionales son perfectamente precisos a menos que se mezclen en flotadores.
  • Funciones de primera clase y objetos lambda (función anónima) … pero todo el mundo hace eso, ¿verdad?
  • Los símbolos y paquetes también son valores de datos. Esto está en muchos idiomas ahora, pero es distinto de, digamos, C.
  • Enlaces Dinámicos. La mayoría de los idiomas solo tienen enlaces léxicos; si define una variable “aquí”, es visible solo desde “aquí”. Los enlaces globales son un caso especial, accesible desde cualquier lugar y temido con razón. Los enlaces dinámicos le permiten vincular un valor “desde aquí, pero a cualquier persona a la que llame desde aquí”. Son un análogo más preciso y útil a los “enlaces locales de subprocesos” en algunos idiomas, menos disruptivos que los globales, e inmensamente útiles en reduciendo los “argumentos de paso” en una pila. Ejemplos comunes? “¿Quién es el usuario actual y tienen permiso para hacerlo?” No requiere pasar un objeto de “solicitud” o “usuario” inútil de otro modo a través de 10 funciones entre la interfaz de usuario y el núcleo.
  • Las listas de Lambda con parámetros de palabras clave son bajas en la lista pero son realmente agradables de tener. Los parámetros opcionales de “descanso” y palabras clave facilitan la vida de todos.
  • Quicklisp existe. Es una biblioteca en línea cuidadosamente seleccionada, bastante probada y multiplataforma de bibliotecas Common Lisp disponibles, análoga a la CPAN de Perl. Quicklisp beta
  • La mayoría de los sistemas Lisp comunes en uso práctico tienen una interfaz de función externa que les permite llamar a funciones de tipo “biblioteca C” (o, en Armed Bear CL, que se ejecuta en la JVM, métodos de biblioteca Java) con bastante facilidad. Entonces, además de las masas de bibliotecas disponibles en CL de forma nativa, hay miles más escritas en C (resp. Java) fácilmente accesibles.