Funkcionālā programmēšana Android izstrādātājiem - 1. daļa

Pēdējā laikā es pavadīju daudz laika, mācoties Elixir - lielisku funkcionālu programmēšanas valodu, kas ir draudzīga iesācējiem.

Tas man lika domāt: kāpēc gan Android programmēšanā neizmantot dažus funkcionālās pasaules jēdzienus un paņēmienus?

Kad lielākā daļa cilvēku dzird terminu Funkcionālā programmēšana, viņi domā par Hacker News ziņām, kas kliedz par monādēm, augstāka pasūtījuma funkcijām un abstraktiem datu tipiem. Šķiet, ka tas ir mistisks Visums, kas ir tālu no ikdienas programmētāja centieniem un ir rezervēts tikai varenākajiem hakeriem, kas cēlušies no Númenoras sfēras.

Nu, pieskrūvējiet to ! Es esmu šeit, lai jums pateiktu, ka arī jūs to varat iemācīties. Arī jūs to varat izmantot. Arī jūs ar to varat izveidot skaistas lietotnes. Lietotnes, kurām ir eleganta, lasāma koda bāze un kurās ir mazāk kļūdu.

Laipni lūdzam Android izstrādātāju funkcionālajā programmēšanā (FP). Šajā sērijā mēs uzzināsim FP pamatus un to, kā mēs tos varam izmantot vecajā labajā Java un jaunajā lieliskajā Kotlin. Ideja ir saglabāt jēdzienu pamatojumu praktiskumā un izvairīties no pēc iespējas vairāk akadēmiskā žargona.

FP ir milzīga tēma. Mēs iemācīsimies tikai tos jēdzienus un paņēmienus, kas ir noderīgi, rakstot Android kodu. Mēs varētu apmeklēt dažus jēdzienus, kurus pilnības labad nevar tieši izmantot, taču es centīšos saglabāt materiālu pēc iespējas atbilstošāku.

Gatavs? Ejam.

Kas ir funkcionālā programmēšana un kāpēc man tā būtu jāizmanto?

Labs jautājums. Termins Funkcionālā programmēšana ir lietussargs dažādām programmēšanas koncepcijām, par kurām monikeram nav taisnības. Būtībā tas ir programmēšanas stils, kas programmas uztver kā matemātisko funkciju novērtēšanu un izvairās no maināmā stāvokļa un blakusparādībām (par tām mēs runāsim pietiekami drīz).

Būtībā FP uzsver:

  • Deklaratīvais kods - programmētājiem vajadzētu uztraukties par to, kas, un ļaut kompilatoram un izpildlaimam uztraukties par to, .
  • Explicitness - Kodam jābūt pēc iespējas acīmredzamākam. Jo īpaši blakusparādībasir jāizolē, lai izvairītos no pārsteigumiem. Datu plūsma un kļūdu apstrāde ir skaidri definēta, un tiek izvairītas no tādām konstrukcijām kā GOTO paziņojumi un izņēmumi, jo tie var nodot jūsu lietojumprogrammu neparedzētos stāvokļos.
  • Vienlaicīgums - lielākā daļa funkcionālo kodu pēc noklusējuma ir vienlaicīgi, jo to sauc par funkcionālo tīrību . Šķiet, ka vispārējā vienošanās ir tāda, ka šī iezīme jo īpaši izraisa funkcionālās programmēšanas popularitātes pieaugumu, jo CPU kodoli katru gadu nepalielinās, kā tas bija agrāk (skat. Mūra likumu), un mums ir jāpadara mūsu programmas vienlaicīgākas, lai izmantotu priekšrocības daudzkodolu arhitektūru.
  • Augstākas kārtības funkcijas - funkcijas ir pirmās klases dalībnieki tāpat kā visi pārējie valodas primitīvi. Funkcijas var nodot apkārt tāpat kā virkni vai int.
  • Nemainīgums - mainīgos pēc to inicializācijas nedrīkst mainīt. Kad lieta ir izveidota, tā ir tā uz visiem laikiem. Ja vēlaties, lai tas mainītos, jūs izveidojat jaunu lietu. Tas ir vēl viens skaidrības aspekts un izvairīšanās no blakusparādībām. Ja jūs zināt, ka lieta nevar mainīties, jums ir daudz lielāka pārliecība par tās stāvokli, kad to izmantojat.

