Maybe? monad-ul care evită null-checks, cu PHP
Inspirat din limbajele de programare funcționale, paradigma ”maybe” poate fi împrumutată și în PHP
Dacă aveți o afinitate spre programarea funcțională, atunci cred că o să vă placă ideea de a împrumuta aspecte funcționale într-un limbaj orientat pe obiecte. În schimb, dacă nu aveți experiență cu limbajale funcționale (Haskell1, Scala2 sau erlang), e o ocazie să aflați despre aceste limbaje și să aplicați o paradimgă des folosită în acest domeniu: monad.
Ce este un monad (monadă)?
În termeni simpli, o monadă este un tipar de proiectare sau abstractizare, care ne permite să înlănțuim calcule și să gestionăm anumite aspecte ale acestora într-un mod structurat. Monada funcționează ca un "container" special care poate conține valori și de asemenea, să definească "efecte secundare" asociate cu aceste valori.
De se folosesc monadele?
Pentru gestionarea efectelor secundare: Monadele ne permit să tratăm efecte secundare precum date din surse externe (I/O), excepții și modificarea valorilor într-un mod pur funcțional3.
Structurarea codului: Monadele ajută să scriem cod mai clar și mai concis, facilitând înlănțuirea de operații și abstractizarea multor detalii de implementare.
Compunerea funcțiilor: Monadele permit compunerea de funcții care operează pe valorile închise în monade, creând astfel "pipeline-uri" de calcul.
Monada maybe
Aceasta reprezintă o valoare care poate fi, fie Just a (o valoare prezentă), fie Nothing (o valoare absentă). Se folosește pentru a modela valori opționale și pentru a evita erorile de tipul null pointer
. Maybe ne permite înlănțuirea de operații, și implicit returnarea ”Nothning” sau o valoare implicite când operațiile nu sunt executate.
În pseudocod, putem defini monada Maybe astfel:
data Maybe val = Just val | Nothing
Implementarea Maybe în PHP
Această implementare ne ajută să evităm evaluările de genul
if (valoare) then:
X
else:
Y
tipice limbajelor de programărie imperative. Iar monada Maybe este relativ ușor de implementat.
Vom defini o clasă care suportă aceste 2 metode: just
și nothing
plus o metodă de map
și una getOrElse
pentru înlănțuire și respectiv un pas de obținere a valorii cu suport pentru valori default sau implicite:
Așa cum am menționat, avem cele două metode statice: Maybe::just
și Maybe::nothing
. just
creează un obiect Maybe
cu o valoare implicită, care poate fi null, iar nothing
creează un obiect Maybe
cu valoare NULL.
Metoda map()
primește o funcție și o aplică valorii salvate pe obiectul Maybe
. Dacă această valoare este NULL, va returna Maybe::nothing
, adică un obiect Maybe fără valoare asociată. Iar dacă există o valoare pe obiect, valoare returnată de aplicarea funcției este returnată ca obiect Maybe prin Maybe::just
.
Mai departe putem folosi această funcționalitate într-un controller de exemplu, care returnează JSON:
Acest controller, obține o valoare pentru email din $request
și apelează un serviciu, UserService pentru a obține datele utilizatorului. UserService
va returna un obiect Maybe, pe care apelăm map pentru a extrage un array cu datele relevante (name, email).
După map
este înlănțuită metoda getOrElse
, prin care obținem fie valoarea procesată la pasul anterior, fie valorile trimise ca implicite (‘name‘=> ‘Unknown‘, ‘email‘=>’Unavailable’
) în cazul în care Maybe este nothing - adică userul nu este găsit.
Implementarea serviciului UserService:
Aici creăm obiectul Maybe care va transmite valoarea din repository, NULL
sau un obiect User
.
Dezavantajul obiectului Maybe
În implementarea de mai sus, deși am evitat evaluarea unei valori NULL în controller și funcția noastră primește garantat un obiect de tip User, tipul acestui argument este ascuns în valoarea obiectul Maybe. Așadar din acest punct de vedere am introdus un nivel de abstractizare care ascunde tipul argumentului.
De asemenea, restul aplicației noastre nu știe cum să intracționeze cu acest nou container de date, și pentru a rezolva asta ar fi nevoie să introducem Maybe sau Optional ca un tip de input / output pentru funcțiile care extrag date in altă sursă.În consecință asta ar putea complica implementarea cu un nou nivel de abstractizare.
Alte implementări notabile:
(en) scala-lang.org
Keeping up with the puritans :))