stringtranslate.com

homoiconicidad

En programación de computadoras , la homoiconicidad (de las palabras griegas homo- que significa "lo mismo" e ícono que significa "representación") es una propiedad de algunos lenguajes de programación . Un lenguaje es homoicónico si un programa escrito en él puede manipularse como datos usando el lenguaje. [1] Por lo tanto, la representación interna del programa se puede inferir simplemente leyendo el programa mismo. Esta propiedad a menudo se resume diciendo que el lenguaje trata el código como datos .

En un lenguaje homoicónico, la representación principal de los programas es también una estructura de datos en un tipo primitivo del propio lenguaje. [1] Esto hace que la metaprogramación sea más fácil que en un lenguaje sin esta propiedad: la reflexión en el lenguaje (examinar las entidades del programa en tiempo de ejecución ) depende de una estructura única y homogénea, y no tiene que manejar varias estructuras diferentes que aparecerían en un sintaxis compleja. Los lenguajes homoicónicos suelen incluir soporte total para macros sintácticas , lo que permite al programador expresar transformaciones de programas de forma concisa.

Un ejemplo comúnmente citado es Lisp , que fue creado para permitir manipulaciones fáciles de listas y donde la estructura está dada por expresiones S que toman la forma de listas anidadas y pueden ser manipuladas por otro código Lisp. [2] Otros ejemplos son los lenguajes de programación Clojure (un dialecto contemporáneo de Lisp), Rebol (también su sucesor Red ), Refal , Prolog y posiblemente Julia (consulte la sección “Métodos de implementación” para más detalles).

Historia

El término apareció por primera vez en relación con el lenguaje de programación TRAC , desarrollado por Calvin Mooers : [3]

Uno de los principales objetivos del diseño era que el script de entrada de TRAC (lo que escribe el usuario) fuera idéntico al texto que guía la acción interna del procesador TRAC. En otras palabras, los procedimientos TRAC deben almacenarse en la memoria como una cadena de caracteres exactamente como los escribió el usuario en el teclado. Si los propios procedimientos del TRAC desarrollan nuevos procedimientos, estos nuevos procedimientos también deben indicarse en el mismo guión. El procesador TRAC en su acción interpreta este script como su programa. En otras palabras, el programa traductor TRAC (el procesador) convierte efectivamente la computadora en una nueva computadora con un nuevo lenguaje de programa: el lenguaje TRAC. En cualquier momento, debería ser posible mostrar información sobre el programa o el procedimiento en la misma forma en que el procesador TRAC actuará sobre él durante su ejecución. Es deseable que la representación del código de caracteres interno sea idéntica o muy similar a la representación del código externo. En la implementación TRAC actual, la representación de caracteres internos se basa en ASCII . Debido a que los procedimientos TRAC y el texto tienen la misma representación dentro y fuera del procesador, se aplica el término homoicónico, de homo que significa lo mismo e icono que significa representación.

La última frase anterior está anotada con la nota 4 a pie de página, que da crédito por el origen del término: [a]

Siguiendo la sugerencia de McCullough WS, basada en la terminología debida a Peirce, CS

Los investigadores implicados en esta cita podrían ser el neurofisiólogo y cibernético Warren Sturgis McCulloch (nótese la diferencia en el apellido de la nota) y el filósofo, lógico y matemático Charles Sanders Peirce . [5] De hecho, Pierce utilizó el término "icono" en su Teoría Semiótica. Según Peirce, existen tres tipos de signos en la comunicación: el icono, el índice y el símbolo. El ícono es la representación más simple: un ícono se parece físicamente a aquello que denota.

Alan Kay utilizó y posiblemente popularizó el término "homoicónico" mediante el uso del término en su tesis doctoral de 1969: [6]

Un grupo notable de excepciones a todos los sistemas anteriores son Interactive LISP [...] y TRAC. Ambos están orientados funcionalmente (una lista, la otra cadena), ambos le hablan al usuario en un idioma y ambos son "homoicónicos" en el sentido de que sus representaciones internas y externas son esencialmente las mismas. Ambos tienen la capacidad de crear dinámicamente nuevas funciones que luego pueden ser elaboradas a gusto del usuario. Su único gran inconveniente es que los programas escritos en ellos se parecen a la carta del rey Burniburiach a los sumerios escrita en cuniforme babilónico. [...]

Usos y ventajas

Una ventaja de la homoiconicidad es que extender el lenguaje con nuevos conceptos generalmente se vuelve más simple, ya que los datos que representan el código se pueden pasar entre la meta y la capa base del programa. El árbol de sintaxis abstracta de una función puede componerse y manipularse como una estructura de datos en la metacapa y luego evaluarse . Puede ser mucho más fácil entender cómo manipular el código, ya que se puede entender más fácilmente como datos simples (ya que el formato del lenguaje en sí es un formato de datos).

