Funkcionalno programiranje kolska 201920 godina Letnji semestar Lekcija

  • Slides: 32
Download presentation
Funkcionalno programiranje Školska 2019/20 godina Letnji semestar

Funkcionalno programiranje Školska 2019/20 godina Letnji semestar

Lekcija 3: Uvod u funkcionalno programiranje

Lekcija 3: Uvod u funkcionalno programiranje

Sadržaj • Kompozicija i razvoj softvera • Šta je funkcionalno programiranje (FP) • Osnovni

Sadržaj • Kompozicija i razvoj softvera • Šta je funkcionalno programiranje (FP) • Osnovni koncepti funkcionalnog programiranja • • • Funkcija Referencijalna transparentnost Bočni efekat Čiste funkcije Nepromenljivost (imutabilnost) • Izbegavanje deljenih stanja • Izbegavanje mutirajućih stanja • Komponovanje • Objekti • Funkcije • Deklarativno izražavanje • Prednosti i nedostaci FP-a

Kompozicija i razvoj softvera • Kompozicija: “Akt kombinovanja delova ili elemenata da bi se

Kompozicija i razvoj softvera • Kompozicija: “Akt kombinovanja delova ili elemenata da bi se formirala celina. ” • Proces razvoja softvera je razlaganje složenih problema na manje probleme, izgradnja komponenti za rešavanje tih manjih problema, i zatim komponovanje tih komponenti da bi se formirala kompletna aplikacije. • Kompozicija je suština razvoja softvera: Za razvoj softvera neophodne su nam komponente i način za njihovu adekvatnu kompoziciju

Softverska komponenta • Softverska komponenta je element softvera koji se može nezavisno koristiti i

Softverska komponenta • Softverska komponenta je element softvera koji se može nezavisno koristiti i komponovati. • Softverska komponenta je softverska jedinica funkcionalnosti koja upravlja pojedinačnom apstrakcijom.

Šta je funkcionalno programiranje • Funkcionalno programiranje (FP) je stil programiranja koji komponuje aplikacije

Šta je funkcionalno programiranje • Funkcionalno programiranje (FP) je stil programiranja koji komponuje aplikacije korišćenjem čistih funkcija izbegavajući mutabilna stanja i bočne efekte. • U funkcionalnom programiranju komponente su funkcije a mehanizam kompozicije je funkcija višeg reda.

Funkcija • Funkcija u programiranju: blok organizovanog, višekratno upotrebljivog koda koji se koristi za

Funkcija • Funkcija u programiranju: blok organizovanog, višekratno upotrebljivog koda koji se koristi za izvršavanje pojedinačne akcije. • Funkcija u matematici: Funkcija je relacija između skupa (domen) ulaza (argument) i skupa (kodomen) dopustivih izlaza (vrednost funkcije) sa svojstvom da je svaki ulaz u relaciji sa tačno jednim izlazom. • Funkcija mora uvek da prima argument. • Funkcija mora uvek da vraća vrednost. • Funkcija treba da proizvodi rezultat samo na osnovu argumenata koje prima i da ne koristi ništa iz spoljašnjeg sveta. • Za zadatu vrednost argumenta postoji samo jedna vrednost funkcije.

Funkcija u Java. Script-u • Primer : Funkcija 1 let procenat. Vrednost = 5;

Funkcija u Java. Script-u • Primer : Funkcija 1 let procenat. Vrednost = 5; let sracunaj. Porez = (value) => { return value/100 * (100 + procenat. Vrednost) } alert (sracunaj. Porez(1500)) • Primer : Funkcija 2 let procenat. Vrednost = 5; let sracunaj. Porez = (value, percent) => { return value/100 * (100 + percent) } alert (sracunaj. Porez(1500, procenat. Vrednost )) • U čemu je razlika?

Funkcija u Java. Script-u • Funkcije se u Java. Script-u javljaju kao samostalne funkcije

