Pagini

2012-12-01

Practical Common Lisp - 2. Sapuneste, Clateste, Repeta: un tur al REPL

Copyright © Peter Seibel
Traducere © Razvan Popa

2. Sapuneste, Clateste, Repeta: Un Tur al REPL

In acest capitol iti vei configura mediul de programare si vei scrie primele programe in Common Lisp. Vom folosi Lisp in a Box,



dezvoltat de Matthew Danish si Mikel Evins, care impacheteaza o implementare de Common Lisp, Emacs, un editor de text si SLIME1, un mediu de dezvoltare Common Lisp construit pe Emacs.
Aceasta combinatie este un mediu de dezvoltare excelent pentru Common Lisp, suportand stilul de programare incremental si interactiv care caracterizeaza programarea in Lisp. Mediul SLIME are in plus avantajul de a pune la dispozitie o interfata in mare parte uniforma, indiferent de sistemul de operare si implementatia Common Lisp aleasa. Voi folosi mediul Lisp in a Box pentru a avea un mediu de dezvoltare specific pe baza caruia sa vorbesc; cei care vor sa incerce alte medii cum ar fi cele integrate (Integrated Development Environment - IDE) vandute de unii comercianti de Lisp sau medii pe baza altor editoare nu ar trebui sa aiba prea mari probleme in traducerea elementelor de baza.2

(pe scurt ▼)


Cum se Alege o Implementare de Lisp


Primul lucru pe care trebuie sa-l faci este sa alegi o implementare de Lisp. Asta poate parea ciudat celor obosnuiti cu limbaje ca Perl, Python, Visual Basic (VB), C# si Java. Spre deosebire de aceste limbaje, Common Lisp este definit ca standard - nu exista o singura implementare controlata de un dictator binevoitor, cum sunt Perl si Python, dar nici o implementare canonica, controlata de o singura comopanie, cum sunt VB, C# si Java. Oricine vrea sa citeasca standardul si sa implementeze limbajul este liber sa o faca. Mai mult, schimbarile in stadard trebuie sa fie facute conform unui proces controlat de Institutul American National de Standarde (American National Standards Institute - ANSI). Procesul respectiv este gandit in asa fel incat sa impiedice o singura entitate, oricare ar fi ea, de a face modificari arbitrare in standard.3 Astfel, standardul Common Lisp este un contract intre orice furnizor de Common Lisp si programatorii Common Lisp. Contractul spune ca daca scrii un program care foloseste capabilitatile limbajului in modul descris in standard, atunci poti fi sigur ca programul se va comporta la fel in orice implementare conforma cu standardul.
Pe de alta parte, standardul poate sa nu acopere toate lucrurile pe care doresti sa le faci in programe - unele lucruri au fost lasate nespecificate intentionat pentru a permite continuarea experimentarii de catre implementatori in zone in care nu exista acord in legatura cu cel mai bun mod in care limbajul putea suporta anumite capabilitati. Fiecare implementare ofera capabilitati peste ceea ce este specificat in standard. In functie de tipul de programare, ar putea avea sens sa alegi pur si simplu o implementare care are facilitatile necesare si sa o folosesti. Pe de alta parte, daca furnizezi surse Lisp pentru uzul celorlalti, cum ar fi biblioteci, atunci vei dori sa scrii cod portabil pe cat posibil. Pentru a putea scrie cod portabil pe cat posibil dar care are nevoie de facilitati care nu apar in standard, Common Lisp pune la dispozitie o modalitate flexibila de a scrie cod "conditionalizat" pe capabilitatile existente intr-o anumita implementare. Vei vedea un exemplu de cod in stilul asta in Capitolul 15 cand vom dezvolta o librarie simpla care netezeste cateva din diferentele dintre modurile in care implementarile Lisp administreaza numele fisierelor.
Pentru moment, cea mai importanta caracteristica a unei implementari este daca ruleaza pe sistemul tau de operare. Cei de la Franz, furnizorii Allegro Common Lisp, au o versiune de incercare a produsului lor special pentru aceasta carte care ruleaza pe Linux, Windows si OS X. Cei care vor o implementare open-source au mai multe optiuni. SBCL4 este o implementare foarte buna care compileaza in cod nativ si ruleaza pe o mare varietate de Unix-uri, inclusiv Linux si OS X. SBCL e derivat din CMUCL,5 care este un Common Lisp dezvoltat la Universitatea Carnegie Mellon (Carnegie Mellon University - CMU) si care este in mare parte in domeniu public, cu exceptia catorva sectiuni cu licenta BSD (Berkeley Software Distribution). CMUCL insusi este o alta alegere buna, desi SBCL tinde sa fie mai usor de instalat si acum suporta Unicode pe 21 biti.6 Pentru utilizatorii de OS X, OpenMCL este o alegere excelenta - compileaza in cod masina, suporta fire de executie si este integrat de stul de bine cu bibliotecile Cocoa si Carbon din OS X. Exista si alte implementari open-source si comerciale. Vezi Capitolul 32 pentru resurse din care poti afla mai multe informatii.
Tot codul Lisp din aceasta carte ar trebui sa functioneze in orice implementare Common Lisp conforma cu standardul, daca nu este semnalat contrariul, si SLIME va netezi unele din diferentele intre implementari punandu-ne la dispozitie o interfata cu Lisp comuna. Rezultatele din aceasta carte sunt rezultate din Allegro pe GNU/Linux; in unele cazuri, alte Lisp-uri ar putea genera mesaje de eroare sau rezultate de depanare usor diferite.

