This is an old revision of the document!


Haskell: Imagini funcționale

  • Data publicării: 09.04.2024
  • Data ultimei modificări: 09.04.2024
  • Deadline hard: ziua laboratorului 10

Obiective

  • Aplicarea mecanismelor funcționale, de tipuri (inclusiv polimorfism) și de evaluare leneșă din limbajul Haskell.
  • Exploatarea evaluării leneșe pentru decuplarea conceptuală a prelucrărilor realizate.

Descriere generală și organizare

În literatura de specialitate, embedded domain-specific languages (EDSL) sunt considerate unele dintre principalele aplicații ale programării funcționale. Sintagma merită explicată pe componente. Un domain-specific language (DSL) este un limbaj de programare restrâns, dedicat unui domeniu particular; exemple bine-cunoscute sunt HTML (pentru descrierea paginilor web) și SQL (pentru interogarea bazelor de date). Acestea se opun așa-ziselor general-purpose languages, care pot fi utilizate într-o gamă largă de domenii, fiind cazul limbajelor C, Java, Python, și al limbajelor studiate la PP. Deși restrânse, DSL-urile sunt înzestrate cu o serie de operații expresive dedicate, care simplifică masiv modelarea în cadrul domeniului specific către care au fost țintite.

Pentru implementarea unui DSL, există următoarele două variante:

  • stand-alone: un interpretor sau un compilator sunt scrise de la zero pentru limbajul respectiv, necesitând o cantitate mare de efort
  • embedded: limbajul este integrat sub forma unei biblioteci într-un limbaj existent (de exemplu, Haskell), valorificându-se astfel construcțiile limbajului gazdă, precum și interpretorul și/sau compilatorul existent.

Continuând pe varianta embedded (de exemplu, în Haskell), un DSL poate utiliza două tipuri de reprezentări în cadrul limbajului gazdă:

  • shallow embeddings: obiectele din cadrul DSL-ului au reprezentări concrete în limbajul gazdă; de exemplu, dacă dorim să creăm un mic limbaj pentru definirea expresiilor aritmetice, fiecare expresie ar putea fi reprezentată prin valoarea ei (Int etc.), iar operațiile pe expresii (adunare etc.) manipulează valorile acestora
  • deep embeddings: obiectele din cadrul DSL-ului au reprezentări abstracte, ca descrieri ale operațiilor care construiesc acele obiecte; continuând exemplul expresiilor aritmetice de mai sus, fiecare expresie poate fi reprezentată prin arborele ei sintactic, care poate fi interpretat ulterior în diverse moduri; o posibilă interpretare vizează obținerea valorii expresiei, realizând astfel corespondența cu shallow embedding-ul de mai sus.

Tema propune ca studiu de caz un mic DSL embedded în Haskell pentru lucrul cu imagini. Pe parcursul etapelor, vom explora ambele modalități de reprezentare de mai sus (shallow, respectiv deep), avantajele și dezavantajele fiecăreia, precum și corespondența lor conceptuală.

Tema este împărțită în 3 etape:

  • una pe care o veți rezolva după laboratorul 7, cu deadline în ziua laboratorului 8
  • una pe care o veți rezolva după laboratorul 8, cu deadline în ziua laboratorului 9
  • una pe care o veți rezolva după laboratorul 9, cu deadline în ziua laboratorului 10.

Deadline-ul depinde de semigrupa în care sunteți repartizați. Restanțierii care refac tema și nu refac laboratorul beneficiază de ultimul deadline, și anume în zilele de 26.04, 10.05, respectiv 17.05.

Rezolvările tuturor etapelor pot fi trimise până în ziua laboratorului 10 (deadline hard pentru toate etapele). Orice exercițiu trimis după un deadline soft se punctează la jumătate. Cu alte cuvinte, nota finală pe etapă se calculează conform formulei: n = (n1 + n2) / 2 (n1 = nota obținută înainte de deadline; n2 = nota obținută după deadline). Când toate submisiile preced deadline-ul, nota pe ultima submisie constituie nota finală (întrucât n1 = n2).

În fiecare etapă, veți valorifica ce ați învățat în săptămâna anterioară și veți avea la dispoziție un schelet de cod, cu toate că vor exista trimiteri la etapele anterioare. Enunțul caută să ofere o imagine de ansamblu atât la nivel conceptual, cât și în privința aspectelor care se doresc implementate, în timp ce detaliile se găsesc direct în schelet.

Etapa 1

În această etapă, ne vom concentra pe o reprezentare concretă a imaginilor din cadrul DSL-ului menționat anterior, corespunzătoare ideii de shallow embeddings. Astfel, fiecare imagine, denumită de acum încolo regiune (bidimensională), va fi reprezentată prin funcția ei caracteristică, având tipul Point → Bool. Rolul acestei funcții este de a preciza care puncte din spațiu aparțin regiunii respective (rezultat True) și care nu (rezultat False). Similar, o transformare a unei regiuni (de exemplu, translație), este reprezentată printr-o funcție care operează la nivelul punctelor, având tipul Point → Point.

Veți implementa funcții care:

  • verifică apartenența unui punct la o regiune
  • definesc regiuni simple (dreptunghi, cerc)
  • combină regiuni
  • desenează regiuni la consolă
  • definesc transformări simple (translație, scalare)
  • combină transformări
  • aplică transformări asupra regiunilor
  • pentru bonus, determină lungimea minimă a unui drum dintre două puncte, evitând o regiune dată, utilizând BFS în spațiul bidimensional.

Deși numărul funcțiilor este mai mare, majoritatea se implementează în câteva cuvinte. cicles și infiniteCircles sunt deja implementate, dar sarcina este de a înțelege utilitatea evaluării leneșe în cadrul acestora.

Construcțiile și mecanismele de limbaj pe care le veți exploata în rezolvare sunt:

  • liste
  • funcționale pe liste și nu numai, ocazie cu care veți intra în contact atât cu cele standard, care se găsesc și în Racket, cât și cu altele specifice în Haskell
  • list comprehensions, pentru descrierea concisă a unor prelucrări
  • evaluare leneșă, implicită în Haskell, pentru decuplarea conceptuală a transformărilor din cadrul unei secvențe
  • pattern matching, pentru radiografierea structurilor (perechi etc.)
  • de legare a variabilelor locale (let sau where).

Modulul de interes din schelet este Shallow, care conține reprezentarea regiunilor și a transformărilor, precum și operațiile pe care trebuie să le implementați. Găsiți detaliile despre funcționalitate și despre constrângerile de implementare, precum și exemple, direct în schelet. Aveți de completat definițiile care încep cu *** TODO ***.

Pentru rularea testelor, încărcați în interpretor modulul TestShallow și evaluați main.

Este suficient ca arhiva pentru vmchecker să conțină doar modulul Shallow.

Precizări

  • Încercați să folosiți pe cât posibil funcții predefinite din modulul Data.List. Este foarte posibil ca o funcție de prelucrare de care aveți nevoie să fie deja definită aici.
  • Ca sugestie, exploatați cu încredere pattern matching, case și gărzi, în locul if-urilor imbricate.

Resurse

Referințe

  • de Moor, O., & Gibbons, J. (Eds.). (2003). The Fun of Programming. Palgrave Macmillan.
pp/24/teme/haskell-imagini-functionale.1712668184.txt.gz · Last modified: 2024/04/09 16:09 by mihnea.muraru
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0