Funkcija u Java. Script-u • Funkcije se u Java. Script-u javljaju kao samostalne funkcije i kao metode: • Funkcija je deo koda koji se može pozvati svojim imenom. // Samostalna funkcija let simple = (a) => {return a} alert (simple(5)) //Poziv: ime • Metoda je deo koda koji se mora pozvati svojim imenom pridruženim objektu. // Metoda (ovde je funkcija svojstvo objekta) let obj = {simple : (a) => {return a} } alert (obj. simple(5)) // Poziv: objekat. ime

Referencijalna transparentnost • Kaže se da je kontekst u rečenici referencijalno transparentan ako zamena

Referencijalna transparentnost • Kaže se da je kontekst u rečenici referencijalno transparentan ako zamena nekog termina drugim terminom kojim se označava isti entitet ne menja značenje. • Funkcija koja za zadati ulaz vraća uvek isti izlaz zadovoljava uslov referencijalne transparentnosti (RT). • Referencijalna transparentnost omogućuje model supstitucije u kome se funkcija na svakom mestu može zamenuti svojom vrednošću: var identity = (i) => { return i } // RT funkcija sum(4, 5) + identity(1) ↔ sum(4, 5) + 1

Bočni efekti • Bočni efekat je bilo koje stanje aplikacije koje se može opaziti

Bočni efekti • Bočni efekat je bilo koje stanje aplikacije koje se može opaziti izvan pozvane funkcije i različito je od povratne vrednosti funkcije, n. pr. : • • • Modifikovanje eksterne varijable ili svojstva objekta Logovanje na konzolu Izlaz na ekran, u fajl, na mrežu Trigerovanje bilo kog eksternog procesa Poziv druge funkcije koja proizvodi bočni efekat

Deljeno stanje • Deljeno stanje je bilo koja varijabla, objekat ili memorijski prostor koji

Deljeno stanje • Deljeno stanje je bilo koja varijabla, objekat ili memorijski prostor koji postoji u deljenom dosegu ili kao svojstvo objekta koji se prosleđuje između dosega. • Deljeno stanje može da uključi globalni doseg ili dosege zatvaranja. • Problem sa deljenim stanjem je što je, za razumevanje efekata funkcije, potrebno poznavati kompletnu istoriju svake deljene varijable koju funkcija koristi ili je modifikuje.