Cum se porneste Lisp in a Box


Deoarece pachetul Lisp in a Box este proiectat sa poata fi folosit foarte usor de noii programatori Lisp, nu trebuie decat sa iei pachetul potrivit pentru sistemul tau de operare si Lisp-ul preferat de pe site-ul Lisp in a Box, a carui adresa o poti gasi in Capitolul 32, si apoi sa urmezi instructiunile de instalare.
Deoarece Lisp in a Box foloseste Emacs ca si editor, va trebui sa inveti macar un pic din el pentru a sti sa-l folosesti. Probabil cel mai bun mod de a incepe este sa citesti tutorialul. Pentru a porni tutorialul selecteaza primul element din meniul de Ajutor, tutorial Emacs. Sau apasa tasta Ctrl, tasteaza h, da drumul la tasta Ctrl key si apoi tasteaza t. Cele mai multe comenzi Emacs sunt accesibile prin combinatii de taste similare; deoarece combinatiile sunt atat de comune, utilizatorii de Emacs au o notatie pentru descrierea combinatiilor in asa fel incat sa evite sa scrie tot timpul "Apasa tasta Ctrl, tasteaza h, da drumul la tasta Ctrl key si apoi tasteaza t." Tastele care trebuie apasate simultan - asa numita "chord", ii vom zice "combinatie" - sunt scrise impreuna si separate de o cratima. Tastele, sau combinatiile, care trebuie apasate in secventa sunt separate de spatii. Intr-o combinatie de taste C reprezinta tasta Ctrl si M reprezinta tasta Meta (cunoscuta si ca Alt). Deci, am putea scrie combinatia de taste pe care tocmai am descris-o astfel: C-h t.
Tutorialul descrie si alte comenzi utile si combinatiile care le invoca. Emacs are si documentatie extinsa in format electronic folosind propriul browser hypertext, Info. Pentru a citi manualul, tastati C-h i. Sistemul Info are propriul tutorial, accesibil prin apasarea tastei h in timp ce citesti manualul. In sfarsit, Emacs pune la dispozitie o seama de modalitati de a primi ajutor, toate legate de combinatii de taste incepand cu C-h. Daca tastezi C-h ? este afisata lista completa. Doua dintre cele mai utile comenzi, in afara de tutorial, sunt C-h k, care ne spune ce comanda invoca o combinatie de taste introdusa de noi, si C-h w, care ne spune, pentru numele unei comenzi introdus de noi, ce combinatie de taste invoca respectiva comanda.
Cealalta parte cruciala de terminologie Emacs, pentru cei care refuza sa parcurga tutorialul, este notiunea de buffer. In timpul lucrului in Emacs, fiecare fisier pe care-l editezi va fi reprezentat de un buffer diferit, unul singur fiind "current" in orice moment. Buffer-ul curent este cel care primeste toate intrarile - orice scrii si orice comanda invoci. Buffer-ele sunt folosite si pentru a reprezenta interactiunile cu programe cum ar fi Common Lisp. Astfel, o actiune comuna pe care o vei lua va fi sa "schimbi buffer-ele", ceea ce inseamna ca vei face ca un alt buffer sa fie curent, incat sa poti edita un anume fisier sau interactiona cu un anume program. Comanda switch-to-buffer, legata la combinatia de taste C-x b, cere numele unui buffer in zona de jos a ferestrei Emacs. Cand introduci un nume de buffer, daca apesi Tab iti va aparea o lista cu completarile posibil. Linia de comanda sugereaza si un buffer implicit, pe care il poti accepta apasand Enter. Poti sa schimbi buffer-ele si prin selectarea unuia din meniul Buffers.
In anumite contexte, alte combinatii de taste ar putea sa fie disponibile pentru a schimba din anumite buffer-e. De exemplu, cand editezi fisiere sursa Lisp, combinatia C-c C-z schimba in buffer-ul in care interactionezi cu Lisp.

