Homoiconicidad ( homoiconicidad , ing. homoiconicidad , ing. homoicónico , del griego ὁμός - igual, idéntico + “iconicidad” - la relación de similitud entre el signo y el objeto al que apunta este signo (ver semiótica ) - a su vez, de cf. - El griego εἰκόνα - "imagen", "imagen") es una propiedad de algunos lenguajes de programación en los que la estructura del programa es similar a su sintaxis y, por lo tanto, la representación interna del programa se puede determinar leyendo el marcado de texto [ 1] . Si un lenguaje es homoicónico, significa que el texto del programa tiene la misma estructura que su árbol de sintaxis abstracta (es decir, AST y sintaxis son isomorfos ). Esto permite acceder a todo el código del lenguaje y procesarlo como datos utilizando la misma representación.
En un lenguaje homoicónico, la representación principal de los programas es también una estructura de datos en el tipo primitivo del propio lenguaje. Esto hace que la metaprogramación sea más fácil que en un lenguaje sin esta propiedad, ya que el código puede verse como datos : la reflexión en el lenguaje (que determina la estructura de un programa en tiempo de ejecución ) se basa en una estructura única y homogénea, y no hay necesidad de manejar varias construcciones diferentes que surgen en estructuras sintácticas complejas. En otras palabras, la homoiconicidad es cuando el código fuente de un programa se escribe como una estructura de datos básica y el lenguaje de programación sabe cómo acceder a él.
Un ejemplo típico es el lenguaje de programación Lisp , que fue diseñado para facilitar la manipulación de listas, y donde la estructura se da como expresiones S , que toman la forma de listas anidadas. Los programas Lisp se escriben como listas; el resultado es que el programa puede acceder a sus propias funciones mientras se ejecuta, así como reprogramarse sobre la marcha. Los lenguajes homoicónicos tienden a incluir soporte completo para macros sintácticas , lo que permite al programador expresar transformaciones de programación de manera concisa. Ejemplos de tales lenguajes de programación son Clojure (un dialecto moderno de Lisp), Rebol y Refal .
El término se mencionó por primera vez en un artículo de 1960 de Doug McIlroy [2] , que a su vez fue mencionado en un artículo de 1965 de Calvin Moores y Peter Deutsch , en el que la propiedad se presentaba como clave para la programación del TRAC . lenguaje que desarrollaron [3] .
Alan Kay usó y puede haber popularizado el término "homoiconicidad", usándolo en su tesis doctoral sobre las propiedades respectivas de Lisp y el lenguaje TRAC [4] , señalando los costos de legibilidad de los programas en este enfoque: "programas escritos en parecen la carta del rey Burna-Buriash a los sumerios impresa en cuneiforme babilónico" .
Uno de los beneficios de ser homoicónico es que extender el lenguaje con nuevos conceptos tiende a ser más fácil, ya que los datos que representan el código se pueden pasar entre las capas meta y base de un programa. El árbol de sintaxis abstracta de una función puede construirse y modificarse como una estructura de datos de metacapa y luego ejecutarse . Puede ser mucho más fácil descubrir cómo manipular el código, ya que puede ser más comprensible como datos simples (ya que el formato de un idioma es el mismo que su formato de datos).
La simplicidad que permite esto también es una desventaja: al menos en el caso de los lenguajes orientados a listas similares a Lisp, esto puede eliminar muchas de las señales visuales que ayudan a las personas a analizar visualmente las construcciones del lenguaje, y esto puede conducir a un aumento en la curva de aprendizaje del idioma [5 ] . Ver también el ensayo "The Curse of Lisp" [6] para una descripción de las deficiencias.
Una demostración típica de homoiconicidad es la calculadora metacircular .
Lenguajes de programación homoicónicos:
En los sistemas de arquitectura von Neumann (incluida la gran mayoría de las computadoras modernas), el código de máquina también tiene esta propiedad, con un tipo de datos de bytes en la memoria.
Lisp usa expresiones S como una representación externa de datos y código. Las expresiones S se pueden leer usando una función primitiva READque devuelve los tipos básicos de Lisp: listas, caracteres, números, cadenas. Una función primitiva de Lisp EVALusa este código, representado como datos de Lisp, para evaluar los efectos secundarios y devolver el resultado.
Un ejemplo de datos en Lisp es una lista que utiliza varios tipos de datos: (sub)listas, caracteres, cadenas y números enteros:
(( :nombre "john" : 20 años ) ( :nombre "mary" : 18 años ) ( :nombre "alice" : 22 años ))código ceceo. El ejemplo utiliza listas, símbolos y números:
( * ( sin 1.1 ) ( cos 2.03 ) ) ; en infijo: sin(1.1)*cos(2.03)Creando tal expresión con una función primitiva LISTy asignando el resultado a una variable expression:
( expresión setf ( lista '* ( lista 'sin 1.1 ) ( lista 'cos 2.03 )) ) -> ( * ( SIN 1.1 ) ( COS 2.03 )) ; Lisp regresa e imprime el resultado. ( tercera expresión ) ; el tercer elemento de la expresión -> ( COS 2.03 )Reemplazo de término COSpor SIN:
( setf ( primera ( tercera expresión )) 'SIN ) ; La expresión ahora es (* (SIN 1.1) (SIN 2.03)).Ejecutar expresión:
( expresión de evaluación ) -> 0.7988834Imprime esta expresión en una cadena:
( expresión de impresión a cadena ) -> "(* (SIN 1.1) (SIN 2.03))"Resta una expresión de una cadena:
( leer desde la cadena "(* (SIN 1.1) (SIN 2.03))" ) -> ( * ( SIN 1.1 ) ( SIN 2.03 )) ; devuelve una lista de listas, números y símbolos