Deklaratīvs, nepārprotams un vienlaicīgs kods, par kuru ir vieglāk pamatot un kas paredzēts, lai izvairītos no pārsteigumiem? Es ceru, ka esmu piesaistījis jūsu interesi.

Šajā sērijas pirmajā daļā sāksim ar dažiem pamatprincipiem FP: Tīrība , Blakusparādības un Pasūtīšana .

Tīras funkcijas

Funkcija ir tīra, ja tās izlaide ir atkarīga tikai no tās ievadīšanas un tai nav blakusparādību (par blakusparādībām mēs runāsim tūlīt pēc tam). Apskatīsim piemēru, vai ne?

Apsveriet šo vienkāršo funkciju, kas pievieno divus skaitļus. Tas nolasa vienu numuru no faila, un otrs numurs tiek ievadīts kā parametrs.

Java

int add(int x) { int y = readNumFromFile(); return x + y;}

Kotlins

fun add(x: Int): Int { val y: Int = readNumFromFile() return x + y}

Šīs funkcijas izvade nav atkarīga tikai no tās ievades. Atkarībā no tā, ko atgriež readNumFromFile () , tai var būt dažādas izejas vienai un tai pašai x vērtībai . Šī funkcija tiek uzskatīta par netīru .

Pārvērsim to par tīru funkciju.

Java

int add(int x, int y) { return x + y;}

Kotlins

fun add(x: Int, y: Int): Int { return x + y}

Tagad funkcijas izeja ir atkarīga tikai no tās ieejām. Dotajiem x un y funkcija vienmēr atgriezīs to pašu izvadi. Tagad tiek teikts, ka šī funkcija ir tīra . Matemātiskās funkcijas darbojas tāpat. Matemātisko funkciju izvade ir atkarīga tikai no tās ievadiem. Tāpēc funkcionālā programmēšana ir daudz tuvāka matemātikai nekā parastais programmēšanas stils, pie kura mēs esam pieraduši.

PS Tukša ievade joprojām ir ievade. Ja funkcija neievada ievades un katru reizi atgriež to pašu konstanti, tā joprojām ir tīra.

PPS Īpašība vienmēr atgriezt vienu un to pašu izvadi konkrētai ieejai ir pazīstama arī kā atsauces caurspīdīgums, un jūs, iespējams, redzēsit, ka tas tiek izmantots, runājot par tīrām funkcijām.

Blakus efekti

Izpētīsim šo jēdzienu ar to pašu pievienošanas funkcijas piemēru. Mēs pārveidosim pievienošanas funkciju, lai arī rezultāts tiktu ierakstīts failā.

Java

int add(int x, int y) { int result = x + y; writeResultToFile(result); return result;}

Kotlins

fun add(x: Int, y: Int): Int { val result = x + y writeResultToFile(result) return result}

Šī funkcija tagad raksta aprēķina rezultātu failā. ti, tas tagad maina ārpasaules stāvokli. Tagad tiek teikts, ka šai funkcijai ir blakus efekts, un tā vairs nav tīra funkcija.

Jebkuram kodam, kas maina ārpasaules stāvokli - maina mainīgo, raksta failā, raksta DB, kaut ko izdzēš utt., Tiek teikts, ka tam ir blakusparādība.

Funkcijas, kurām ir blakusparādības, FP izvairās, jo tās vairs nav tīras un atkarīgas no vēsturiskā konteksta . Koda konteksts nav patstāvīgs. Tas padara viņus daudz grūtāk pamatotus.

Pieņemsim, ka jūs rakstāt koda daļu, kas ir atkarīga no kešatmiņas. Tagad jūsu koda izvade ir atkarīga no tā, vai kāds ir ierakstījis kešatmiņā, kas tajā ir ierakstīts, kad tas ir rakstīts, ja dati ir derīgi utt. Jūs nevarat saprast, ko dara jūsu programma, ja vien jūs nesaprotat visus iespējamos stāvokļus no kešatmiņas tas ir atkarīgs. Ja jūs to paplašināt, iekļaujot visas citas lietas, no kurām ir atkarīga jūsu lietotne - tīkls, datu bāze, faili, lietotāja ievade un tā tālāk, kļūst ļoti grūti zināt, kas tieši notiek, un to visu uzreiz ievietot savā galvā.

