Omoiconicitate ( homoiconicitate , ing. homoiconicitate , ing. homoiconic , din greaca ὁμός - egal, identic + „iconicitate” - relația de asemănare dintre semn și obiectul către care se referă acest semn (vezi semiotica ) - la rândul său, din cf. - Greaca εἰκόνα - „imagine”, „imagine”) este o proprietate a unor limbaje de programare în care structura programului este similară cu sintaxa sa și, prin urmare, reprezentarea internă a programului poate fi determinată prin citirea textului de marcare [ 1] . Dacă un limbaj este homoiconic, înseamnă că textul programului are aceeași structură ca arborele său de sintaxă abstractă (adică AST și sintaxa sunt izomorfe ). Acest lucru permite accesul și procesarea întregului cod din limbă ca date folosind aceeași reprezentare.
Într-un limbaj homoiconic, reprezentarea primară a programelor este, de asemenea, o structură de date în tipul primitiv al limbajului însuși. Acest lucru face metaprogramarea mai ușoară decât într-un limbaj fără această proprietate, deoarece codul poate fi văzut ca date : reflectarea în limbaj (determinarea structurii unui program în timpul execuției ) se bazează pe o singură structură omogenă și nu este nevoie să se ocupe mai multe constructe diferite care apar în structuri sintactice complexe. Cu alte cuvinte, homoiconicitatea este atunci când codul sursă al unui program este scris ca o structură de date de bază și limbajul de programare știe cum să-l acceseze.
Un exemplu tipic este limbajul de programare Lisp , care a fost conceput pentru a fi ușor pentru manipularea listelor și în care structura este dată ca expresii S , care iau forma unor liste imbricate. Programele Lisp sunt scrise ca liste; rezultatul este că programul își poate accesa propriile funcții în timpul rulării, precum și se poate reprograma din mers. Limbile homoiconice tind să includă suport complet pentru macro-urile sintactice , permițând programatorului să exprime transformările de programare într-o manieră concisă. Exemple de astfel de limbaje de programare sunt Clojure (un dialect modern al Lisp), Rebol și Refal .
Termenul a fost menționat pentru prima dată într-un articol din 1960 al lui Doug McIlroy [2] , care a fost la rândul său referit într-o lucrare din 1965 de Calvin Moores și Peter Deutsch , în care proprietatea a fost prezentată ca cheie pentru programarea TRAC limbaj pe care l-au dezvoltat [3] .
Alan Kay a folosit și poate să fi popularizat termenul de „homoiconicitate”, folosindu-l în teza sa de doctorat privind proprietățile respective ale Lisp și limbajul TRAC [4] , remarcând costurile de lizibilitate ale programelor în această abordare: „programs written in ele arată ca scrisoarea regelui Burna-Buriash către sumerieni tipărită în cuneiform babilonian” .
Unul dintre avantajele de a fi homoiconic este că extinderea limbajului cu concepte noi tinde să fie mai ușoară, deoarece datele care reprezintă codul pot fi transmise între straturile meta și de bază ale unui program. Arborele de sintaxă abstractă al unei funcții poate fi construit și modificat ca structură de date metalayer și apoi executat . Poate fi mult mai ușor să vă dați seama cum să manipulați codul, deoarece poate fi mai ușor de înțeles ca date simple (deoarece formatul unei limbi este același cu formatul său de date).
Simplitatea care permite acest lucru este, de asemenea, un dezavantaj: cel puțin în cazul limbilor orientate pe liste asemănătoare Lisp, acest lucru poate scăpa de multe dintre indicațiile vizuale care îi ajută pe oameni să analizeze vizual constructele limbajului și acest lucru poate duce la o creștere în curba de învăţare pentru limba [5 ] . Vezi și eseul „Blestemul lui Lisp” [6] pentru o descriere a neajunsurilor.
O demonstrație tipică a homoiconicității este calculatorul metacircular .
Limbaje de programare homoiconice:
În sistemele de arhitectură von Neumann (inclusiv marea majoritate a calculatoarelor moderne), codul mașinii are și această proprietate, cu un tip de date de octeți în memorie.
Lisp folosește expresiile S ca o reprezentare externă a datelor și codului. Expresiile S pot fi citite folosind o funcție primitivă READcare returnează tipurile de bază Lisp: liste, caractere, numere, șiruri. O funcție primitivă Lisp EVALutilizează acest cod, reprezentat ca date Lisp, pentru a evalua efectele secundare și a returna rezultatul.
Un exemplu de date în Lisp este o listă care utilizează diferite tipuri de date: (sub)liste, caractere, șiruri și numere întregi:
(( :nume „john” :varsta 20 ) ( :nume „mary” :varsta 18 ) ( :nume „alice” :varsta 22 ))Cod Lisp. Exemplul folosește liste, simboluri și numere:
( * ( sin 1.1 ) ( cos 2.03 ) ) ; în infix: sin(1.1)*cos(2.03)Crearea unei astfel de expresii cu o funcție primitivă LISTși atribuirea rezultatului unei variabile expression:
( expresie setf ( lista '* ( lista ' sin 1.1 ) ( lista ' cos 2.03 )) ) -> ( * ( SIN 1.1 ) ( COS 2.03 )) ; Lisp revine și tipărește rezultatul ( a treia expresie ) ; al treilea element al expresiei -> ( COS 2.03 )Înlocuirea termenului COScu SIN:
( setf ( prima ( a treia expresie )) 'SIN ) ; Expresia este acum (* (SIN 1.1) (SIN 2.03)).Executarea expresiei:
( expresie eval ) -> 0,7988834Tipăriți această expresie într-un șir:
( expresie tipărită în șir ) -> „(* (SIN 1.1) (SIN 2.03))”Scădeți o expresie dintr-un șir:
( citit-din-șir „(* (SIN 1.1) (SIN 2.03))” ) -> ( * ( SIN 1.1 ) ( SIN 2.03 )) ; returnează o listă de liste, numere și simboluri