Elibereaza-ti Mintea: Programare Interactiva


Cand pornesti Lisp in a Box, ar trebui sa vezi un buffer continand o linie de comanda care arata astfel:
CL-USER>
Aceasta este linia de comanda Lisp. La fel ca o linie de comanda Unix sau DOS, cea din Lisp este locul in care poti sa scrii expresii care fac sa se intample diverse lucruri. Dar, spre deosebire de citirea si interpretarea unei linii de comenzi din Unix sau DOS, Lisp citeste expresii Lisp, le evalueaza conform regulilor Lisp si afiseaza rezultatul. Apoi face acelasi lucru cu urmatoarea expresie pe care o scrii. Acest ciclu nesfarsit de citire, evaluare si afisare este motivul pentru care este numit Bucla Citeste-Evalueaza-Afiseaza (Read-Eval-Print Loop - REPL). Mai este cunoscuta si ca top-level, top-level listener, sau Lisp listener.
Din mediul pus la dispozitie de REPL, poti sa definesti si sa redefinesti elemente de program ca variabile, functii, clase si metode; sa evaluezi orice expresie Lisp; sa incarci fisiere continand cod Lisp, sursa sau compilat; sa compilezi fisiere intregi sau functii individuale; sa intri in depanator; sa rulezi codul pas cu pas; si sa inspectezi starea obiectelor individuale din Lisp.
Toate aceste facilitati sunt construite in limbaj, accesibile prin functii definite in standardul limbajului. Daca ai avea nevoie, ai putea sa construiesti un mediu de programare rezonabil folosind doar REPL si un editor de text care sa stie sa indenteze surse Lisp. Dar pentru o experienta de programare in Lisp adevarata e nevoie de un mediu, ca SLIME, care te lasa sa interactionezi cu Lisp atat prin REPL cat si in timpul editarii fisierelor. De exemplu, nu vrei sa trebuiasca sa tai si apoi sa lipesti o definitie de functie dintr-un fisier sursa in REPL sau sa trebuiasca sa incarci un fisier intreg doar pentru ca ai schimbat o functie; mediul tau Lisp ar trebui sa te lase sa evaluezi sau sa compilezi atat expresii individuale cat si fisiere intregi direct din editor.

Experimentand in REPL


Pentru a incerca REPL, ai nevoie de o expresie Lisp care sa poata fi citita, evaluata si afisata. Unul din cele mai simple tipuri de expresii Lisp este un numar. La linia de comanda Lisp poti sa scrii 10 apoi Enter si vei vedea ceva in genul asta:
CL-USER> 10
10
Primul 10 este cel introdus de la tastatura. Cititorul Lisp, R din REPL, citeste textul "10" si creeaza un obiect Lisp reprezentand numarul 10. Acest obiect este auto-evaluator, ceea ce inseamna ca atunci cand este trimis evaluatorului, E-ul din REPL, este evaluat ca el insusi. Valoarea este apoi trimisa printerului, care afiseaza 10 singur pe linie. Asta poate sa para ca multa munca doar pentru a te intoarce de unde ai plecat, dar lucrurile devin un pic mai interesante cand Lisp primeste ceva mai consistent. De exemplu, poti sa scrii (+ 2 3) la linia de comanda.
CL-USER> (+ 2 3)
5
Orice apare in paranteze este o lista, in acest caz o lista de trei elemente, simbolul + si numerele 2 si 3. Lisp, in general, evalueaza listele tratand primul element ca numele unei functii si restul elementelor ca expresii ce trebuiesc evaluate pentru a gasi argumentele trimise functiei. In acest caz, simbolul + denumeste o functie care face adunari. 2 si 3 sunt evaluate ca 2 si 3 si apoi sunt trimise functiei adunare, care returneaza 5. Valoarea 5 este trimisa printer-ului, care o afiseaza. Lisp poate sa evalueze o expresie-lista in alte moduri, dar nu trebuie deocamdata sa ne preocupam de acest lucru. Mai intai trebuie sa scriem. . .

"Hello, World," in Stil Lisp