Vai tas nozīmē, ka tad mēs neizmantojam tīklu, datu bāzes un kešatmiņas? Protams, nē. Izpildes beigās vēlaties, lai lietotne kaut ko darītu. Android lietotņu gadījumā tas parasti nozīmē lietotāja saskarnes atjaunināšanu, lai lietotājs faktiski varētu iegūt kaut ko noderīgu no mūsu lietotnes.

FP lielākā ideja nav pilnībā atteikties no blakusparādībām, bet gan to ierobežošana un izolēšana. Tā vietā, lai mūsu lietotne būtu piesārņota ar funkcijām, kurām ir blakusparādības, mēs nospiežam blakusparādības līdz mūsu sistēmas malām, lai tām būtu pēc iespējas mazāka ietekme, padarot mūsu lietotni vieglāk pamatotu. Mēs par to sīkāk runāsim, kad vēlāk sērijā izpētīsim mūsu lietotņu funkcionālo arhitektūru .

Pasūtīšana

Ja mums ir virkne tīru funkciju, kurām nav blakusparādību, tad to izpildes secība kļūst neatbilstoša.

Pieņemsim, ka mums ir funkcija, kas iekšēji izsauc 3 tīras funkcijas:

Java

void doThings() { doThing1(); doThing2(); doThing3();}

Kotlins

fun doThings() { doThing1() doThing2() doThing3()}

Mēs noteikti zinām, ka šīs funkcijas nav atkarīgas viena no otras (tā kā vienas izeja nav otras ievade), un mēs arī zinām, ka tās neko nemainīs sistēmā (jo tās ir tīras). Tas padara to izpildes kārtību pilnīgi savstarpēji aizstājamu.

Izpildes secību var atkārtoti sajaukt un optimizēt neatkarīgām tīrajām funkcijām. Ņemiet vērā, ka, ja ieejas doThing2 () bija rezultāts doThing1 () , tad tie būtu jāveic, lai, bet doThing3 () joprojām varētu būt atkārtoti lika izpildīt pirms doThing1 ().

Ko gan mums dod šis pasūtīšanas īpašums? Vienlaicīgums, tas ir kas! Mēs varam palaist šīs funkcijas uz 3 atsevišķiem CPU kodoliem, neuztraucoties par kaut ko ieskrūvēšanu!

Daudzos gadījumos kompilatori progresīvās, funkcionālās valodās, piemēram, Haskels, var pateikt, formāli analizējot jūsu kodu, vai tas ir vienlaikus vai nē, un var atturēt jūs nošaušanas pa pēdām ar strupceļiem, sacensību apstākļiem un tamlīdzīgi. Šie kompilatori teorētiski var arī automātiski paralēlizēt jūsu kodu (tas faktiski nepastāv nevienā kompilatorā, kuru es šobrīd zinu, bet pētījumi turpinās).

Pat ja jūsu kompilators neskatās uz šīm lietām, kā programmētājs ir lieliski, ja varēsiet pateikt, vai kods ir vienlaicīgs, tikai aplūkojot funkciju parakstus un izvairoties no nejaukām vītņu kļūdām, mēģinot paralēlizēt obligāto kodu, kas varētu būt pilns ar slēptu blakus efekti.

Kopsavilkums

Es ceru, ka šī pirmā daļa jūs ir ieinteresējusi FP. Pure, Side effect bezmaksas funkcijas ļauj daudz vieglāk pamatot kodu un ir pirmais solis, lai panāktu vienlaicīgumu.

Pirms nonākam pie vienlaicīguma, tomēr mums ir jāapgūst nemainīgums . Mēs to darīsim šīs sērijas 2. daļā un redzēsim, kā tīras funkcijas un nemainīgums var palīdzēt mums uzrakstīt vienkāršu un viegli saprotamu vienlaicīgu kodu, neizmantojot slēdzenes un mutēšus.

Lasiet tālāk

Funkcionālā programmēšana Android izstrādātājiem - 2. daļa

Ja neesat lasījis 1. daļu, lūdzu, izlasiet to šeit: medium.com

Ja jums tas patika, noklikšķiniet uz? zemāk. Es pamanīju katru no viņiem un esmu pateicīgs par katru no viņiem.

Lai uzzinātu vairāk par programmēšanu, sekojiet man, lai jūs saņemtu paziņojumu, kad es rakstīšu jaunas ziņas.