ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 150
Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 82
Abonament PDF

La fel, dar diferit

Kovács György
Scala Developer @ Itiviti



PROGRAMARE

A programa funcțional este a programa. Consensul programatorilor, mai ales al celor care folosesc programarea funcțională, este că programarea funcțională este viitorul. Ei ne zic că este mai ușor de înțeles codul, pentru că nu trebuie să ținem cont de starea sistemului. Codul e mai succint, pentru că refolosim zeci și poate sute de funcții predefinite care au fost scrise deja și ne permit să ne concentrăm direct pe implementare.

Totuși programarea funcțională reprezintă doar o mică parte a pieței. Ea poate fi mai dificilă de înțeles, decât cea imperativă sau orientată pe obiecte. În aceste paradigme un programator poate învăța conceptele pe parcurs, însă când vine vorba de programarea funcțională, un programator se descurcă doar dacă cunoaște deja majoritatea funcțiilor și conceptelor folosite des.

Cu toate acestea, toate limbajele moderne încorporează concepte luate din programarea funcțională, precum pattern match, funcții lambda și funcții higher order. În acest articol nu voi încerca să promovez programarea funcțională în detrimentul altor paradigme, ci voi prezenta concepte de bază care se pot aplica oriunde, folosind aproape orice limbaj modern.

Funcțiile sunt tabele leneșe

În paradigma OO considerăm un program ca fiind un număr de obiecte care interacționează cu alte obiecte folosind interfețele obiectelor. Fiecare obiect are niște date și niște metode asociate. Programarea funcțională, putem spune, este orientată pe funcții.

Metodele din limbajele OO nu sunt neapărat funcții, deși uneori se pot trata ca funcții. O metodă poate fi o procedură, o funcție sau o combinație de metodă și funcție. O procedură ia niște parametri, și modifică ori parametri, ori alte variabile din sistem. Funcția produce o valoare nouă folosind doar parametri de intrare. E important de menționat faptul că funcția nu modifică parametri de intrare. Tot ce modifică parametrii de intrare nu sunt funcții, sunt proceduri, chiar dacă returnează valori noi. Din această cauză cele mai multe metode sunt proceduri.

În viziunea programării funcționale, tot programul este considerat o singură funcție. Funcția este compusă din mai multe funcții mai mici și tot așa. O deosebire între OO și PF este că în OO se zice "Functions in the small, objects in the large", adică se folosesc obiecte pentru logica abstractă, dar se scriu metode pentru logica concretă. În schimb în PF, fiecare funcție este alcătuită dintr-o altă funcție, până când ajungem la o valoare "Functions in the small, functions in the large".

În cazul în care nu sunteți siguri dacă o bucată de cod este sau nu o funcție, puneți-vă următoare întrebare: se poate înlocui bucata de cod cu un tabel? Dacă răspunsul este da, atunci aveți o funcție în față. Dacă nu, vă uitați la o procedură. Din cauză că orice funcție se poate înlocui cu un tabel, rezultatele funcției se pot mnemoniza. Adică se calculează o dată, și nu se mai recalculează, fiindcă avem garanția că răspunsul rămâne la fel. Desigur această optimizare nu există în toate limbajele, dar există în limbajele care sunt făcute pentru programarea funcțională.

Polimorfismul este o funcție împrăștiată

Când se vorbește despre polimorfism, de obicei se face în cadrul programării OO. Există însă polimorfism și în PF. În PF polimorfismul este obținut prin (surpriză) funcții.

Cum adăugăm o funcționalitate a unui număr de clase? Prin folosirea unei interfețe comune cu unul sau mai multe metode, forțând fiecare clasă care implementează interfața, să implementeze metodele din interfață. În programarea funcțională acest lucru se poate replica prin a face o funcție care primește un obiect și returnează o valoare bazată pe obiectul primit.

Mai jos în stânga, am definit o interfață Zi care are o singură metodă nume. Apoi am extins un număr de clase care implementează interfața Zi. În dreapta, am definit o structură echivalentă în programarea funcțională. Aceste exemple sunt scrise în Scala, un limbaj care combină programarea funcțională cu programarea OO.

Putem observa că în primul caz (stânga) sunt grupate metodele care acționează peste un tip de date, iar în cazul doi (dreapta), sunt grupate obiectele care au o anumită proprietate.