Nici o carte de programare nu este completa fara un program "hello, world"7. Dupa cum vom vedea, este trivial sa facem REPL sa afiseze "hello, world."
CL-USER> "hello, world"
"hello, world"
Aceasta functioneaza deoarece sirurile de caractere, la fel ca numerele, au o sintaxa literala care e inteleasa de cititorul Lisp si sunt obiecte auto-evaluatoare: List citeste sirul de caractere dintre ghilimele si instantiaza un obiect de tip string (sir de caractere) in memorie care se evalueaza ca el insusi si apoi este afisat cu aceeasi sintaxa literala. Ghilimelele nu fac parte din obiectul string in memorie - sunt doar sintaxa care spun cititorului ca urmeaza un sir de caractere. Printer-ul le afiseaza deoarece incearca sa afiseze obiectele in sintaxa pe care o intelege cititorul.
Totusi, asta nu e chiar un program "hello, world". Este mai degraba valoarea "hello, world".
Poti sa mai faci un pas catre un program adevarat scriind un pic de cod care ca efect secundar afiseaza sirul de caractere "hello, world" la iesirea standard. Common Lisp pune la dispozitie mai multe moduri de a emite caractere, dar cea mai flexibila este functia FORMAT. FORMAT primeste un numar variabil de argumente, dar singurele doua argumente obligatorii sunt locul catre care trebuie trimise datele si un sir de caractere. Vei vedea in capitolul urmator cum sirul de caractere poate sa contina directive inglobate care iti permit sa interpolezi argumente ulterioare in string, à la printf sau string-% din Python. Atata timp cat string-ul nu contine ~, el va fi emis nemodificat. Daca transmiti t ca prim argument, datele vor fi trimise la iesirea standard. Deci o expresie FORMAT care afiseaza "hello, world" arata in genul asta:8
CL-USER> (format t "hello, world")
hello, world
NIL
Un lucru ar trebui observat in privinta rezultatului expresiei FORMAT: expresia NIL de pe linia de dupa rezultatul "hello, world". Acel NIL este rezultatul evaluarii expresiei FORMAT, afisat de catre REPL. (NIL este versiunea de false si/sau null din Lisp. Mai multe detalii despre asta in Capitolul 4.) Spre deosebire de celelalte expresii vazute pana acum, o expresie FORMAT este mai interesanta pentru efectele secundare - afisarea la iesirea standard in cazul de fata - decat pentru valoarea returnata. Dar fiecare expresie in Lisp este evaluata la un rezultat.9
Oricum, inca este discutabil daca ai scris un "program." adevarat. Dar ajungem si acolo. Si astfel poti vedea stilul "de jos" (bottom-up) de programare suportat in REPL: poti sa experimentezi cu mai multe abordari si sa construiesti o solutie din piese pe care deja le-ai testat. Acum ca ai o expresie simpla care face ceea ce vrei tu, nu mai trebuie decat sa o impachetezi intr-o functie. Functiile sunt unul din tipurile de baza de construire a programelor in Lisp si pot fi definite cu o expresie DEFUN in genul asta:
CL-USER> (defun hello-world () (format t "hello, world"))
HELLO-WORLD
hello-world de dupa DEFUN este numele functiei. In Capitolul 4 ne vom uita exact la ce caractere sunt permise intr-un nume, dar deocamdata e suficient sa spunem ca multe caractere, cum ar fi -, care nu pot fi folosite in identificatori in alte limbaje sunt permise in Common Lisp. Este stilul standard Lisp - ca sa nu spunem ca e mai natural si ca limbaj uman - sa formezi nume compuse cu cratime, ca in hello-world, decat cu underscore, ca in hello_world, sau cu majuscule in interiorul cuvantului, ca in helloWorld. Parantezele ()s de dupa nume delimiteaza lista de parametri, care este goala in acest caz deoarece functia nu primeste nici un argument. Restul este corpul functiei.
Aceasta expresie este, la fel cu celelalte de pana acum, doar o alta expresie de citit, evaluat si afisat de catre REPL. Valoarea returnata in acest caz este numele functiei pe care tocmai ai definit-o.10 Dar la fel cu expresia FORMAT, aceasta expresie este mai interesanta pentru efectele secundare decat pentru valoarea returnata. Spre deosebire de expresia FORMAT, totusi, efectele secundare sunt invizibile: cand aceasta expresie este evaluata, o functie noua care nu primeste nici un argument si care are corpul (format t
"hello, world")
este creata si i se da numele HELLO-WORLD.
Odata ce ai definit functia, poti sa o apelezi in felul acesta:
CL-USER> (hello-world)
hello, world
NIL
Poti sa vezi ca rezultatul este la fel ca atunci cand ai evaluat expresia FORMAT direct, inclusiv valoarea NIL afisata de REPL. Functiile din Common Lisp returneaza automat valoarea ultimei expresii evaluate.

