Paradigma
Funzionale
Nicola Fanizzi
Corso di Linguaggi di Programmazione - 010194
Dipartimento di Informatica
Università degli Studi di Bari
27 maggio 2015
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
N. Fanizzi ( LdP)
3
Programmazione nei
Linguaggi Funzionali
Ambiente Locale
Interattività
Tipi
Pattern Matching
Oggetti Infiniti
Aspetti Imperativi
Paradigma Funzionale
27 maggio 2015
2 / 56
Agenda
1
Computazione Senza Stato
Espressioni e Funzioni
Computazione come Riduzione
Ingredienti Fondamentali
2
Valutazione
3
Programmazione nei Linguaggi Funzionali
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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,
...
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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 four = f 2;
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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)
Se g è il nome di una funzione
g a1 a2 ... ak sta per (...((g a1) a2) ... ak)
Esempio
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
9 / 56
Espressioni e Funzioni [. . . cont.]
Un’espressione funzionale può occorrere all’interno di un’altra
Esempio val add = fn x => (fn y => y+x);
il valore di add è una funzione che, dato l’argomento, x, restituisce
una funzione anonima che, dato un argomento y, restituisce x+y.
Si può usare add in tante maniere diverse:
val three = add 1 2;
val addtwo = add 2;
val five = addtwo 3;
NB addtwo funzione definita come risultato della valutazione di
un’altra espressione
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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);
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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)
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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 n*fact(n-1))))
3*(2*(1*1))
6
NB: manipolazione di stringhe: no variabili, no stack
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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−→
(fn
(fn
(fn
K a
(fn
(fn
a
N. Fanizzi ( LdP)
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
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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)
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
20 / 56
Agenda
1
Computazione Senza Stato
2
Valutazione
Valori
Sostituzione senza cattura
Strategie di valutazione
3
Programmazione nei Linguaggi Funzionali
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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)
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
(fun y => 1)(r 2) espr. complessiva, argomento da valutare
(r 2) → r (r 2) → r (r (r 2)) → · · ·
divergente!
In conclusione, v indefinito
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
28 / 56
Valutazione per nome
Strategia dell’ordine normale o esterna:
una redex viene valutata prima della sua parte argomento
algoritmo
1
sia (f_exp a_exp) la prima applicazione che occorre,
a partire da sinistra, nell’espressione
2
si valuta f_exp
(applicando questo algoritmo ricorsivamente)
fino a ridurla ad un valore funzionale (fn x => ...)
3
si riduce ((fn x => ..) a_exp) usando la β-regola e
si torna a (1)
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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 – al passo (3) – la redex più esterna:
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
32 / 56
Confronto delle strategie
Teorema (strategie di valutazione)
Sia exp un’espressione chiusa∗ . Se la valutazione di exp converge 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.
Osservazione
non si contempla il caso che da exp si producano valori primitivi
differenti a seconda della strategia
(*) Un’espressione è chiusa sse tutte le sue variabili sono legate in qualche costrutto fn
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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)
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
34 / 56
Agenda
1
Computazione Senza Stato
2
Valutazione
3
Programmazione nei Linguaggi Funzionali
Ambiente Locale
Interattività
Tipi
Pattern Matching
Oggetti Infiniti
Aspetti Imperativi
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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 add_p che prende in input
una coppia di interi e ne restituisce la somma:
fun add_p (n1,n2) = n1+n2;
NB: non avrebbe senso fornire a add_p un solo argomento
come nel currying
tipi di funzioni, funzioni come valori
valori denotabili ed esprimibili
(anche memorizzabili, se sono inclusi anche aspetti imperativi)
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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!
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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)
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
43 / 56
Pattern Matching [. . . cont.]
Meccanismo flessibile con tipi strutturati
Esempio liste, in ML:
si usano valori separati da virgole tra parentesi quadre
["one", "two", "three"] lista di 3 stringhe
:: denota l’operatore cons (aggiunta di un elemento in testa)
"zero"::["one", "two", "three"]
espressione che ha per valore la lista
["zero", "one", "two", "three"]
nil denota la lista vuota
"four"::nil ha valore ["four"]
Funzione che calcola la lunghezza di una lista
1
2
fun length nil = 0
| length e::rest = 1 + length(rest);
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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 length list = if list = nil then 0
else 1 + length(tl list);
che richiede una funzione di selezione (tl) della lista priva
della testa (coda o tail).
La selezione è implicita nel pattern matching
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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::rest = true
x::y::rest = 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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
47 / 56
Oggetti Infiniti [. . . cont.]
Esempio valutazione lazy
Selezione di testa e coda di una lista non-vuota
1
2
fun hd x::rest = x;
fun tl x::rest = rest;
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)
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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 infinity2 = 2 :: infinity2;
infinity2 corrisponde ad una lista, potenzialmente infinita,
i cui elementi sono tutti 2
valutazione eager: la valutazione divergerebbe
valutazione lazy: valore perfettamente manipolabile
hd infinity2
la valutazione termina con 2
hd (tl (tl (tl infinity2)))
idem
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
49 / 56
Oggetti Infiniti [. . . cont.]
Esempio Si definisce la funzione che costruisce una lista
infinita di numeri naturali a partire dal parametro n:
fun numbersFrom n = n :: numbersFrom(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::rest = f(e)::rest;
Si può produrre ora la lista infinita dei quadrati a partire da n*n:
fun squaresFrom n = map (fn y => y*y) (numbersFrom n);
1
si usa la sintassi di ML ma considerando la valutazione lazy
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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)
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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 "pear";
val L = ref ["one", "two", "three"];
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 della precedente";
L := "zero" :: "four" :: "five" :: !L;
i nuovi valori di L e S richiedono più memoria dei precedenti
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
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
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
55 / 56
Riferimenti
Gabbrielli & Martini: Linguaggi di Programmazione,
McGraw-Hill 2a edizione
N. Fanizzi ( LdP)
Paradigma Funzionale
27 maggio 2015
56 / 56
Scarica

Paradigma Funzionale - LACAM - Università degli Studi di Bari