Una demostración típica de homoiconicidad es el evaluador metacircular .

Métodos de implementación

Todos los sistemas de arquitectura Von Neumann , que incluyen la gran mayoría de las computadoras de uso general actuales, pueden describirse implícitamente como homoicónicos debido a la forma en que el código de máquina sin procesar se ejecuta en la memoria, siendo el tipo de datos bytes en la memoria. Sin embargo, esta característica también se puede abstraer al nivel del lenguaje de programación.

Lenguajes como Lisp y sus dialectos, [7] como Scheme , [8] Clojure y Racket emplean expresiones S para lograr homoiconicidad, y se consideran las formas "más puras" de homoiconicidad, ya que estos lenguajes usan la misma representación para ambos. datos y código.

Otros lenguajes proporcionan estructuras de datos para manipular código de manera fácil y eficiente. Ejemplos notables de esta forma más débil de homoiconicidad incluyen a Julia , Nim y Elixir .

Los idiomas que a menudo se consideran homoicónicos incluyen:

en ceceo

Lisp utiliza expresiones S como representación externa de datos y código. Las expresiones S se pueden leer con la función Lisp primitiva READ. READdevuelve datos Lisp: listas, símbolos , números, cadenas. La función Lisp primitiva EVALutiliza código Lisp representado como datos Lisp, calcula efectos secundarios y devuelve un resultado. El resultado será impreso por la función primitiva PRINT, que crea una expresión S externa a partir de datos de Lisp.

Datos Lisp, una lista que utiliza diferentes tipos de datos: (sub)listas, símbolos, 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.

( * ( pecado 1.1 ) ( cos 2.03 )) ; en infijo: pecado(1.1)*cos(2.03)     

Cree la expresión anterior con la función Lisp primitiva LISTy establezca la variable EXPRESSIONen el resultado