Cum sa Iti Salvezi Munca


Ai putea spune ca este un program "hello, world" complet. Totusi, inca are o problema. Daca iesi din Lisp si pornesti din nou, definitia functiei nu va mai fi acolo. Dupa ce ai scris asa o functie faina, vei vrea sa-ti salvezi munca.
Destul de usor. Nu trebuie decat sa creezi un fisier in care sa salvezi definitia. In Emacs poti crea un fisier nou tastand C-x C-f si apoi, la cererea Emacs, introducand numele fisierului pe care vrei sa-l creezi. Nu conteaza unde pui fisierul. Se obisnuieste sa se puna extensia .lisp la fisierele sursa Lisp, desi unii folosesc in schimb .cl.
Odata ce ai creat fisierul, poti sa tastezi definitia de mai devreme. E util de stiut ca odata ce ai tastat paranteza de deschidere si cuvantul DEFUN, SLIME iti va arata argumentele asteptate, in josul ferestrei Emacs. Forma exacta va depinde de implementarea folosita, dar probabil va arata ceva in genul asta:
(defun name varlist &rest body)
Mesajul va disparea pe masura ce scrii fiecare element nou si va aparea de fiecare data cand introduci un spatiu. Cand introduci definitia in fisier, s-ar putea sa treci pe alta linie dupa ce scrii lista de parametri. Daca apesi Enter si apoi Tab, SLIME va indenta automat a doua linie corespunzator:11
(defun hello-world ()
  (format t "hello, world"))
SLIME ajuta si la imperecherea parantezelor - cand tastezi o paranteza de inchidere, paranteza de deschidere corespunzatoare va clipi. Sau poti sa tastezi C-c C-q si va fi invocata comanda slime-close-parens-at-point, care va introduce atatea paranteze de inchidere cate sunt necesare pentru toate parantezele deja introduse.
Acum poti sa introduci definitia aceasta in Lisp in mai multe feluri. Cel mai usor este sa tastezi C-c C-c avand cursorul oriunde in forma DEFUN sau imediat dupa ea, ceea ce ruleaza comanda slime-compile-defun, care trimite definitia in Lisp spre a fi evaluata si compilata. Ca sa te asiguri ca metoda asta functioneaza, poti sa faci niste schimbari in hello-world, sa o recompilezi si apoi sa te intorci in REPL, tastand C-c C-z sau C-x b, si sa o apelezi din nou. De exemplu, ai putea sa o faci mai corecta din punct de vedere gramatical.
(defun hello-world ()
  (format t "Hello, world!"))
