Paradigma Funzionale Nicola Fanizzi Linguaggi di Dipartimento di Informatica Programmazione [010194] Università degli Studi di Bari 25 mag, 2016 Sommario 1 Valutazione per valore Valutazione per nome Valutazione lazy Confronto delle strategie Computazione Senza Stato Espressioni e Funzioni Ricorsione Computazione come Riduzione Ingredienti Fondamentali Semantica 2 Valutazione Valori Sostituzione senza cattura Strategie di valutazione LdP.ITPS/[email protected] 3 Programmazione nei Linguaggi Funzionali Ambiente Locale Interattività Tipi Pattern Matching Oggetti Infiniti Aspetti Imperativi Paradigma Funzionale 25 mag, 2016 2 / 56 Agenda Ingredienti Fondamentali 1 Computazione Senza Stato Espressioni e Funzioni Computazione come Riduzione LdP.ITPS/[email protected] 2 3 Valutazione Programmazione nei Linguaggi Funzionali Paradigma Funzionale 25 mag, 2016 3 / 56 Introduzione: Linguaggi Convenzionali Linguaggi convenzionali: modello di calcolo basato sulla trasformazione di stato base: variabile modificabile contenitore con un nome al quale si possono assegnare valori diversi durante la computazione l’associazione con il nome viene conservata nell’ambiente costrutto principale: assegnazione cambia il valore della variabile non l’associazione nome-locazione ossia modifica r-valori ma non l-valori, fissati una volta per tutte [al momento della dichiarazione] si differenziano per livello d’astrazione, tipi, istruzioni per la manipolazione delle variabili diverse astrazioni della macchina di von Neumann LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 4 / 56 Intro – linguaggi funzionali Calcolare senza usare var. modificabili, cioè senza assegnazione ossia senza ricorrere ad uno stato (o memoria) calcolo = riscrittura di espressioni i cambiamenti si verificano nell’ambiente possibilità di manipolare funzioni [anche di ordine superiore] senza assegnazione cade la necessità dell’iterazione: basta la ricorsione per poter definire computazioni indefinitamente lunghe [anche divergenti] I linguaggi di questo paradigma si dicono funzionali Storia: paradigma vecchio come quello imperativo Dagli anni ’30 oltre a MDT esisteva il λ-calcolo: modello astratto per definire funzioni calcolabili LISP primo ling. seguito da SCHEME, ML, MIRANDA, HASKELL, ... LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 5 / 56 Espressioni e Funzioni In matematica, spesso ambiguità sui concetti di definizione ed applicazione di una funzione Esempio sia f (x) = x2 che associa ad x il suo quadrato; se x = 3, ne consegue che f (x) = 9 l’espressione sintattica f (x) denota due cose molto diverse: la prima volta, serve ad introdurre il nome f di una data funzione; la seconda, denota il risultato dell’applicazione di f ad un dato valore In matematica il contesto aiuta a distinguere i due casi; nei ling. di programmazione questi casi vanno distinti LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 6 / 56 Espressioni e Funzioni [. . . cont.] Per distinguere linguisticamente nome e corpo della funzione, nella sintassi di ML: val f = fn x => x * x; val introduce una dichiarazione: si aggiunge all’ambiente una nuova associazione nome-valore nome: f valore: trasformazione di x in x * x nei ling. funzionali le funzioni sono valori esprimibili: risultato della valutazione di un’espressione complessa fn introduce un’espressione che denota una funzione LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 7 / 56 Espressioni e Funzioni [. . . cont.] Per l’applicazione di una funzione ad un argomento, si mantiene la notazione consueta: f(2) (f 2) f 2 si può usare val per introdurre nuovi nomi: val quattro = f 2; LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 8 / 56 Espressioni e Funzioni [. . . cont.] Conseguentemente, si può definire (ed applicare) una funzione senza doverle assegnare un nome: funzione anonima Esempio l’espressione (fn y => y+1) (6); ha valore 7 come risultato dell’applicazione della funzione fn y => y+1 all’argomento 6 Si assumerà (come in ML) che l’applicazione sia denotata con la giustapposizione, con associatività a sinistra (notazione prefissa) Esempio Se g è il nome di una funzione g a1 a2 ... ak sta per (...((g a1) a2) ... ak) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 9 / 56 Espressioni e Funzioni [. . . cont.] Un’espressione funzionale può occorrere all’interno di un’altra Esempio val somma = fn x => (fn y => y+x); il valore di somma è una funzione che, dato l’argomento, x, restituisce una funzione anonima che, dato un argomento y, restituisce x+y Si può usare somma in tante maniere diverse: val tre = somma 1 2; val sommadue = somma 2; val cinque = sommadue 3; NB sommadue funzione definita come risultato della valutazione di un’altra espressione LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 10 / 56 Notazione compatta Esempio in ML, funzione f che calcola il quadrato definita: fun f x = x*x; In generale, la forma fun F x1 x2 ... xn = corpo ; è un’abbreviazione (syntactic sugar) di val F = fn x1 => (fn x2 => ... (fn xn => corpo )...); Esempio Si consideri fun comp f g x = f(g(x)); che restituisce la composta dai suoi primi due argomenti, a loro volta due funzioni LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 11 / 56 Ricorsione Ogni linguaggio funzionale consente la definizione di funzioni ricorsive Esempio Supponendo di avere un costrutto condizionale (sintassi ML: if then else), si può definire il fattoriale: fun fatt n = if n=0 then 1 else n*fatt(n-1); LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 12 / 56 Computazione come Riduzione riduzione: valutazione come processo di riscrittura: le funzioni aritmetiche e l’espressione condizionale hanno una propria semantica predefinita in un’espressione complessa, una sotto-espressione della forma funzione applicata ad un argomento viene sostituita testualmente dal corpo della funzione istanziando il parametro formale con quello effettivo (regola di copia del passaggio per nome) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 13 / 56 Computazione come Riduzione [. . . cont.] Esempio fact 3 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ (fn n => if n=0 then 1 else n*fact(n-1)) 3 if 3=0 then 1 else 3*fact(3-1) 3*fact(3-1) 3*fact(2) 3*((fn n => if n=0 then 1 else n*fact(n-1)) 2) 3*(if 2=0 then 1 else 2*fact(2-1)) 3*(2*fact(2-1)) 3*(2*fact(1)) 3*(2*((fn n => if n=0 then 1 else n*fact(n-1)) 1)) 3*(2*(if 1=0 then 1 else 1*fact(1-1)) 3*(2*(1*fact(0)) 3*(2*(1*((fn n => if n=0 then 1 else n*fact(n-1)) 0))) 3*(2*(1*(if 0=0 then 1 else 0*fact(0-1)))) 3*(2*(1*1)) 6 NB: manipolazione di stringhe: no variabili, no stack LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 14 / 56 Computazione come Riduzione [. . . cont.] Esempio [identità] fun K x y = x; fun S p q r = p r (q r); 3 val a = ...; 1 2 si valuta S K K a: S K K a 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ 7−→ LdP.ITPS/[email protected] (fn (fn (fn K a (fn (fn a p => (fn q => (fn r => p r (q r)))) K K a q => (fn r => K r (q r))) K a r => K r (K r)) a (K a) x => (fn y => x)) a (K a) y => a) (K a) Paradigma Funzionale 25 mag, 2016 15 / 56 Computazione come Riduzione [. . . cont.] Data la definizione: fun r x = r(r(x)); ogni computazione che comporti la valutazione di r porta ad una riscrittura all’infinito: computazione divergente con risultato indefinito LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 16 / 56 Ingredienti Fondamentali Da un punto di vista sintattico: espressione non esiste la nozione di comando (necessita di quella di stato) valori ed operatori primitivi Costrutti per la def. di espressioni: astrazione data una espressione exp e un identificatore x, consente la costruzione dell’espressione fn x => exp che denota la funzione che trasforma il parametro formale x in exp (exp astratta dal valore specifico legato a x) applicazione f_exp a_exp, denota l’applicazione della funzione f_exp all’argomento a_exp LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 17 / 56 Ingredienti Fondamentali [. . . cont.] Omogeneità tra dati e programmi: nessun vincolo sulla possibilità di passare funzioni come argomenti di altre funzioni restituire funzioni come risultato di altre funzioni (di ordine su superiore) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 18 / 56 Semantica Programma serie di definizioni di valori che introducono nuove associazioni nell’ambiente e possono richiedere la valutazione di espressioni complesse Operazioni per la valutazione (riduzione): ricerca di binding di un identificatore in un ambiente e sostituzione con la definizione nome = abbreviazione per il valore associato applicazione di un’espressione funzionale ad un argomento si usa la β-regola, una variante della regola di copia LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 19 / 56 Semantica [. . . cont.] redex (reducible expression): applicazione della forma ((fn x => corpo) arg) ridotto di una redex ((fn x => corpo) arg): espressione ottenuta sostituendo in corpo ogni occorrenza libera di x con una copia di arg (evitando la cattura) β-regola se in exp compare una redex come sotto-espressione, essa si riduce a exp1; notazione: exp → exp1 exp1 ottenuta da exp sostituendo la redex con il ridotto Implementazione La macchina astratta adotta meccanismi di garbage-collection: in caso presenza di funzioni come risultato, va preservato l’ambiente locale per un tempo illimitato LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 20 / 56 Agenda 1 Computazione Senza Stato 2 Valutazione Valori LdP.ITPS/[email protected] Sostituzione senza cattura Strategie di valutazione 3 Programmazione nei Linguaggi Funzionali Paradigma Funzionale 25 mag, 2016 21 / 56 Valutazione Punti aperti: condizione di terminazione della riduzione, la forma semplice per un valore semantica precisa della β-regola: (cattura,) ordine di riduzione in caso di più redex LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 22 / 56 Valori valore: espressione non ulteriormente riducibile Tipologie semplici primitivi predefiniti (interi, booleani, caratteri, . . . ) funzionali val nome = exp comporta la valutazione dell’espressione a destra di = e il binding del valore risultante con il nome a sinistra NB: la valutazione non ha luogo nell’astrazione fn x => exp rappresenta un valore funzionale: le redex eventualmente contenute in exp non vengono semplificate fino alla sua applicazione ad un argomento LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 23 / 56 Valori [. . . cont.] Esempio valore associato a G? val G = fn x => ((fn y => y+1) 2); Alternative: secondo la semantica informale, il valore sarebbe: fn x => 3 corpo semplificato valutando la redex contenuta invece: come nei ling. convenzionali, valutazione alla chiamata/applicazione: fn x => ((fn y => y+1) 2) nessuna valutazione del corpo LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 24 / 56 Sostituzione senza cattura Sostituzione senza cattura: implementata attraverso le chiusure Sintatticamente: nomi distinti convenzione in ogni espressione, si tengono distinti i nomi per i parametri formali e quelli delle altre variabili (non param.) In tal modo non si verifica cattura LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 25 / 56 Strategie di valutazione Esempio fun fun 3 fun 4 fun 5 val 1 2 valutazione di v K x y = x; r z = r(r(z)); D u = if u=0 then 1 else u; succ v = v+1; v = K (D (succ 0)) (r 2); per la β-regola, a destra della def. di v ci sono 4 redex candidate: K (D (succ 0)), D (succ 0), succ 0, r 2 Quale ridurre prima? Da sinistra destra? Allora quale fra K (D (succ 0)), D (succ 0), succ 0 ? Anche con questo ordinamento 3 possibili strategie LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 26 / 56 Valutazione per valore Strategia dell’ordine d’applicazione, eager, o interna: una redex viene valutata solo se l’espressione che costituisce la parte argomento è già un valore algoritmo 1 cercare da sinistra la prima occorrenza di applicazione: (f_exp a_exp) 2 applicando ricorsivamente lo stesso metodo, valutare f_exp riducendola ad un valore funzionale (fn x => ...) 3 valutare a_exp, riducendola ad un valore val 4 ridurre ((fn x => ...) val) e tornare a (1) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 27 / 56 Valutazione per valore [. . . cont.] Nell’es. precedente val v = K (D (succ 0)) (r 2); al passo (1) si seleziona K (D (succ 0)) con i passi (1), (2) e (3) si ha che K, D e succ sono già valori il nome è un’abbreviazione dell’espressione associata redex da semplificare: succ 0 → ((fn v => v + 1) 0) → 1 successiva: (D 1) → ((fn u => if u = 0 then 1 else u) 1) → 1 successiva: (K 1) → (fun y => 1) val. funzionale semplice espr. complessiva, arg. da valutare (fun y => 1)(r 2) (r 2) → r (r 2) → r (r (r 2)) → · · · divergente! In conclusione, v indefinito LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 28 / 56 Valutazione per nome Strategia dell’ordine normale o esterna: una redex viene valutata prima della sua parte argomento algoritmo 1 2 3 sia (f_exp a_exp) la prima applicazione che occorre, a partire da sinistra, nell’espressione si valuta f_exp (applicando questo algoritmo ricorsivamente) fino a ridurla ad un valore funzionale (fn x => ...) si riduce ((fn x => ..) a_exp) usando la β-regola e si torna a (1) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 29 / 56 Valutazione per nome [. . . cont.] Nell’es. precedente: val v = K (D (succ 0)) (r 2); la prima redex da ridurre è K (D (succ 0)) → fn y => D (succ 0) valore funzionale la nuova forma dell’espressione sarà: (fn y => D (succ 0)) (r 2) nota: prescinde da y riducendo la redex più esterna (passo 3.): D (succ 0) → if (succ 0)=0 then 1 else (succ 0) → if 1=0 then 1 else (succ 0) → succ 0 → 1 associato a v LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 30 / 56 Valutazione lazy Osservazioni sulla valutazione per nome una stessa redex può essere valutata più volte per duplicazioni richieste dalla riscrittura nell’es. precedente, (succ 0) è duplicata a causa di D e viene ridotta due volte nel condizionale nel corpo di D if (succ 0)=0 then 1 else (succ 0) Idea: posporre la valutazione di un argomento rispetto all’applicazione di una funzione consente di non divergere ma ha un prezzo dipende dal costo per la valutazione di ogni copia LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 31 / 56 Valutazione lazy [. . . cont.] Strategia lazy come quella per nome ma il valore di una redex viene calcolato solo alla prima occorrenza e conservato per essere utilizzato in caso di duplicati Le strategie per nome e lazy sono dette a chiamata, o by need: si semplifica una redex solo se necessario LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 32 / 56 Confronto delle strategie Teorema (strategie di valutazione) Sia exp un’espressione chiusa∗ . Se la valutazione di exp la riduce al valore primitivo val per ognuna delle strategie allora si produce val per la strategia per nome. Se, invece, diverge usando la strategia per nome allora diverge anche usando le altre. Osservazioni non è possibile che da exp si producano valori primitivi differenti a seconda della strategia le strategie possono differenziarsi solo in caso di divergenza di una mentre le altre convergono su un valore (*) Un’espressione è chiusa sse tutte le sue variabili sono legate in qualche costrutto fn LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 33 / 56 Confronto delle strategie [. . . cont.] Criterio per avere un linguaggio funzionale puro: Fissata la strategia in un dato ambiente, la valutazione di tutte le occorrenze di una stessa espressione produce sempre lo stesso valore Questo sembra favorire le strategie con chiamate by need anche dal punto di vista dell’efficienza LISP, SCHEME e ML adottano la strategia per valore (includendo anche alcuni dei principali aspetti imperativi) MIRANDA e HASKELL adottano quella lazy (ling. funzionali puri) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 34 / 56 Agenda 1 Computazione Senza Stato 2 Valutazione 3 Ambiente Locale Interattività Tipi Pattern Matching Oggetti Infiniti Aspetti Imperativi Programmazione nei Linguaggi Funzionali LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 35 / 56 Programmazione nei Linguaggi Funzionali Il meccanismi presentati sono sufficienti per descrivere un linguaggio Turing-completo: si può esprimere qualunque funzione computabile Ogni linguaggio funzionale aggiunge a tale nucleo altri costrutti più espressivi per facilitare la programmazione LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 36 / 56 Ambiente Locale Un linguaggio di prog. moderno necessità di un ambiente strutturato per le definizioni (non solo uno scope globale) si adotta una politica con scope a più livelli esempio, costrutto let let x = exp in exp1 end binding di x al valore di exp in un ambito che include exp1 nota: le espressioni funzionali determinano scope annidati gli ambienti relativi includono i parametri formali legati a quelli effettivi ai fini della valutazione, il costrutto dell’es. può essere considerato come syntactic sugar per (fn x => exp1) exp LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 37 / 56 Interattività Ogni linguaggio funzionale presenta anche un ambiente interattivo si usa il linguaggio per immettere espressioni che la macchina astratta valuta restituendone il valore le definizioni sono espressioni che modificano l’ambiente globale (e possono restituire valori) le implementazioni sono tipicamente interpretate esistono, tuttavia, implementazioni che compilano le definizioni alla loro prima immissione nell’ambiente LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 38 / 56 Tipi Sistemi dei tipi tipi primitivi standard (interi, boolean, caratteri): valori ed operazioni tipizzazione statica tranne SCHEME, che adotta la tipizzazione dinamica definizione di nuovi tipi es. coppie, liste, record (n-ple con etichette dei campi) in ML, si può definire la funzione somma_c che prende in input una coppia di interi e ne restituisce la somma: fun somma_c (n1,n2) = n1 + n2; NB: fornire a somma_c un solo argomento come nel currying (o applicazione parziale) tipi di funzioni, funzioni come valori valori denotabili ed esprimibili (anche memorizzabili, se sono inclusi anche aspetti imperativi) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 39 / 56 Tipi [. . . cont.] espressioni funzionali illegali (nei ling. tipizzati) perché non può essere assegnato un tipo preciso es. funzione illegale: fun F f n = if n=0 then f(1) else f("pippo"); tipo di f ? int -> ’a oppure string -> ’a ? dove ’a denota una variabile di tipo, ossia un tipo generico non istanziato LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 40 / 56 Tipi [. . . cont.] espressione illegale, auto-applicazione: fun Delta x = x x; (x x) è illegale: non c’è modo di assegnare a x un singolo tipo occorrendo a sinistra di una applicazione, dev’essere di tipo ’a -> ’b essendo anche argomento di una funzione, deve avere il tipo richiesto, ossia ’a non c’è modo di unificare tali espressioni In SCHEME , mancando la tipizzazione forte: X Delta è legale X si può anche applicare Delta a se stessa: (Delta Delta) è un esempio di programma divergente X senza controllo statico si può scrivere (4 3) dato che 4 non è una funzione, errore a runtime In ML, è ammesso il polimorfismo LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 41 / 56 Pattern Matching Esempio calcolo dell’n-esimo termine della serie di Fibonacci: fun Fibo n = if n = 0 then 1 else if n = 1 then 1 3 else Fibo(n-1) + Fibo(n-2); 1 2 inefficiente! LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 42 / 56 Pattern Matching [. . . cont.] Meccanismo di pattern matching ammesso da ML e HASKELL fun Fibo 0 = 1 | Fibo 1 = 1 3 | Fibo n = Fibo(n-1) + Fibo(n-2); 1 2 def. (ricorsiva) per casi parametro formale: pattern schema (modello) di espressione fatto di variabili, costanti e altri costrutti, a seconda del sistema di tipo quando la funzione viene applicata ad un param. effettivo questo viene confrontato con i pattern (nell’ordine) e si sceglie il corpo relativo alla prima corrispondenza (match) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 43 / 56 Pattern Matching [. . . cont.] Meccanismo flessibile con tipi strutturati Esempio liste, in ML: si usano valori separati da virgole tra parentesi quadre ["uno", "due", "tre"] lista di 3 stringhe :: denota l’operatore cons (aggiunta di un elemento in testa) "zero"::["uno", "due", "tre"] espressione che ha per valore la lista ["zero", "uno", "due", "tre"] nil denota la lista vuota "quattro"::nil ha valore ["quattro"] Esempio 1 2 Funzione che calcola la lunghezza di una lista fun lung nil = 0 | lung e::resto = 1 + lung(resto); LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 44 / 56 Pattern Matching [. . . cont.] Osservazioni i nomi nei pattern servono come parametri formali per denotare parti del parametro effettivo maggiore concisione rispetto a: 1 2 fun lung list = if list = nil then 0 else 1 + lung(tl list); che richiede una funzione di selezione (tl) della lista priva della testa (coda o tail) selezione implicita nel pattern matching LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 45 / 56 Pattern Matching [. . . cont.] Vincolo: una variabile non deve comparire più volte in un pattern Esempio fun | 3 | 4 | 1 2 Eq Eq Eq Eq funzione per il test di uguaglianza nil = false [e] = false x::x::resto = true x::y::resto = false; illegale dal punto di vista sintattico (il 3° pattern contiene due x) Pattern-matching 6= unificazione unificazione meccanismo più generale (ma più complesso), adottato dai ling. del paradigma logico LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 46 / 56 Oggetti Infiniti Adottando strategie per nome o lazy (come in HASKELL1 ), si possono definire e manipolare strutture dati potenzialmente infinite (es. gli stream) Nozione di valore valutazione eager, un valore di tipo T list è una lista i cui elementi sono valori di tipo T valutazione lazy, questa non è una buona nozione di valore perché potrebbe richiedere la valutazione di redex inutili, contro la filosofia call-by-need LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 47 / 56 Oggetti Infiniti [. . . cont.] Esempio valutazione lazy Selezione di testa e coda di una lista non-vuota 1 2 fun hd x::resto = x; fun tl x::resto = resto; Consideriamo hd [2, ((fn n => n+1) 2)] Per calcolarne il valore (cioè 2), non serve ridurre la redex nel secondo elemento (mai usato nel corpo di hd) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 48 / 56 Oggetti Infiniti [. . . cont.] In un contesto by-need, un valore di tipo list è un’espressione della forma: exp1 :: exp2 dove exp1 e exp2 possono contenere redex Esempio Si può definire una lista in modo ricorsivo: val infiniti2 = 2 :: infiniti2; infiniti2 corrisponde ad una lista, potenzialmente infinita, i cui elementi sono tutti 2 valutazione eager: la valutazione divergerebbe valutazione lazy: valore perfettamente manipolabile hd infiniti2 la valutazione termina con 2 hd (tl (tl (tl infiniti2))) idem LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 49 / 56 Oggetti Infiniti [. . . cont.] Esempio Si definisce la funzione che costruisce una lista infinita di numeri naturali a partire dal parametro n: fun numeriDa n = n :: numeriDa(n+1); Si possono definire funzioni di ordine superiore che applicano il proprio argomento funzionale a tutti gli elementi di una lista: 1 2 fun map f nil = nil | map f e::resto = f(e)::map(f,resto); Si può produrre ora la lista infinita dei quadrati a partire da n*n: fun quadratiDa n = map (fn y => y*y) (numeriDa n); 1 si usa la sintassi di ML ma considerando la valutazione lazy LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 50 / 56 Aspetti Imperativi Molti ling. funzionali (es. ML) includono meccanismi che necessitano della nozione di stato Sono date variabili modificabili (reference cell), attraverso effetti collaterali, con propri tipi per ogni tipo T (inclusi quelli funzionali) si ha il tipo T ref, i cui valori sono variabili modificabili con valori di tipo T ref v crea una var. inizializzata con il valore v (di tipo T) LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 51 / 56 Aspetti Imperativi [. . . cont.] i costrutti standard valgono anche per il tipo T ref es. val I = ref 4; crea la variabile I di tipo int ref, inizializzata a 4 I è il nome della variabile non del valore contenuto (l-value) dereferenziando esplicitamente con l’op. ! si ottiene l’r-value: val n = !I + 1; con n (nome, non var.) associato al valore 5 e !I di tipo int In generale, se V è un’espressione di tipo T ref, !V è di tipo T assegnazione: modifica delle variabili I := !I + 1; notare la differenza con la def. del valore n di cui sopra si sta modificando l’r-value di una variabile che già esiste il tipo di un costrutto imperativo è unit LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 52 / 56 Aspetti Imperativi [. . . cont.] la presenza di var. modificabili rende possibile l’aliasing: 1 2 val I = ref 4; val J = I; la 2 associa il valore del nome I (cioè la var.) al nome J I e J sono nomi diversi (alias) per lo stesso l-value Dopo l’esecuzione di 3 4 I := 5; val z = !J; il nome z è associato al valore 5 LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 53 / 56 Aspetti Imperativi [. . . cont.] La definizione di ML non specifica come implementare l’assegnazione tra variabili implementazione tradizionale: r-value copiato dalla sorgente alla destinazione ma le var. possono avere fabbisogni di memoria molto diversi Es. variabili con liste e stringhe 1 2 val S = ref "corta"; val L = ref ["uno", "due", "tre"]; Il nome S è di tipo string ref, mentre L è di tipo (string list) ref le due var. associate ai nomi S e L possono contenere, risp., qualunque stringa e qualunque lista, ad es.: 1 2 S := "stringa molto piu’ lunga delle precedenti"; L := "zero" :: "quattro" :: "cinque" :: !L; i nuovi valori di L e S richiedono più memoria dei precedenti LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 54 / 56 Aspetti Imperativi [. . . cont.] È possibile implementare queste assegnazioni usando una semplice (e standard) copia di valori la macchina astratta copia i riferimenti (puntatori) a tali valori tutto trasparente all’utente l’implementazione gestisce i due fabbisogni di spazio, allocando la mem. necessaria per ognuno dei casi e modificando i riferimenti ML fornisce anche costrutti di controllo imperativo come la sequenza (;) e i cicli Osservazione l’uso di caratteristiche imperative lede il teorema precedente: risultati non corretti LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 55 / 56 Riferimenti Gabbrielli & Martini: Linguaggi di Programmazione, McGraw-Hill 2a edizione. Cap. 13 LdP.ITPS/[email protected] Paradigma Funzionale 25 mag, 2016 56 / 56