Un alt fel de a vedea lucrurile, este că în primul caz, uitându-ne la un obiect putem vedea toate metodele definite pentru acel obiect, însă e mai dificil de văzut toate obiectele care au un anumit comportament.

În cazul doi, uitându-ne la o funcție putem vedea toate obiectele care au definit acel comportament, dar e mai greu să aflăm toate comportamentele pentru un anumit obiect.

Ambele variante sunt valide, cu avantaje și dezavantaje. Totuși, un argument pentru varianta funcțională ar fi că de multe ori când facem debug sau implementăm ceva, e mai folositor să vedem obiectele grupate în funcții, pentru că ne concentrăm pe funcționalitate, nu pe un anumit obiect.

nullul este un Option ascuns

Tony Hoare, informaticianul care a inventat quicksortul printre altele, și-a cerut scuze în 2009 pentru o greșeală imensă introdusă de el în informatică: "I call it my billion-dollar mistake. It was the invention of the null reference in 1965.".

Cum putem scăpa de nulluri? Ușor: Nu returnați nulluri, sub nici o circumstanță. Din nefericire, s-au scris foarte multe sisteme care returnează nulluri și se așteaptă la nulluri în argumente. Pentru cine e obișnuit cu acest mod de programare, s-ar putea să îi fie greu să își imagineze o modalitate diferită de a programa. Însă ea există, și nu e relegată doar la programarea funcțională. Soluția de a scăpa de nulluri este una simplă dar poate nu e familiară: folosirea Optionalurilor.

Un Optional este un tip care poate conține sau nu conține o valoare, oarecum similar cu o referință care poate fi null sau o valoare existentă. Dacă aderăm la programarea fără nulluri, atunci Optional ne permite să controlăm mai bine efectele erorilor și lipselor. Aceasta e posibil datorită faptului că există multe funcții existente scrise pentru obiectele de tip Optional.

În exemplul de mai sus am folosit unele din acele funcții pentru a elimina toate verificările, folosind doar două tipuri de funcții mapN și map. mapN este echivalent cu primul if, care verifică dacă unul dintre variabile este lipsă. Dacă o variabilă lipsește, se returnează lipsa. În cazul în care ambele variabile sunt prezente, parcurgem lista cu un map, pe care se creează o nouă listă aplicând funcția lambda. Al doilea map verifică dacă elementul curent din b există. Dacă da, returnează valoarea lui adunată cu a, în caz contrar rămâne o valoare lipsă.

Aceste funcții există în toate limbajele funcționale, dar se pot implementa ușor și în alte limbaje precum Java, C# sau C++. Codul care nu verifică nulluri tot timpul e mai succint, și dacă e citit de cineva care cunoaște funcțiile folosite, e mai transparent.

Toate containerele sunt similare

După cum am văzut în exemplul de mai sus, lista și Option are o metodă map. Dacă ne gândim la efectul mapului pe Option, se verifică dacă există sau nu o valoare, iar dacă ea există, este transformată în ceva mod. Dacă ne gândim la efectul aplicat unei liste, transformăm toate valorile listei într-un anumit fel.

Putem lua un pas în spate și ajungem la concluzia că lista și Optional sunt tipuri de date similare. Acest lucru devine clar, dacă ne gândim că o listă poate fi goală. Dacă lista este goală, nu se schimbă nici un element. Dacă Optional e gol, nici atunci. Dacă lista conține valori, ele sunt modificate, similar cu Optional. (E important să precizăm că de fapt nu se schimbă valorile în listă, ci se creează o altă listă cu noile valori. Acest lucru se aplică și la Optional). Vom denumi aceste tipuri de date care au funcția map definită, containere. În programarea funcțională numele folosit este Functor, dar nu este un nume tocmai intuitiv sau prietenos. Exemple de containere sunt liste, optionaluri, arbori de orice fel, procese asincrone (Future / Promise), tipuri care conțin erori, etc.

Unele containere sunt mai surprinzătoare decât altele, dar este cert că se poate implementa un map pentru toate aceste obiecte.

Concluzie

Toate conceptele sunt diferite într-un anumit sens, dar sunt similare într-un alt sens. A vedea similaritățile este la fel de important, chiar mai important decât să ne concentrăm pe diferențe.

În acest articol, scopul a fost evidențierea unor similarități. Acest proces de a face legături între concepte este unul care poate avea beneficii în orice paradigmă sau limbaj de programare .

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects

Kovács György a mai scris