Apoi, recompileaza cu C-c C-c si apoi tasteaza C-c C-z pentru a te intoarce in REPL ca sa incerci versiunea noua.
CL-USER> (hello-world)
Hello, world!
NIL
Probabil vei dori sa salvezi fisierul in care lucrai; in buffer-ul hello.lisp, tasteaza C-x C-s ca sa invoci comanda Emacs save-buffer.
Pentru a incerca sa reincarci functia din fisierul de pe disc, va trebui sa iesi din Lisp si sa il pornesti din nou. Pentru a iesi poti sa folosesti o scurtatura din SLIME: tasteaza o virgula la REPL. In josul ferestrei Emacs ti se va cere sa introduci o comanda. Tasteaza quit (ori sayoonara), si apoi apasa Enter. Lisp se va termina si va inchide toate buffer-ele create de SLIME, cum ar fi buffer-ul REPL12. Apoi porneste SLIME din nou tastand M-x slime.
Doar de amuzament, poti sa incerci sa invoci hello-world.
CL-USER> (hello-world)
In acel moment SLIME va deschide un nou buffer care incepe cam asa:
attempt to call `HELLO-WORLD' which is an undefined function.
   [Condition of type UNDEFINED-FUNCTION]

Restarts:
  0: [TRY-AGAIN] Try calling HELLO-WORLD again.
  1: [RETURN-VALUE] Return a value instead of calling HELLO-WORLD.
  2: [USE-VALUE] Try calling a function other than HELLO-WORLD.
  3: [STORE-VALUE] Setf the symbol-function of HELLO-WORLD and call it again.
  4: [ABORT] Abort handling SLIME request.
  5: [ABORT] Abort entirely from this process.

Backtrace:
  0: (SWANK::DEBUG-IN-EMACS #<UNDEFINED-FUNCTION @ #x716b082a>)
  1: ((FLET SWANK:SWANK-DEBUGGER-HOOK SWANK::DEBUG-IT))
  2: (SWANK:SWANK-DEBUGGER-HOOK #<UNDEFINED-FUNCTION @ #x716b082a> #<Function SWANK-DEBUGGER-HOOK>)
  3: (ERROR #<UNDEFINED-FUNCTION @ #x716b082a>)
  4: (EVAL (HELLO-WORLD))
  5: (SWANK::EVAL-REGION "(hello-world)
" T)
Bang! Ce s-a intamplat? Pai, ai incercat sa invoci o functie care nu exista. Dar, in ciuda puhoiului de informatii, Lisp administreaza situatia destul de bine. Spre deosebire de Java ori Python, Common Lisp nu renunta pur si simplu - aruncand o exceptie si descarcand (unwinding) stiva. Si categoric nu moare (dump core) doar pentru ca ai incercat sa apelezi o functie care nu era acolo. In loc de asta Lisp deschide depanatorul.
In timp ce esti acolo inca ai acces la Lisp, deci poti evalua expresii pentru a examina starea programului si poate chiar pentru a remedia problema. Deocamdata nu te gandi prea mult la asta; tasteaza doar q ca sa iesi din depanator si sa te intorci la REPL. Buffer-ul depanatorului va disparea si REPL va afisa urmatorul text:
CL-USER> (hello-world)
; Evaluation aborted
CL-USER>
Evident ai mai multe variante in depanator, pe langa renuntare - vom vedea, de exemplu, in Capitolul 19 cum se integreaza depanatorul cu sistemul de tratare a erorilor. Deocamdata, cel mai important lucru de stiut este ca poti oricand sa iesi inapoi la REPL tastand q.
In REPL poti sa incerci din nou. Lucrurile nu au mers tocmai bine din cauza ca Lisp nu stia definitia lui hello-world. Deci trebuie sa ii spui lui Lisp despre definitia pe care am salvat-o in fisierul hello.lisp. Ai mai multe moduri in care poti sa faci asta. Poti sa te intorci in buffer-ul in care este fisierul (tasteaza C-x b si apoi scrii hello.lisp cand ti se cere) si sa recompilezi definitia cum ai facut si inainte cu C-c C-c. Sau poti sa incarci tot fisierul, ceea ce ar putea fi o abordare mai comoda daca fisierul ar contine mai multe definitii, folosind functia LOAD din REPL astfel:
CL-USER> (load "hello.lisp")
; Loading /home/peter/my-lisp-programs/hello.lisp
T
T-ul inseamna ca totul s-a incarcat corect.13 Incarcarea unui fisier cu comanda LOAD este in mare parte echivalent cu a tasta fiecare expresie din fisier la REPL in ordinea in care apar in fisier, prin urmare dupa ce apelezi LOAD, hello-world ar trebui sa fie definit:
CL-USER> (hello-world)
Hello, world!
NIL
Alt mod de a incarca definitiile dintr-un fisier este sa compilezi fisierul inainte cu COMPILE-FILE si apoi sa incarci cu LOAD fisierul compilat rezultat, de tip FASL, care este prescurtarea pentru fast-load (incarcare rapida). COMPILE-FILE returneaza numele fisierului FASL, deci putem compila si incarca din REPL in felul urmator:
CL-USER> (load (compile-file "hello.lisp"))
;;; Compiling file hello.lisp
;;; Writing fasl file hello.fasl
;;; Fasl write complete
; Fast loading /home/peter/my-lisp-programs/hello.fasl
T
SLIME are suport si pentru incarcarea si compilarea fisierelor fara a folosi REPL. Cand esti in buffer-ul sursa, poti folosi C-c C-l pentru a incarca fisierul cu comanda slime-load-file. Emacs te va intreba ce fisier sa incarce, varianta implicita fiind fisierul curent; poti sa tastezi direct Enter. Sau poti sa tastezi C-c C-k pentru a compila si incarca fisierul reprezentat de buffer-ul curent. In unele implementari Lisp, daca se compileaza codul in acest mod il va face sensibil mai rabit; in altele, lucrul asta nu se va intampla, de obicei din cauza ca ele compileaza intotdeauna totul.
Asta ar trebui sa fie suficient ca sa iti dai seama cam cum functioneaza programarea in Lisp. Desigur ca n-am acoperit toate tehnicile inca, dar ai vazut elementele esentiale - cum se interactioneaza cu REPL, cum se incearca diverse variante, cum se incarca si testeaza codul, cum se optimizeaza (tweak) si cum se depaneaza. Hacker-ii seriosi in Lisp ruleaza o imagine de Lisp zile in sir, adaugand, redefinind si testant incremental bucati din cod.
Chiar si cand se distribuie o aplicatie Lisp inca mai exista o metoda de a ajunge la REPL. Vei vedea in Capitolul 26 cum poti folosi REPL si SLIME pentru a interactiona cu un Lisp care ruleaza un server Web chiar in timp ce acesta serveste pagini. Este posibil chiar sa folosesti SLIME pentru a te conecta la Lisp pe alt calculator, permitandu-ti - de exemplu - sa depanezi un server la distanta la fel ca unul local.
O secventa de depanare la distanta chiar mai impresionanta a avut loc in misiunea NASA Deep Space 1 din 1998. La jumatate de an dupa lansare, o bucatica de cod Lisp trebuia sa controleze naveta timp de doua zile in timp ce aveau loc niste experimente. Din pacate, o subtila conditie de cursa din cod a scapat din vedere in timpul testarii de pe pamant si se afla deja in spatiu. Cand bug-ul a aparut in exploatare - la 150 de milioane de km de Pamant - echipa a putut sa diagnosticheze si sa repare codul, permitand completarea experimentelor.14 Unul din programatorii respectivi spunea:
Depanarea unui program care ruleaza pe un echipament de 100 milioane de USD la 150 de milioane de km distanta e o experienta interesanta. Rularea unui REPL pe naveta s-a dovedit de nepretuit in gasirea si rezolvarea problemei.
Inca nu esti gata sa trimiti cod Lisp in spatiu, dar in capitolul urmator vei incerca sa scrii un program un pic mai interesant decat "hello, world."


1Superior Lisp Interaction Mode for Emacs (Mod Superior de Interactiune cu Lisp pentru Emacs)
2Daca ai avut experiente negative cu Emacs inainte, ar trebui sa tratezi Lisp in a Box ca pe un IDE (Integrated Development Environment) care se intampla sa foloseasca un editor gen Emacs; nu trebuie sa fii guru in Emacs ca sa programezi in Lisp. Dar este mult mai placut sa programezi Lisp intr-un editor care "stie" macar un pic de Lisp. Ca minime capabilitati, sa poata sa potriveasca parantezele si sa indenteze cod Lisp in mod automat. Din cauza ca Emacs e scris el insusi in mare parte intr-un dialect de Lisp, Elisp, are ceva suport pentru editarea de cod Lisp. Emacs este si profund inglobat in istoria Lisp si in cultura hacker-ilor Lisp: Emacs-ul original si predecesorii imediati, TECMACS si TMACS, au fost scrise de programatori Lisp de la Massachusetts Institute of Technology (MIT). Editoarele de pe Masinile Lisp erau versiuni de Emacs scrise in intregime in Lisp. Primele doua Emacs-uri pentru Masini Lisp, in virtutea traditiei hacker-ilor de a folosi acronime recursive, erau EINE si ZWEI, adica EINE Is Not Emacs si ZWEI Was EINE Initially. Versiunile ulterioare foloseau un descendent al ZWEI, denumit, mai prozaic, ZMACS.
3Practic, e foarte putin probabil ca standardul limbajului sa fie redefinit - desi sunt cateva bube de care ar trebui scapat, procesul ANSI nu deschide un standard existent pentru modificari minore, si nici una din bubele respective nu provoaca reale probleme. Viitorul standardizarii Common Lisp va continua foarte probabil prin standarde de facto, in mare parte asemanator cu "standardizarea" Perl si Python - pe masura ce diferiti implementatori experimenteaza cu interfete de programare a aplicatiilor (Application Programming Interfaces - API) si biblioteci pentru a face lucruri care nu sunt specificate in standardul limbajului, alti implementatori le vor adopta sau oamenii vor dezvolta biblioteci de portabilitate pentru a netezi diferentele dintre implementari in cazul facilitatilor neincluse in standardul limbajului.
4Steel Bank Common Lisp
5CMU Common Lisp
6SBCL a fost derivat din CMUCL pentru a se face ordine in structura interna si pentru a-l face mai usor de mentinut. Dar ramificarea a fost amiabila; rezolvarile bug-urilor tind sa se propage intre cele doua proiecte, si exista discutii despre o posibila reunire a lor candva.
7Venerabilul "hello, world" precede chiar si cartea clasica de C a lui Kernighan si Ritchie, carte care a jucat un rol important in popularizarea lui. Originalul "hello, world" pare sa fi aparut din cartea "A Tutorial Introduction to the Language B" de Brian Kernighan, parte in Bell Laboratories Computing Science Technical Report #8: The Programming Language B publicat in ianuary 1973. (E disponibila online la http://cm.bell-labs.com/cm/cs/who/dmr/bintro.html.)
8Astea sunt alte expresii care la randul lor afiseaza string-ul "hello, world":
9Ei bine, dupa cum vei vedea cand o sa discut despre returnarea a mai multor valori, e posibil, tehnic, sa se scrie expresii care nu sunt evaluate la o valoare, dar chiar si aceste expresii sunt tratate ca si returnand NIL cand sunt evaluate intr-un context care asteapta o valoare.
10Voi discuta in Capitolul 4 de ce numele a fost convertit la majuscule.
11Ai fi putut sa introduci definitia si ca doua linii in REPL, deoarece acesta citeste expresii intregi, nu linii.
12Scurtaturile SLIME nu fac parte din Common Lisp - sunt comenzi din SLIME.
13Daca pentru un motiv sau altul LOAD nu merge cum trebuie, vei primi alta eroare si vei ajunge inapoi in depanator. Daca se intampla aceasta, cel mai probabil este din cauza ca Lisp nu poate sa gaseasca fisierul, probabil din cauza ca ideea lui de director curent nu are legatura cu adresa fisierului. In acest caz, poti sa iesi din depanator tastand q si apoi sa folosesti scurtatura SLIME cd pentru a schimba directorul curent in Lisp - tasteaza o virgula si apoi cd cand ti se cere o comanda si apoi numele directorului in care a fost salvat fisierul hello.lisp.
14http://www.flownet.com/gat/jpl-lisp.html



Sus
Poti sa folosesti Lisp in a Box, cum e scris mai sus.
Pe Windows poti folosi Corman Lisp.
Pe Windows, Linux, Mac OS, FreeBSD si Solaris poti folosi urmatoarele variante gratuite de implementari Common Lisp: Allegro Common Lisp sau LispWorks Personal Edition. Citeste pe site-urile respective pentru a afla care sunt limitarile variantelor gratuite.

Metoda folosita de mine este sa descarc Emacs, Clozure Common Lisp si Quicklisp si sa le fac sa mearga impreuna - functioneaza la fel si pe Windows, si pe Linux. Voi descrie cum se pot instala pe Windows.
  1. Descarca Emacs: poti sa iei cel mai recent fisier ZIP de aici. Dezarhiveaza undeva pe discul local la adresa C:\Emacs - daca la adresa respectiva este creat un folder gen "emacs-24.2.50", continutul lui trebuie mutat in parinte. Verifica sa ai o variabila de mediu "HOME" - daca nu exista deja, creeaz-o tu -, continutul ei ar trebui sa fie numele unui folder pe disc, in care Emacs sa-si salveze fisierul .emacs de configurare. Poti sa o setezi cu comanda set HOME=c:\Emacs rulata in Command Prompt.
  2. Descarca SBCL si instaleaza-l in C:\SBCL\.
  3. Descarca Quicklisp beta la adresa "c:\quicklisp.lisp". Deschide Lisp-ul descarcat la pasul anterior si executa comanda (load "c:/quicklisp.lisp"). Vor aparea instructiuni pentru instalarea Quicklisp. Executa in ordine comenzile (quicklisp-quickstart:install) pentru a instala Quicklisp, (ql:add-to-init-file) (si apasa Enter cand iti cere) pentru a-l incarca la pornirea SBCL si (ql:quickload "quicklisp-slime-helper") pentru a instala SLIME.
  4. Creeaza un fisier ".emacs" in C:\Emacs. Daca Windows nu te lasa sa creezi fisier care sa nu aiba nume ci doar extensie, creeaza C:\Emacs\1.emacs apoi, din Command Prompt, ruleaza mv c:\emacs\1.emacs c:\emacs\.emacs. Deschide-l in Notepad si adauga ultimele linii de la pasul anterior in el:(load (expand-file-name "~/quicklisp/slime-helper.el"))
    ;; Replace "sbcl" with the path to your implementation
    (setq inferior-lisp-program "c:/sbcl/sbcl.exe")
  5. Ruleaza din Command Prompt C:\Emacs\bin\runemacs.exe. Apasa Alt+x, scrie slime si apoi apasa Enter. Ar trebui sa fii acum in prompt-ul SBCL.


Odata pornit Lisp, utilizatorul este lasat in REPL, adica linia de comanda Lisp. Acolo poate sa introduca expresii pentru a fi evaluate de Lisp, poate sa defineasca variabile, functii, sa incarce fisiere, sa compileze fisiere, sa depaneze programe Lisp.

Niciun comentariu:

Trimiteți un comentariu

S-ar putea sa nu vedeti comentariul aparand imediat, asta inseamna ca el asteapta aprobarea mea. Aceasta e o masura anti-SPAM.