( setf expresión ( 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 )    

Cambiar el COStérmino aSIN

( setf ( primera ( tercera expresión )) 'SIN ) ; La expresión ahora es (* (SIN 1.1) (SIN 2.03)).    

Evaluar la expresión

( expresión de evaluación ) -> 0,7988834  

Imprime la expresión en una cadena.

( expresión de impresión en cadena ) -> "(* (SIN 1.1) (SIN 2.03))"  

Leer la expresión de una cadena.

( lectura de cadena "(* (SIN 1.1) (SIN 2.03))" ) -> ( * ( SIN 1.1 ) ( SIN 2.03 )) ; devuelve una lista de listas, números y símbolos       

En prólogo

1 ?- X es 2*5. X = 10.      2 ?-L = ( X es 2 *5 ) , write_canonical ( L ) . es ( _, * ( 2 , 5 )) L = ( X es 2*5 ) .             3 ? - L = ( diez ( X ) :-( X es 2*5 )) , write_canonical ( L ) . :- ( diez ( A ) , es ( A, * ( 2 , 5 ))) L = ( diez ( X ) :-X es 2*5 ) .              4 ? - L = ( diez ( X ) :-( X es 2*5 )) , afirmar ( L ) . L = ( diez ( X ) : -X es 2*5 ) .           5 ?- diez ( X ) . X = 10.    6 ?- 

En la línea 4 creamos una nueva cláusula. El operador :-separa el encabezado y el cuerpo de una cláusula. Con assert/1* lo agregamos a las cláusulas existentes (lo agregamos a la "base de datos"), para poder llamarlo más tarde. En otros idiomas lo llamaríamos "crear una función durante el tiempo de ejecución". También podemos eliminar cláusulas de la base de datos con abolish/1, o retract/1.

* El número después del nombre de la cláusula es el número de argumentos que puede tomar. También se le llama aridad .

También podemos consultar la base de datos para obtener el cuerpo de una cláusula:

7 ?- cláusula ( diez ( X ) , Y ) . Y = ( X es 2*5 ) .      8 ?- cláusula ( diez ( X ) , Y ) , Y = ( X es Z ) . Y = ( X es 2*5 ) , Z = 2*5.             9 ?- cláusula ( diez ( X ) , Y ) , llamada ( Y ) . X = 10 , Y = ( 10 es 2*5 ) .         

calles análoga a la función de Lisp eval.

En Rebol

El concepto de tratar el código como datos y su manipulación y evaluación se puede demostrar muy claramente en Rebol . (Rebol, a diferencia de Lisp, no requiere paréntesis para separar expresiones).

El siguiente es un ejemplo de código en Rebol (nota que >>representa el mensaje del intérprete; se han agregado espacios entre algunos elementos para facilitar la lectura):

>>repeat i 3 [ print [ i "hello" ] ]1 hola2 hola3 hola

( repeates de hecho una función incorporada en Rebol y no es una construcción del lenguaje ni una palabra clave).

Al encerrar el código entre corchetes, el intérprete no lo evalúa, sino que simplemente lo trata como un bloque que contiene palabras:

[ repetir  i  3 [ imprimir [ i  "hola" ] ] ]

¡Este bloque tiene el tipo bloque! y además se puede asignar como el valor de una palabra usando lo que parece ser una sintaxis de asignación, pero que en realidad el intérprete entiende como un tipo especial ( set-word!) y toma la forma de una palabra seguida de dos puntos:

>>  ;; Asigne el valor del bloque a la palabra `bloque1`block1: [ repeat i 3 [ print [ i "hello" ] ] ]== [repetir i 3 [imprimir [i "hola"]]]>>  ;; Evaluar el tipo de la palabra `block1`type? block1== bloquear!

El bloque aún se puede interpretar utilizando la dofunción proporcionada en Rebol (similar a evalLisp ).

Es posible interrogar los elementos del bloque y cambiar sus valores, alterando así el comportamiento del código si fuera evaluado:

>>  ;; El tercer elemento del bloque.block1/3== 3>>  ;; Establezca el valor del tercer elemento en 5block1/3: 5== 5>>  ;; Mostrar el bloque modificadoprobe block1== [repetir i 5 [imprimir [i "hola"]]]>>  ;; Evaluar el bloquedo block11 hola2 hola3 hola4 hola5 hola

Ver también

Notas

  1. ^ Las versiones anteriores de esta página de Wikipedia 2006-2023 combinaron la nota 5 no relacionada del artículo TRAC anterior, lo que resultó en la afirmación errónea de que el término "homoicónico" tenía su origen en un artículo sobre procesamiento macro de Douglas McIlroy. Ese artículo no menciona ninguna terminología ni siquiera remotamente similar; su influencia en la labor del TRAC es de diferente naturaleza. Desde entonces, algunas fuentes han repetido esta afirmación. [4]

Referencias

  1. ^ ab Ceravola, Antonello; Joublin, Frank (2021). "De JSON a JSEN a través de Lenguajes Virtuales". Revista Abierta de Tecnologías Web . 8 (1): 1–15. En un lenguaje homoicónico, la representación principal de los programas es también una estructura de datos en un tipo primitivo del lenguaje mismo.
  2. ^ Wheeler, David A. "Expresiones S legibles en Lisp".
  3. ^ Mooers, CN ; Alemán, LP (1965). "TRAC, un lenguaje de manejo de texto". Actas ACM '65 Actas de la vigésima conferencia nacional de 1965 . págs. 229–246. doi :10.1145/800197.806048.
  4. ^ Mannaert, Herwig; McGroarty, Chris; Galante, Scott; De Cock, Koen; Gallogly, Jim; Raval, Anup; Snively, Keith (2022). "Hacia una metaprogramación colaborativa escalable: un estudio de caso para integrar dos entornos de metaprogramación" (PDF) . Revista internacional sobre avances en software . 15 : 128-140. ISSN  1942-2628.
  5. ^ "No digas" homoicónico"". Expresiones de cambio . 1 de marzo de 2018.
  6. ^ Kay, Alan (1969). El motor reactivo (Doctor). Universidad de Utah.
  7. ^ abcdefghi Lenguas homoicónicas
  8. ^ ab Lenguajes homoicónicos (archivado), en el blog True Blue en Oracle
  9. ^ "Elixir lispy". 8thlight.com . El elixir, en la superficie, no es homoicónico. Sin embargo, la sintaxis en la superficie es sólo una fachada para la estructura homoicónica que se encuentra debajo.
  10. ^ "Por qué creamos Julia". julialang.org . Queremos un lenguaje que sea homoicónico, con macros verdaderas como Lisp, pero con notación matemática obvia y familiar como Matlab.
  11. ^ "metaprogramación". docs.julialang.org . Al igual que Lisp, Julia representa su propio código como una estructura de datos del propio lenguaje.
  12. ^ Shapiro, Ehud Y.; Sterling, León (1994). El arte de Prolog: técnicas de programación avanzadas . Prensa del MIT. ISBN 0-262-19338-8.
  13. ^ Ramsay, S.; Pytlik-Zillig, B. (2012). "Técnicas de generación de código para la interoperabilidad de colecciones XML". Actas de la conferencia de humanidades digitales dh2012 .
  14. ^ "Notas para expertos en lenguajes de programación". Lenguaje Wolfram . Wolframio. 2017.

enlaces externos