Deljeno stanje: primer 1 var global = "global. Value" alert (' global pre poziva

Deljeno stanje: primer 1 var global = "global. Value" alert (' global pre poziva funkcije bad. Function = ' + global)// Ispis: global. Value var bad. Function = (value) => { global = "changed"; return value * 2 } bad. Function (1) alert (' global posle poziva funkcije bad. Function = ' + global)// Ispis: changed

Deljeno stanje: primer 2 // Deljeno stanje const x = { val: 2 };

Deljeno stanje: primer 2 // Deljeno stanje const x = { val: 2 }; // Mutira (menja) deljeno stanje const x 1 = () => x. val += 1; // Mutira (menja) deljeno stanje const x 2 = () => x. val *= 2; x 1(); x 2(); console. log(x. val); // Ispis: 6 /* Ovaj primer je potpuno isti kao levi, izuzev što se funkcije pozivaju obrnutim redosledom. . . */ const y = { val: 2 }; // Mutira (menja) deljeno stanje const y 1 = () => y. val += 1; // Mutira (menja) deljeno stanje const y 2 = () => y. val *= 2; y 2(); y 1(); console. log(y. val); // Ispis: 5

Čiste funkcije • Čista funkcija je funkcija koja: • Zadovoljava uslov referencijalne transparentnosti (za

Čiste funkcije • Čista funkcija je funkcija koja: • Zadovoljava uslov referencijalne transparentnosti (za iste ulaze uvek vraća isti izlaz), i • Nema bočnih efekata. • Sledeća funkcija je čista funkcija var double = (value) => value * 2; • Prima ulaz value • Za sračunavanje izlaza koristi samo primljeni ulaz • Ne menja ništa izvan sebe same • Takva funkcija će za isti ulaz (n. pr. 12) UVEK vratiti isti izlaz (24) • A sledeća funkcija nije čista funkcija var not. Pure = (value) => { return value/100 * (100 + percent. Value) } • Za sračunavanje izlaza koristi primljeni ulaz, ali i vrednost percent. Value koja je spoljašnja vrednost • Takva funkcija će za isti ulaz (n. pr. 12) vratiti izlaz koji će zavisiti od vrednosti percent. Value

Imutabilnost • Argumenti Java. Script objekata su reference • To znači da će se,

Imutabilnost • Argumenti Java. Script objekata su reference • To znači da će se, ako funkcija mutira svojstvo objekta ili parametar niza, time mutirati stanje kome se može pristupiti izvan funkcije. • Čiste funkcije NE SMEJU da mutiraju eksterna stanja • Primeri: • imutabilnost 1. js – funkcija koja nije čista • imutabilnost 2. js – funkcija koja je čista

Imutabilnost: imutabilnost 1. js // Nečista add. To. Cart mutira postojeću korpu (original. Chart)

Imutabilnost: imutabilnost 1. js // Nečista add. To. Cart mutira postojeću korpu (original. Chart) const add. To. Cart = (cart, item, quantity) => { cart. items. push({ item, quantity }); return cart; }; const original. Cart = { items: [] }; const new. Cart = add. To. Cart( original. Cart, { name: "Digital SLR Camera", price: '1495' }, 1 ); console. log( // pretty print original. Cart to the console JSON. stringify(original. Cart, undefined, 2) );

Imutabilnost: imutabilnost 2. js // Čista add. To. Cart() vraća novi cart, // Ne

Imutabilnost: imutabilnost 2. js // Čista add. To. Cart() vraća novi cart, // Ne menja original. const add. To. Cart = (cart, item, quantity) => { return {. . . cart, items: cart. items. concat([{ item, quantity }]), }; }; const original. Cart = { items: [] }; const new. Cart = add. To. Cart( original. Cart, { name: "Digital SLR Camera", price: '1495' }, 1 ); // Ispis original. Cart na konzolu console. log(' original. Cart') console. log( JSON. stringify(original. Cart, undefined, 2) ); // Ispis new. Cart na konzolu console. log(' new. Cart' ) console. log( JSON. stringify(new. Cart, undefined, 2) );

Komponovanje: komponovanje objekata • “U računarskoj nauci, kompozitni tip je bilo koji tip koji

Komponovanje: komponovanje objekata • “U računarskoj nauci, kompozitni tip je bilo koji tip koji se može konstruisati u programu korišćenjem primitivnih tipova programskog jezika i drugih kompozitnih tipova. […] Akt konstruisanja kompozitnog tipa poznat je kao komponovanje. ” Wikipedia • Primitive: const ime = 'Claude'; const prezime = 'Debussy'; • Kompozit: const puno. Ime = { ime: 'Claude' , prezime: 'Debussy' }; • Postoji mnogo različitih vrsta komponovanja

Komponovanje objekata: nedostaci klasnog obrasca • Jedan od načina komponovanja je klasni obrazac dizajna

Komponovanje objekata: nedostaci klasnog obrasca • Jedan od načina komponovanja je klasni obrazac dizajna • Preporuka: “Favor object composition over class inheritance” the Gang of Four, “Design Patterns: Elements of Reusable Object Oriented Software” • Zašto ta preporuka? • The tight coupling problem: Because child classes are dependent on the implementation of the parent class, class inheritance is the tightest coupling available in object oriented design. • The fragile base class problem: Due to tight coupling, changes to the base class can potentially break a large number of descendant classes – potentially in code managed by third parties. The author could break code they’re not aware of. • The inflexible hierarchy problem: With single ancestor taxonomies, given enough time and evolution, all class taxonomies are eventually wrong for new use-cases. • The duplication by necessity problem: Due to inflexible hierarchies, new use cases are often implemented by duplication, rather than extension, leading to similar classes which are unexpectedly divergent. Once duplication sets in, it’s not obvious which class new classes should descend from, or why. • The gorilla/banana problem: “…the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle. ” Joe Armstrong, “Coders at Work”⁷

Komponovanje objekata: Java. Script • Najčešća vrsta kompozicije objekata u Java. Script-u je konkatenacija

Komponovanje objekata: Java. Script • Najčešća vrsta kompozicije objekata u Java. Script-u je konkatenacija (“mixin kompozicija”) objekata • Glavna (za FP izuzetno važna osobina) je imutabilnost: • Konkatenacijom komponenti formira se novi kompozit a komponente ostaju očuvane

Primer: Komponovanje klasnim obrascem class Foo { constructor () { this. a = 'a'

Primer: Komponovanje klasnim obrascem class Foo { constructor () { this. a = 'a' } } class Bar extends Foo { constructor (options) { super(options); this. b = 'b' } } const my. Bar = new Bar(); // Rezultat: {a: 'a', b: 'b'}

Primer: Isto komponovanje mixin-om const a = { a: 'a' }; const b =

Primer: Isto komponovanje mixin-om const a = { a: 'a' }; const b = { b: 'b' }; const c = {. . . a, . . . b}; // Rezultat : // {a: 'a', b: 'b'}

Komponovanje: komponovanje funkcija • Komponovanje funkcija je proces primene funkcije na izlaz druge funkcije.

Komponovanje: komponovanje funkcija • Komponovanje funkcija je proces primene funkcije na izlaz druge funkcije. • Matematička notacija je: Za dve zadate funkcije f i g, kompozicija je (f ◦ g)(x) = f(g(x)) simbol ◦ označava kompoziciju a značenje je: prvo se evaluira (sračunava) g(x) pa se na rezultat primenjuje funkcija f (dobijeni izlaz se prosleđuje kao argument funkciji f) • Primer: const g = n => n + 1; const f = n => n * 2; const do. Stuff = x => { const after. G = g(x); const after. F = f(after. G); return after. F; }; alert (' Rezultat je: ' + do. Stuff(20); // Ispis: Rezultat je: 42

Komponovanje funkcija: primeri • Fun comp 1. js • Fun comp 2. js

Komponovanje funkcija: primeri • Fun comp 1. js • Fun comp 2. js

FP: Apstrakcija koda • Apstrakcija koda je izdvajanje funkcionalnosti u funkcije koje se mogu

FP: Apstrakcija koda • Apstrakcija koda je izdvajanje funkcionalnosti u funkcije koje se mogu primenjivati svaki put kada odgovarajuća funkcionalnost treba da se ostvari – u stvari, pravljenje funkcija. • Primer: Nad nizovima vrlo često određene operacije treba primeniti na sve elemente niza. Apstrakcija za ovu funkcionalnost može da bude funkcija for. Each : const for. Each = (array. In, array. Out, fn) => { // array. In – originalni niz // fn – funkcija koja se primenjuje na članove array. In // array. Out - rezultat primene funkcije fn let i; for(i=0; i<array. In. length; i++){ array. Out [i] = fn(array. In[i]) } } let ulaz = [1, 2, 3] console. log (' Ulaz ' + ulaz) let izlaz = [] for. Each(ulaz, izlaz, (data) => console. log(data)) console. log (' Izlaz za fn => console. log(data): ' + izlaz) for. Each(ulaz, izlaz, (data) => 2*data) console. log (' Izlaz za fn => 2*data: ' + izlaz)

FP: Deklarativnost koda • Imperativni stil programiranja rezultuje kodom koji izvršiocu kaže KAKO TAČNO

FP: Deklarativnost koda • Imperativni stil programiranja rezultuje kodom koji izvršiocu kaže KAKO TAČNO TREBA DA RADI (koje korake da izvršava) da bi rešio određeni zadatak: const for. Each = (array. In, array. Out, fn) => { // array. In – originalni niz // fn – funkcija koja se primenjuje na članove array. In // array. Out - rezultat primene funkcije fn let i; for(i=0; i<array. In. length; i++){ array. Out [i] = fn(array. In[i]) } } • Deklarativni stil programiranja rezultuje kodom koji izvršiocu kaže ŠTA TREBA DA URADI da bi rešio određeni zadatak for. Each(ulaz, izlaz, (data) => console. log(data))

Prednosti FP-a • Kod koji je lakši za testiranje – zato što nema (ili

Prednosti FP-a • Kod koji je lakši za testiranje – zato što nema (ili ima kontrolisane) bočne efekte • Razumljiviji (čitljiviji) kod – apstrakcija i deklarativnost uklanjaju nepotrebne detalje // Imperativno let dva = 2 let cetiri = 2*2 // a šta dalje da bismo dobili 8? // Deklarativno (funkcionalno) const double = (value) => value * 2 alert (double (2)) // a za 8 jednostavno: alert (double (2)))

Redosled pozivanja može da bude važan • Nečiste funkcije : let global = “nešto"

Redosled pozivanja može da bude važan • Nečiste funkcije : let global = “nešto" let function 1 = (input) => { // radi nad varijablom // input // menja varijablu // global = “nešto. Drugo" } let function 2 = () => { if(global === “nešto") { // poslovna logika } } • Iste funkcije – “očišćene”: let function 1 = (input, global) => { // radi nad varijablom // input // menja varijablu // global = "nešto. Drugo" } let function 2 = (global) => { if(global === “nešto") { //poslovna logika } }

Mogućnost keširanja const dugotrajna. Fun = (ip) => { /* radi nešto dugotrajno i

Mogućnost keširanja const dugotrajna. Fun = (ip) => { /* radi nešto dugotrajno i vrati se kada završiš */ } • Šta ako ovu funkciju treba pozivati više puta sa istim ulazom? • Keširanje: let d. Fun. Skladiste = { 2 : 3, 4 : 5 } //proveri da ima ključeva u d. Fun. Skladiste; ako ima, preuzmi // postojeći rezultat iz d. Fun. Skladiste, inače ažuriraj // objekat d. Fun. Skladiste dugotrajna. Fun. has. Own. Property(ip) ? dugotrajna. Fun. Skladiste[ip] : dugotrajna. Fun. Skladiste[ip] = dugotrajna. Fun(ip)

Nedostaci FP-a • Ulaz/izlaz : U/I se oslanja na bočne efekte pa je inherentno

Nedostaci FP-a • Ulaz/izlaz : U/I se oslanja na bočne efekte pa je inherentno ne-funkcionalan. FP se sa ovim problemom bori tako što vrši izolovanje U/I-a. • Cena rekurzije (memorijski i procesno), koja se smatra jednim od najboljih delova FP-a, je veoma visoka. FP se sa ovim problemom bori tako što uvodi partikularna rešenja (n. pr. terminalna rekurzija) što, kasnije, na žalost dovodi do drugih problema kao što je optimizacija koda. • Terminološki problemi koji, delom, sigurno potiču iz čvrstog oslonca FP-a na matematiku uz istovremenu činjenicu da klijentela (prosečni programeri) nije baš mnogo familijarna sa matematikom. FP se sa ovim problemom nosi tako što prosečan korisnik operiše sa pojednostavljenim konceptima i idejama svedenim na direktne praktične primene. • Ne-funkcionalna priroda današnjih računara – računar koji mi koristimo je Turingova mašina, a ne Čerčov Lambda račun pa se funkcionalno programiranje ne može direktno implementirati do hardvera. FP se sa ovim problemom bori tako što realno sgledava nemogućnost primene modela “kornjače do dna”. • Problem programiranja stanja, što zahtevaju mnoge aplikacije, jer predstavljanje stanja u funkcionalnom programiranju nije ni blizu toliko intuitivno kao većina drugih funkcionalnih operacija. FP se sa ovim problemom nosi razvojem metoda za funkcionalnu reprezentaciju stanja i pratećih softverskih alata kao što je, na primer, Redux. js.

Literatura za predavanje 1. Z. Konjović, Funkcionalno programiranje, Uvod u FP, slajdovi sa predavanja,

Literatura za predavanje 1. Z. Konjović, Funkcionalno programiranje, Uvod u FP, slajdovi sa predavanja, dostupni na stranici predmeta 2. E. Elliot, Composing Software - An Exploration of Functional Programming and Object Composition in Java. Script, Leanpub, 2019, Poglavlje “A Functional Programmer’s Introduction to Java. Script” 3. A. Aravinth, Početno Java. Script Funkcionalno programiranje sa Java. Script-om Ecma. Script 6, (Poglavlje 1), dostupno na stranici predmeta