Documente Academic
Documente Profesional
Documente Cultură
S, tefan Ciobâcă
În laboratorul aferent cursului “Programare Funct, ională”, vom studia limbajul de
programare Haskell. Limbajul Haskell este un limbaj de programare general, pur funct, ional,
lenes, s, i tipizat static. În cursul acestui laborator, vom studia s, i vom ı̂nt, elege fiecare din-
tre aceste particularităt, i ale limbajului Haskell.
Cultură generală
Limbajul Haskell poartă numele logicianului Haskell Brooks Curry. Curry
a lucrat ı̂n special ı̂n domeniul logicii combinatoriale, dar a avut contribut, ii
importante s, i ı̂n alte domenii ale informaticii. O anecdotă interesantă este că
fiecare dintre cele trei nume ale lui Haskell Brooks Curry a dat numele câte
unui limbaj de programare.
Limbajul Haskell a luat nas, tere ı̂n anul 1987, când la conferint, a FPCA din Portland,
Oregon, participant, ii au hotărât să dezvolte un standard deschis pentru un limbaj de
programare ne-strict (vom vedea ce ı̂nseamnă ne-strict ı̂n continuare). Astfel de limbaje
de programare existau deja, dar nu erau open-source. Cel mai cunoscut era limbajul
Miranda, care a inspirat mult dezvoltarea Haskell.
Specificat, ia limbajului Haskell este definită de un comitet de standardizare s, i ex-
istă mai multe versiuni, dintre care cele mai recente sunt Haskell 98, definit ı̂n “Haskell
98 Language and Libraries: The Revised Report” s, i, respectiv, Haskell 2010, definit ı̂n
“Haskell 2010 Language Report”. Există mai multe implementări ale limbajului Haskell.
Cea mai cunoscută implementare a limbajului Haskell este GHC (Glasgow Haskell Com-
piler), un compilator open-source al cărui dezvoltator principal, Simon Peyton Jones,
lucrează la Microsoft Research ı̂n Cambridge, UK.
3
4 CHAPTER 1. INTRODUCERE ÎN HASKELL
Cultură generală
Un alt exemplu de paradigmă de programare declarativă este programarea
logică, unde programele sunt o secvent, ă de clauze Horn de ordinul I, iar
execut, ia este realizată printr-o formă de rezolut, ie.
Avantajul major al limbajelor pur funct, ionale este că lipsa efectelor secundare sim-
plifică semnificativ rat, ionamentele despre comportamentul unui program. Mai mult,
1.1. DE CE SĂ STUDIEZ LIMBAJUL HASKELL? 5
limbajele pur funct, ionale ı̂ncurajează programatorul să scrie cod despre care se poate
rat, iona mai simplu. Din acest motiv, este mult mai us, or să ne convingem că un program
pur funct, ional este corect.
Altă particularitate importantă a limbajului Haskell este evaluarea lenes, ă. Evaluarea
lenes, ă asigură faptul că o expresie este evaluată doar dacă acest lucru este strict necesar.
De exemplu, dacă o funct, ie foloses, te valoarea unui parametru doar ı̂n anumite cazuri,
atunci parametrul nu este evaluat decât ı̂n acele cazuri. Evaluarea lenes, ă poate eficientiza
execut, ia unui program, dar, mai important, permite abordări mai deseobite, cum ar fi
folosirea structurile de date infinite.
Haskell este un limbaj tipizat static. Acest lucru ı̂nseamnă că orice expresie dintr-
un program Haskell are asociat un tip ı̂ncă din momentul compilării. Compilatorul se
asigură că programul este bine tipizat, generând altfel o eroare de compilare. Acest
lucru ı̂nseamnă că un program nu poate produce erori de tip, cum ar fi ı̂nmult, irea
unui număr ı̂ntreg cu un s, ir de caractere, la rulare. Limbajele de programare tipizate
dinamic, cum ar fi Python, nu detectează erorile de tipuri decât ı̂n momentul rulării
programului. Dezavantajul limbajelor tipizate dinamic este că erorile de tip care apar
doar ı̂n cazuri rare nu sunt ı̂n general detectate ı̂n timpul dezvoltării/testării programului,
ci sunt detectate direct de către utilizatori.
Sistemul de tipuri din Haskell este extrem de expresiv, mult mai expresiv decât,
de exemplu, sistemul de tipuri al limbajului C. Cu toate acestea, nu este necesar ca
toate funct, iile/variabilele să fie adnotate explicit cu tipul lor, ca ı̂n C. Haskell cont, ine
un motor de inferent, ă de tipuri care poate, ı̂n cele mai multe cazuri, să calculeze tipul
funct, iilor/variabilelor din modul ı̂n care sunt folosite. De exemplu, dacă o variabilă de
program x este comparată cu un caracter, atunci tipul lui x trebuie să fie Char.
Limbajul Haskell este folosit ı̂n special ı̂n mediul universitar pentru dezvoltarea de
unelte software, ı̂n general prototip, care implementează rezultate ale cercetării (algo-
ritmi) de ultimă oră, dar s, i in industrie. Este put, in probabil să găsit, i un job unde să
programat, i ı̂n Haskell, dar nu imposibil.
Cultură generală
Sloganul neoficial al limbajului Haskell este “avoid success at all costs”. În mod
aparent paradoxal, dezvoltatorii limbajului nu ı̂s, i doresc ca acesta să devină
un succes comercial. Folosirea pe scară largă a limbajului Haskell ar limita
evolut, ia acestuia, deoarece orice schimbare majoră ar avea impact asupra unui
număr prea mare de persoane.
Totus, i, multe dintre conceptele pe care le vet, i studia vă vor fi de folos chiar dacă vet, i
programa ı̂ntr-un limbaj obis, nuit, cum ar fi C++, Java sau C#. Cu trecerea timpului,
aceste limbaje evoluează s, i se apropie din ce ı̂n ce mai mult de limbajele funct, ionale.
De exemplu, lambda-expresiile adăugate ı̂n C++11 sunt inspirate direct din limbajele
funct, ionale.
Mai mult, studiul limbajului Haskell vă va schimba profund modul de gândire s, i
6 CHAPTER 1. INTRODUCERE ÎN HASKELL
vă va transforma ı̂n programatori mai buni. De asemenea, fiind deosebit de expresiv
(programele Haskell sunt de obicei mai scurte decât programele C echivalente), vet, i
putea folosi Haskell pentru a crea rapid prototipuri ale programelor pe care dorit, i să le
dezvoltat, i.
ghci
Dacă instalarea a decurs cu succes, vet, i obt, ine un rezultat similar cu cel din Figura 1.1.
Comanda ghci pornes, te compilatorul GHC ı̂n modul interactiv. În acest mod de lu-
cru, GHC cites, te o expresie Haskell, o evaluează, afis, ează rezultatul evaluării s, i reia din
nou acest proces. Acest tip de interact, iune este cunoscut s, i sub numele de toplevel (ı̂n
cazul limbajului OCaml), sau REPL (read-evaluate-print loop) ı̂n cazul limbajelor Com-
mon Lisp, Standard ML, Scheme, Python s, i altele. Un mod similar de lucru este disponi-
bil s, i pentru limbajul JavaScript (consola JavaScript), dacă accesăm ı̂ntr-un browser
modern modul dezvoltator.
Cultură generală
Modul interactiv permite experimentarea rapidă a unor idei s, i favorizează o
manieră de dezvoltare a programelor de tip bottom-up (ı̂n contrast cu dez-
voltarea de tip top-down). Limbajele tradit, ionale cum ar fi C(++), Java sau
C# nu dispun, ı̂n principiu, de un astfel de mod interactiv de lucru.
1.2. CUM INSTALEZ HASKELL PE CALCULATORUL MEU? 7
După cum am discutat deja, ı̂n modul interactiv, GHC cites, te o expresie, o evaluează,
afis, ează rezultatul evaluării s, i apoi reı̂ncepe. Cele mai simple expresii Haskell sunt cele
care cont, in numere ı̂ntregi s, i operat, ii matematice obis, nuite peste numere ı̂ntregi. De
exemplu, tastat, i
3 + 4 * 5
2^
100
8 CHAPTER 1. INTRODUCERE ÎN HASKELL
> 3 + 4 * 5
23
> 3 - 4
-1
> (3 + 4) * 5
35
> 3 / 4
0.75
> -3
-3
> 4 + (-3)
10 CHAPTER 1. INTRODUCERE ÎN HASKELL
1
> 2 ** 5
32.0
> 2 ^ 5
32
> 2 ^ 100
1267650600228229401496703205376
> 2.25 ** 3
11.390625
> 2.25 ** 3.25
13.95060955069482
> pi
3.141592653589793
> 2 * pi * 7
43.982297150257104
Observat, i că sunt acceptat, i tot, i operatorii matematici obis, nuit, i, iar ordinea operat, iilor
este cea cunoscută de la matematică (ı̂nmult, irea are prioritate ı̂n fat, a adunării). Paran-
tezele pot fi folosite pentru a schimba ordinea efectuării operat, iilor. Numerele prefixate
de un - (minus unar) trebuie puse ı̂ntre paranteze când apar ı̂ntr-o expresie mai com-
plicată (ce se ı̂ntâmplă altfel?). Operatorul ** este operatorul de exeponent, iere pentru
numere fract, ionare, ı̂n timp ce ^ este operatorul de exponent, iere pentru numere ı̂ntregi.
Pentru numerele ı̂ntregi, Haskell foloses, te implicit o precizie infinită (limitată doar de
memoria disponibilă).
Exercit, iu. Încercat, i s, i alte expresii care combină operat, iile prezentate mai sus.
În afară de operatorii matematici de mai sus, expresiile Haskell pot cont, ine apeluri
la funct, ii. În limbajele tradit, ionale, cum ar fi C sau Java, sintaxa de apel a funct, iilor
este: se scrie numele funct, iei, se scrie ’(’, se scriu parametrii separat, i prin virgule, se
scrie ’)’. Spre deosebire de limbajele tradit, ionale, sintaxa de apel a funct, iilor este mai
simplă, des, i init, ial poate părea cam ciudată. În Haskell, sintaxa de apel a unei funct, ii
este: se scrie numele funct, iei, se scrie un spat, iu, se scriu parametrii, separat, i prin spat, ii.
Mai jos sunt câteva exemple de apeluri de funct, ii. Funct, ia div calculează câtul
ı̂mpărt, irii ı̂ntregi iar funct, ia mod restul ı̂mpărt, irii. Funct, ia sin calculează o aproximare
a funct, iei sinus cunoscută de la matematică.
> div 10 2
5
> div 9 2
4
> div 100 7
14
> mod 100 7
2
> 14 * 7 + 2
1.3. EXPRESII SIMPLE ÎN HASKELL 11
100
> sin 3.14
1.5926529164868282e-3
> sin pi
1.2246467991473532e-16
Dacă ı̂ncercat, i să evaluat, i expresia de mai sus, vet, i obt, ine o eroare de tip. Deo-
camdata nu vom ı̂ncerca să ı̂nt, elegem eroarea, dar intuitiv trebuie să s, tit, i că sistemul
ı̂ncearcă să apeleze funct, ia div pe patru argumente: div, 100, 7 s, i respectiv 3. Deoarece
funct, ia div nu as, teaptă decât două argumente, amândouă numere ı̂ntregi, apare eroare
de tip. Acestă eroare se ı̂ntâmplă din cauza modului de parsare a apelurilor de funct, ie.
Putem folosi paranteze pentru a fort, a interpretarea pe care o dorim:
În general, apelurile de funct, ii care apar ı̂n interiorul unei expresii mai complicate
trebuie marcate prin paranteze pentru a obt, ine efectul dorit:
Deoarece parantezele pot fi folosite ı̂n mod liber ı̂n jurul unor expresii, există tendint, a
printre ı̂ncepători să parantezeze funct, iile unare pentru a obt, ine o sintaxă similară cu
sintaxa limbajului C:
> sin(sin(3.14))
1.5926522431813962e-3
Acest lucru trebuie evitat, deoarece acest mod de scriere a codului nu este idiomatic
s, i poate genera confuzii pe viitor (de exemplu, nu se poate generaliza la funct, ii cu mai
multe argumente).
12 CHAPTER 1. INTRODUCERE ÎN HASKELL
Exercit, iu. Încercat, i s, i alte expresii mai complicate s, i ı̂ncercat, i să minimizat, i numărul
de paranteze necesare pentru a obt, ine rezultatul dorit.
În Haskell, True s, i False reprezintă cele două valori de adevăr. Funct, ia not cal-
culează negat, ia logică, ı̂n timp ce operatorii && s, i respectiv || calculează “s, i”-ul logic s, i
respectiv “sau”-ul logic:
> True
True
> False
False
> not True
False
> not (not True)
True
> not (not (not False))
True
> True && True
True
> False && True
False
> False || True
True
> not (False || True)
1.4. TRUCURI SINTACTICE 13
False
> 42 == 42
True
> 41 == 42
False
> 42 /= 9
True
> 42 /= 42
False
> True == True
True
> True == False
False
Dacă ı̂ncercăm să comparăm două elemente de tipuri diferite, vom obt, ine o eroare
de tip (deocamdata nu ı̂ncercăm să o ı̂nt, elegem):
> 5 == True
... [eroare]
1. Dacă o este un operator binar (folosit ı̂n format infix), atunci (o) este o funct, ie
binară s, i x o y este aceeas, i expresie cu (o) x y;
Atent, ie! În itemul al doilea, caracterul ‘ (numit backtick, cod ASCII 96) este diferit
de caracterul apostrof (’) s, i de caracterul ghilimele ("). Acest caracter se găses, te de
obicei la stânga tastei 1 sau, la unele calculatoare, ı̂ntre tasta Shift s, i tasta Z.
Mai jos sunt câteva exemple de folosire a notat, iilor de mai sus:
Prelude> (+) 5 3
8
Prelude> (-) 5 3
2
Prelude> (-) 3 5
14 CHAPTER 1. INTRODUCERE ÎN HASKELL
-2
Prelude> (*) 4 2
8
Prelude> (*) 4 ((+) 2 3)
20
Prelude> 4 ‘div‘ 2
2
Prelude> 100 ‘mod‘ 7
2
Prelude> (+) (100 ‘mod‘ 7) (7 - 2)
7
Observat, ie. Cele două notat, ii nu se pot folosi simultan. Astfel, textul (‘div‘) s, i
textul ‘(+)‘ vor genera eroari de sintaxă.
Pentru a evita aglomerarea cu paranteze, se poate folosi uneori operatorul $:
Situat imediat după o funct, ie (cu un singur argument), acest operator fort, ează ca
toată expresia ce ı̂ncepe imediat după $ s, i se termină la sfârs, itul liniei (sau până la o
paranteză ı̂nchisă) să fie considerată argumentul funct, iei ı̂n cauză.