Alice Troise - Lambda calcolo e tipi polimorfi - SELP
Alice Troise - Lambda calcolo e tipi polimorfi - SELP
Alice Troise - Lambda calcolo e tipi polimorfi - SELP
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
Polimorfismo parametrico<br />
e type reconstruction<br />
<strong>Alice</strong> <strong>Troise</strong><br />
Università degli Studi di Firenze<br />
Polimorfismo parametrico – p. 1
Polimorfismo<br />
Polimorfismo parametrico – p. 2
Polimorfismo<br />
La necessità di avere <strong>polimorfi</strong>smo nei linguaggi nasce da due<br />
importanti obiettivi, in parte contrastanti:<br />
Polimorfismo parametrico – p. 2
Polimorfismo<br />
La necessità di avere <strong>polimorfi</strong>smo nei linguaggi nasce da due<br />
importanti obiettivi, in parte contrastanti:<br />
riusabilità del codice<br />
Polimorfismo parametrico – p. 2
Polimorfismo<br />
La necessità di avere <strong>polimorfi</strong>smo nei linguaggi nasce da due<br />
importanti obiettivi, in parte contrastanti:<br />
riusabilità del codice<br />
e<br />
typing statico (essendo le variabili legate ai <strong>tipi</strong> prima della fase<br />
runtime, siamo sicuri che quando il programma sarà in esecuzione non<br />
avverranno errori di tipo)<br />
Polimorfismo parametrico – p. 2
Polimorfismo<br />
La necessità di avere <strong>polimorfi</strong>smo nei linguaggi nasce da due<br />
importanti obiettivi, in parte contrastanti:<br />
riusabilità del codice<br />
e<br />
typing statico (essendo le variabili legate ai <strong>tipi</strong> prima della fase<br />
runtime, siamo sicuri che quando il programma sarà in esecuzione non<br />
avverranno errori di tipo)<br />
Nel presente approfondimento viene descritto il <strong>polimorfi</strong>smo<br />
parametrico implicito (parametri di tipo type e applicazioni di<br />
tipo non sono ammesse) applicato al linguaggio del λ-<strong>calcolo</strong>.<br />
Polimorfismo parametrico – p. 2
Polimorfismo<br />
La necessità di avere <strong>polimorfi</strong>smo nei linguaggi nasce da due<br />
importanti obiettivi, in parte contrastanti:<br />
riusabilità del codice<br />
e<br />
typing statico (essendo le variabili legate ai <strong>tipi</strong> prima della fase<br />
runtime, siamo sicuri che quando il programma sarà in esecuzione non<br />
avverranno errori di tipo)<br />
Nel presente approfondimento viene descritto il <strong>polimorfi</strong>smo<br />
parametrico implicito (parametri di tipo type e applicazioni di<br />
tipo non sono ammesse) applicato al linguaggio del λ-<strong>calcolo</strong>.<br />
Iniziamo col ricordare sintassi, semantica e typing del λ-<strong>calcolo</strong>.<br />
Polimorfismo parametrico – p. 2
Sintassi del λ-<strong>calcolo</strong><br />
t ::= termini :<br />
x variabile<br />
λx : T.t astrazione (a la Church)<br />
tt applicazione<br />
v ::= valori :<br />
λx : T.t astrazione<br />
T ::= <strong>tipi</strong> :<br />
T → T freccia<br />
Γ ::= contesti :<br />
∅ contesto vuoto<br />
Γ, x : T binding di variabile<br />
Polimorfismo parametrico – p. 3
Estensione alla sintassi del λ-<strong>calcolo</strong><br />
t ::= termini :<br />
true, false, 0 booleani e zero<br />
if t then t else t costrutto if<br />
succ t successore<br />
pred t predecessore<br />
iszero t test sullo zero<br />
v ::= valori :<br />
true, false booleani<br />
nv (0 o succ nv) valori numerici<br />
T ::= <strong>tipi</strong> :<br />
Nat, Bool naturali e booleani<br />
Polimorfismo parametrico – p. 4
Semantica del λ-<strong>calcolo</strong><br />
t1 → t ′ 1<br />
t1t2 → t ′ 1 t2<br />
t2 → t ′ 2<br />
v1t2 → v1t ′ 2<br />
(E-APP1)<br />
(E-APP2)<br />
(λx: T.t)v → [x ↦→ v]t (E-APPABS)<br />
Polimorfismo parametrico – p. 5
Estensione alla semantica del λ-<strong>calcolo</strong><br />
t1 → t ′ 1<br />
iszero t1 → iszero t ′ 1<br />
(E-ISZERO)<br />
iszero 0 → true (E-ISZEROZ)<br />
iszero(succ nv1) → false (E-ISZEROS)<br />
t1 → t ′ 1<br />
if t ′ 1 then t2 else t3<br />
(E-IF)<br />
if true then t2 else t3 → t2 (E-IFTRUE)<br />
if false then t2 else t3 → t3 (E-IFFALSE)<br />
Polimorfismo parametrico – p. 6
Estensione alla semantica del λ-<strong>calcolo</strong><br />
t1 → t ′ 1<br />
pred t1 → pred t ′ 1<br />
t1 → t ′ 1<br />
succ t1 → succ t ′ 1<br />
(E-PRED)<br />
(E-SUCC)<br />
pred(succ nv1) → nv1 (E-PREDSUCC)<br />
pred 0 → 0 (E-PREDZERO)<br />
Polimorfismo parametrico – p. 7
Typing del λ-<strong>calcolo</strong><br />
x : T ∈ Γ<br />
Γ ⊢ x : T<br />
Γ,x : T1 ⊢ t2 : T2<br />
Γ ⊢ λx : T1.t2 : T1 → T2<br />
Γ ⊢ t1 : T11 → T12 Γ ⊢ t2 : T11<br />
Γ ⊢ t1t2 : T12<br />
(T-VAR)<br />
(T-ABS)<br />
(T-APP)<br />
Polimorfismo parametrico – p. 8
Estensione al typing del λ-<strong>calcolo</strong><br />
Γ ⊢ true : Bool (T-TRUE) Γ ⊢ false : Bool (T-FALSE)<br />
Γ ⊢ 0 : Nat (T-ZERO)<br />
Γ ⊢ t1 : Nat<br />
Γ ⊢ succt1 : Nat<br />
(T-SUCC)<br />
Γ ⊢ t1 : Nat<br />
Γ ⊢ iszero t1 : Bool<br />
Γ ⊢ t1 : Nat<br />
Γ ⊢ predt1 : Nat<br />
Γ ⊢ t1 : Bool Γ ⊢ t2 : T Γ ⊢ t3 : T<br />
Γ ⊢ if t1 then t2 else t3 : T<br />
(T-IF)<br />
(T-ISZERO)<br />
(T-PRED)<br />
Polimorfismo parametrico – p. 9
Variabili di tipo e sostituzioni<br />
Aggiungiamo al linguaggio del λ-<strong>calcolo</strong>, esteso coi valori<br />
numerici e qualche funzione, un insieme infinito A di <strong>tipi</strong> base<br />
non interpretati senza operazioni primitive associate. Questi <strong>tipi</strong><br />
sconosciuti prendono il nome di variabili di tipo e vengono<br />
indicati con X, Y, Z, ....<br />
Polimorfismo parametrico – p. 10
Variabili di tipo e sostituzioni<br />
Aggiungiamo al linguaggio del λ-<strong>calcolo</strong>, esteso coi valori<br />
numerici e qualche funzione, un insieme infinito A di <strong>tipi</strong> base<br />
non interpretati senza operazioni primitive associate. Questi <strong>tipi</strong><br />
sconosciuti prendono il nome di variabili di tipo e vengono<br />
indicati con X, Y, Z, ....<br />
Il processo che assegna <strong>tipi</strong> concreti a variabili di tipo è chiamato<br />
sostituzione oppure istanziazione.<br />
Polimorfismo parametrico – p. 10
Le due fasi della sostituzione<br />
È conveniente separare le operazioni di sostituzione in due parti:<br />
Polimorfismo parametrico – p. 11
Le due fasi della sostituzione<br />
È conveniente separare le operazioni di sostituzione in due parti:<br />
1. descrizione del mapping σ dalle variabili di tipo nei <strong>tipi</strong>,<br />
chiamato sostituzione<br />
Polimorfismo parametrico – p. 11
Le due fasi della sostituzione<br />
È conveniente separare le operazioni di sostituzione in due parti:<br />
1. descrizione del mapping σ dalle variabili di tipo nei <strong>tipi</strong>,<br />
chiamato sostituzione<br />
2. applicazione del mapping ad un particolare tipo T per<br />
ottenere una istanza σT .<br />
Polimorfismo parametrico – p. 11
Le due fasi della sostituzione<br />
È conveniente separare le operazioni di sostituzione in due parti:<br />
1. descrizione del mapping σ dalle variabili di tipo nei <strong>tipi</strong>,<br />
chiamato sostituzione<br />
2. applicazione del mapping ad un particolare tipo T per<br />
ottenere una istanza σT .<br />
֒→ Esempio: possiamo definire [X ↦→ Bool,Y ↦→ X → Int].<br />
Se applichiamo σ al tipo X ↦→ X otteniamo Bool → Bool.<br />
Se applichiamo σ al tipo Y otteniamo X → Int.<br />
Polimorfismo parametrico – p. 11
Sostituzione di tipo - definizione<br />
Def. Sostituzione di tipo: mapping finito σ da variabili di tipo<br />
in <strong>tipi</strong>.<br />
Polimorfismo parametrico – p. 12
Sostituzione di tipo - definizione<br />
Def. Sostituzione di tipo: mapping finito σ da variabili di tipo<br />
in <strong>tipi</strong>.<br />
Scriviamo dom(σ) per indicare l’insieme delle variabili di tipo<br />
che appaiono a sinistra delle coppie di σ; range(σ) per l’insieme<br />
dei <strong>tipi</strong> che appaiono a destra.<br />
Polimorfismo parametrico – p. 12
Sostituzione di tipo - definizione<br />
L’applicazione di una sostituzione ad un tipo è definita nel<br />
seguente modo:<br />
σ(X) =<br />
⎧<br />
⎨<br />
⎩<br />
σ(Nat) = Nat<br />
σ(Bool) = Bool<br />
σ(T1 → T2) = σT1 → σT2<br />
T se (X ↦→ T) ∈ σ<br />
X se X /∈ dom(σ)<br />
Polimorfismo parametrico – p. 13
Sostituzione di tipo - definizione<br />
La sostituzione di tipo si estende banalmente ad un contesto di<br />
assunzioni di tipo Γ definendo:<br />
σ(x1 : T1,...,xn : Tn) = (x1 : σT1,...,xn : σTn)<br />
Polimorfismo parametrico – p. 14
Sostituzione di tipo - definizione<br />
La sostituzione di tipo si estende banalmente ad un contesto di<br />
assunzioni di tipo Γ definendo:<br />
σ(x1 : T1,...,xn : Tn) = (x1 : σT1,...,xn : σTn)<br />
Se σ e γ sono sostituzioni, scriviamo σ ◦ γ per identificare la<br />
sostituzione formata dalla composizione di esse:<br />
⎡<br />
X ↦→ σ(T) per ogni (X ↦→ T) ∈ γ<br />
σ◦γ = ⎣<br />
X ↦→ T per ogni (X ↦→ T) ∈ σ con X /∈ dom(γ)<br />
Si noti che (σ ◦ γ)S = σ(γS).<br />
⎤<br />
⎦<br />
Polimorfismo parametrico – p. 14
Preservation del typing<br />
Teorema: Il buon tipaggio si preserva sotto sostituzione di tipo,<br />
ovvero ∀σ Γ ⊢ t : T ⇒ σΓ ⊢ σt : σT.<br />
Dimostrazione: Per induzione sulla derivazione del tipo<br />
Polimorfismo parametrico – p. 15
Istanze di sostituzione valide<br />
Sia t un termine contenente variabili di tipo, Γ un contesto<br />
associato; ci chiediamo se può essere istanziato a un termine ben<br />
tipato scegliendo appropriati valori per le sue variabili di tipo.<br />
Polimorfismo parametrico – p. 16
Istanze di sostituzione valide<br />
Sia t un termine contenente variabili di tipo, Γ un contesto<br />
associato; ci chiediamo se può essere istanziato a un termine ben<br />
tipato scegliendo appropriati valori per le sue variabili di tipo.<br />
֒→ Per esempio, sia Γ = {f : F, x : X} e sia t = fx. t non è tipabile così<br />
com’è, ma lo può essere con una qualsiasi delle seguenti sostituzioni:<br />
[F ↦→ Nat → Bool, X ↦→ Nat] ed in questo modo ha tipo Bool.<br />
[F ↦→ X → Nat] ed in questo modo t ha tipo Nat;<br />
[F ↦→ X → Y] ed in questo modo ha tipo Y;<br />
Polimorfismo parametrico – p. 16
Istanze di sostituzione valide<br />
Sia t un termine contenente variabili di tipo, Γ un contesto<br />
associato; ci chiediamo se può essere istanziato a un termine ben<br />
tipato scegliendo appropriati valori per le sue variabili di tipo.<br />
֒→ Per esempio, sia Γ = {f : F, x : X} e sia t = fx. t non è tipabile così<br />
com’è, ma lo può essere con una qualsiasi delle seguenti sostituzioni:<br />
[F ↦→ Nat → Bool, X ↦→ Nat] ed in questo modo ha tipo Bool.<br />
[F ↦→ X → Nat] ed in questo modo t ha tipo Nat;<br />
[F ↦→ X → Y] ed in questo modo ha tipo Y;<br />
La ricerca di istanze di sostituzione valide porta all’idea di type<br />
reconstruction (o type inference), nella quale il compilatore<br />
completa le informazioni di tipo non specificate dal<br />
programmatore.<br />
Polimorfismo parametrico – p. 16
Verso un typing vincolato<br />
Nelle prossime slides presenteremo un algoritmo che, dato un<br />
termine t ed un contesto Γ, trova le condizioni minime sulle loro<br />
variabili di tipo per rendere t ben tipato.<br />
Polimorfismo parametrico – p. 17
Verso un typing vincolato<br />
Nelle prossime slides presenteremo un algoritmo che, dato un<br />
termine t ed un contesto Γ, trova le condizioni minime sulle loro<br />
variabili di tipo per rendere t ben tipato.<br />
La differenza con l’ordinario algoritmo di typechecking è che,<br />
invece di controllare che siano soddisfatte certe condizioni, le<br />
registra per una considerazione successiva.<br />
Polimorfismo parametrico – p. 17
Vincoli - definizioni<br />
Un insieme di vincoli (constraint) C è un insieme di equazioni<br />
C = {S1 = T1, S2 = T2, ..., Sn = Tn}.<br />
Polimorfismo parametrico – p. 18
Vincoli - definizioni<br />
Un insieme di vincoli (constraint) C è un insieme di equazioni<br />
C = {S1 = T1, S2 = T2, ..., Sn = Tn}.<br />
Si dice che una sostituzione σ unifica un’equazione Si = Ti se<br />
σSi = σTi.<br />
Si dice che σ soddisfa C se σ unifica tutte le equazioni di C.<br />
Polimorfismo parametrico – p. 18
Typing basato sui vincoli<br />
La relazione Γ ⊢ t : T|χC è letta “il termine t ha tipo T nel<br />
contesto delle assunzioni Γ ogni qual volta i vincoli C sono<br />
soddisfatti”.<br />
Polimorfismo parametrico – p. 19
Typing basato sui vincoli<br />
La relazione Γ ⊢ t : T|χC è letta “il termine t ha tipo T nel<br />
contesto delle assunzioni Γ ogni qual volta i vincoli C sono<br />
soddisfatti”.<br />
Il simbolo χ è usato per tenere traccia delle variabili di tipo<br />
introdotte; tali annotazioni assicurano che:<br />
1. ogni qual volta una variabile di tipo è scelta dalla regola<br />
finale di una certa derivazione, è sicuro che essa è diversa<br />
da ogni altra variabile scelta fra le sotto-derivazioni;<br />
2. ogni volta che una regola coinvolge due o più<br />
sotto-derivazioni, gli insiemi delle variabili scelte da tali<br />
sotto-derivazioni sono disgiunti.<br />
Polimorfismo parametrico – p. 19
Regole typing con vincoli 1<br />
x : T ∈ Γ<br />
Γ ⊢ x : T|∅{}<br />
Γ, x : T1 ⊢ t2 : T2|χC<br />
Γ ⊢ λx : T1.t2 : T1 → T2|χC<br />
Γ ⊢ t1 : T1|χ1C1 Γ ⊢ t2 : T2|χ2C2<br />
χ1 ∩ χ2 = χ1 ∩ FV (T2) = χ2 ∩ FV (T1) = ∅<br />
X /∈ χ1,χ2, T1, T2, C1, C2, Γ, t1, t2<br />
Γ ⊢ t1t2 : X|χ1∪χ2∪{X}C1 ∪ C2 ∪ {T1 = T2 → X}<br />
dove indichiamo con FV(T) l’insieme di tutte le variabili di tipo<br />
menzionate in T.<br />
(CT-VAR)<br />
(CT-ABS)<br />
(CT-APP)<br />
Polimorfismo parametrico – p. 20
Regole typing con vincoli 2<br />
Γ ⊢ true : Bool|∅{} (CT-TRUE)<br />
Γ ⊢ false : Bool|∅{} (CT-FALSE)<br />
Γ ⊢ t1 : T1|χ1C1 Γ ⊢ t2 : T2|χ2C2 Γ ⊢ t3 : T3|χ3C3<br />
χ1,χ2,χ3 nonoverlapping<br />
Γ ⊢ if t1 then t2 else t3 : T2|χ1∪χ2∪χ3 C ′<br />
dove C ′ = C1 ∪ C2 ∪ C3 ∪ {T1 = Bool, T2 = T3}<br />
(CT-IF)<br />
Polimorfismo parametrico – p. 21
Regole typing con vincoli 3<br />
Γ ⊢ 0 : Nat|∅{} (CT-ZERO)<br />
Γ ⊢ t1 : T|χC<br />
Γ ⊢ predt1 : Nat|χC ∪ {T = Nat}<br />
Γ ⊢ t1 : T|χC<br />
Γ ⊢ succt1 : Nat|χC ∪ {T = Nat}<br />
Γ ⊢ t1 : T|χC<br />
Γ ⊢ iszero t1 : Bool|χC ∪ T = Nat<br />
(CT-PRED)<br />
(CT-SUCC)<br />
(CT-ISZERO)<br />
Polimorfismo parametrico – p. 22
Osservazioni<br />
• Il sistema di typing con vincoli è guidato dalla sintassi del<br />
linguaggio.<br />
Polimorfismo parametrico – p. 23
Osservazioni<br />
• Il sistema di typing con vincoli è guidato dalla sintassi del<br />
linguaggio.<br />
• Le regole definiscono una procedura deterministica,<br />
ovvero un algoritmo che dato un termine t ed un contesto Γ<br />
ricava il tipo T e l’insieme C tali che Γ ⊢ t : T|χC.<br />
Polimorfismo parametrico – p. 23
Osservazioni<br />
• Il sistema di typing con vincoli è guidato dalla sintassi del<br />
linguaggio.<br />
• Le regole definiscono una procedura deterministica,<br />
ovvero un algoritmo che dato un termine t ed un contesto Γ<br />
ricava il tipo T e l’insieme C tali che Γ ⊢ t : T|χC.<br />
• Questo algoritmo non fallisce mai: per ogni Γ e per ogni t<br />
vi è sempre un tipo T ed un contesto C tali che Γ ⊢ t : T|χC.<br />
Questo in quanto i vincoli non vengono verificati<br />
all’interno delle regole, ma vengono solo memorizzati in C<br />
per un’analisi successiva.<br />
Polimorfismo parametrico – p. 23
Esempio di derivazione di tipo<br />
֒→ Derivazione di tipo per il termine λx : X. λy : Y.xy<br />
x : X ∈ Γ<br />
y : Y ∈ Γ<br />
{x : X, y : Y} ⊢ x : X| ∅{} {x : X, y : Y} ⊢ y : Y| ∅{}<br />
{x : X, y : Y} ⊢ (xy) : W| {W}{X = Y → W}<br />
{x : X} ⊢ λy : Y.(xy) : Y → W| {W}{X = Y → W}<br />
{} ⊢ λx : X. λy : Y.(xy) : X → Y → W| {W}{X = Y → W}<br />
Polimorfismo parametrico – p. 24
Esempio di derivazione di tipo<br />
֒→ Derivazione di tipo per il termine λx : X. λy : Y.xy<br />
x : X ∈ Γ<br />
y : Y ∈ Γ<br />
{x : X, y : Y} ⊢ x : X| ∅{} {x : X, y : Y} ⊢ y : Y| ∅{}<br />
{x : X, y : Y} ⊢ (xy) : W| {W}{X = Y → W}<br />
{x : X} ⊢ λy : Y.(xy) : Y → W| {W}{X = Y → W}<br />
{} ⊢ λx : X. λy : Y.(xy) : X → Y → W| {W}{X = Y → W}<br />
ricordando la regola CT-ABS:<br />
Γ, x : T1 ⊢ t2 : T2|χC<br />
Γ ⊢ λx : T1.t2 : T1 → T2|χC<br />
Polimorfismo parametrico – p. 24
Esempio di derivazione di tipo<br />
֒→ Derivazione di tipo per il termine λx : X. λy : Y.xy<br />
x : X ∈ Γ<br />
y : Y ∈ Γ<br />
{x : X, y : Y} ⊢ x : X| ∅{} {x : X, y : Y} ⊢ y : Y| ∅{}<br />
{x : X, y : Y} ⊢ (xy) : W| {W}{X = Y → W}<br />
{x : X} ⊢ λy : Y.(xy) : Y → W| {W}{X = Y → W}<br />
{} ⊢ λx : X. λy : Y.(xy) : X → Y → W| {W}{X = Y → W}<br />
ricordando la regola CT-ABS:<br />
Γ, x : T1 ⊢ t2 : T2|χC<br />
Γ ⊢ λx : T1.t2 : T1 → T2|χC<br />
ricordando la regola CT-APP:<br />
Γ ⊢ t1 : T1|χ1 C1 Γ ⊢ t2 : T2|χ2 C2<br />
χ1 ∩ χ2 = χ1 ∩ FV (T2) = χ2 ∩ FV (T1) = ∅ X fresh<br />
Γ ⊢ t1t2 : X| χ1∪χ2∪{X}C1 ∪ C2 ∪ {T1 = T2 → X}<br />
Polimorfismo parametrico – p. 24
Osservazioni pratiche<br />
Normalmente la costruzione della derivazione coi vincoli non<br />
avviene con un solo passaggio; ciò significa che inizialmente<br />
verranno identificati i <strong>tipi</strong> delle variabili senza supporre alcunchè<br />
sui vincoli (raggiungeremo cioè la cima della derivazione in<br />
modo bottom up). Successivamente, a partire dalla parte alta<br />
della derivazione (ovvero dalla definizione degli elementi<br />
inizialmente appartenenti al contesto), verranno costruiti<br />
l’insieme dei vincoli ed il tipo S mediante l’applicazione delle<br />
regole semantiche.<br />
Polimorfismo parametrico – p. 25
Soluzioni<br />
Soluzione per (Γ, t)<br />
Sia Γ un contesto di assunzioni di tipo e t un termine. Una<br />
soluzione per (Γ, t) è una coppia (σ, T) tale che σΓ ⊢ σt : T.<br />
Soluzione per (Γ, t, S, C)<br />
Sia Γ ⊢ t : S|C. Una soluzione per (Γ, t, S, C) è una coppia<br />
(σ, T) tale che σ soddisfa C e σS = T.<br />
Polimorfismo parametrico – p. 26
Soluzioni<br />
Soluzione per (Γ, t)<br />
Sia Γ un contesto di assunzioni di tipo e t un termine. Una<br />
soluzione per (Γ, t) è una coppia (σ, T) tale che σΓ ⊢ σt : T.<br />
Soluzione per (Γ, t, S, C)<br />
Sia Γ ⊢ t : S|C. Una soluzione per (Γ, t, S, C) è una coppia<br />
(σ, T) tale che σ soddisfa C e σS = T.<br />
Correttezza: Sia Γ ⊢ t : S|C. Se (σ, T) è una soluzione per<br />
(Γ, t, S, C), allora è anche una soluzione per (Γ, t).<br />
Completezza: Sia Γ ⊢ t : S|C. Se (σ, T) è una soluzione per<br />
(Γ, t), allora è anche una soluzione per (Γ, t, S, C).<br />
Dimostrazione: per induzione (si veda Pierce [1])<br />
Polimorfismo parametrico – p. 26
Completezza del typing vincolato<br />
In realtà la formulazione del teorema di completezza è<br />
leggermente diversa.<br />
Def.: σ\χ è una sostituzione indefinita per tutte le variabili in χ<br />
e con le altre si comporta come σ.<br />
Teorema di completezza: Sia Γ ⊢ t : S|C. Se (σ, T) è una<br />
soluzione per (Γ, t) e dom(σ) ∩ χ = ∅ allora esiste una<br />
soluzione (σ ′ , T) per (Γ, t, S, C) tale che σ ′ \χ = σ.<br />
Polimorfismo parametrico – p. 27
Unificazione<br />
Per calcolare le soluzioni di un insieme di vincoli usiamo<br />
l’algoritmo di unificazione di Robinson (1971).<br />
Polimorfismo parametrico – p. 28
Unificazione<br />
Per calcolare le soluzioni di un insieme di vincoli usiamo<br />
l’algoritmo di unificazione di Robinson (1971).<br />
Def. Sostituzione meno specifica: Una sostituzione σ è meno<br />
specifica (o più generale) di una sostituzione σ ′ , scritto σ ⊑ σ ′ ,<br />
se σ ′ = γ ◦ σ per qualche sostituzione γ.<br />
Polimorfismo parametrico – p. 28
Unificazione<br />
Per calcolare le soluzioni di un insieme di vincoli usiamo<br />
l’algoritmo di unificazione di Robinson (1971).<br />
Def. Sostituzione meno specifica: Una sostituzione σ è meno<br />
specifica (o più generale) di una sostituzione σ ′ , scritto σ ⊑ σ ′ ,<br />
se σ ′ = γ ◦ σ per qualche sostituzione γ.<br />
Def. Unificatore principale: Un unificatore principale per un<br />
insieme di vincoli C è una sostituzione σ che soddisfa C e tale<br />
che σ ⊑ σ ′ per ogni sostituzione σ ′ che soddisfa C.<br />
Polimorfismo parametrico – p. 28
Unificazione<br />
Per calcolare le soluzioni di un insieme di vincoli usiamo<br />
l’algoritmo di unificazione di Robinson (1971).<br />
Def. Sostituzione meno specifica: Una sostituzione σ è meno<br />
specifica (o più generale) di una sostituzione σ ′ , scritto σ ⊑ σ ′ ,<br />
se σ ′ = γ ◦ σ per qualche sostituzione γ.<br />
Def. Unificatore principale: Un unificatore principale per un<br />
insieme di vincoli C è una sostituzione σ che soddisfa C e tale<br />
che σ ⊑ σ ′ per ogni sostituzione σ ′ che soddisfa C.<br />
Def. Algoritmo di unificazione: Vedi pseudo-codice nella<br />
prossima slide.<br />
Polimorfismo parametrico – p. 28
Algoritmo di unificazione<br />
unify(C) =<br />
- if C = ∅ then [ ]<br />
- else let {S = T} ∪ C ′ = C in<br />
Polimorfismo parametrico – p. 29
Algoritmo di unificazione<br />
unify(C) =<br />
- if C = ∅ then [ ]<br />
- else let {S = T} ∪ C ′ = C in<br />
- if S = T<br />
- then unify(C ′ )<br />
- else if S = X and X /∈ FV(T)<br />
- then unify([X ↦→ T]C ′ ) ◦ [X ↦→ T]<br />
- else if T = X and X /∈ FV(S)<br />
- then unify([X ↦→ S]C ′ ) ◦ [X ↦→ S]<br />
Polimorfismo parametrico – p. 29
Algoritmo di unificazione<br />
unify(C) =<br />
- if C = ∅ then [ ]<br />
- else let {S = T} ∪ C ′ = C in<br />
- if S = T<br />
- then unify(C ′ )<br />
- else if S = X and X /∈ FV(T)<br />
- then unify([X ↦→ T]C ′ ) ◦ [X ↦→ T]<br />
- else if T = X and X /∈ FV(S)<br />
- then unify([X ↦→ S]C ′ ) ◦ [X ↦→ S]<br />
- else if S = S1 → S2 and T = T1 → T2<br />
- then unify(C ′ ∪ {S1 = T1, S2 = T2})<br />
Polimorfismo parametrico – p. 29
Algoritmo di unificazione<br />
unify(C) =<br />
- if C = ∅ then [ ]<br />
- else let {S = T} ∪ C ′ = C in<br />
- if S = T<br />
- then unify(C ′ )<br />
- else if S = X and X /∈ FV(T)<br />
- then unify([X ↦→ T]C ′ ) ◦ [X ↦→ T]<br />
- else if T = X and X /∈ FV(S)<br />
- then unify([X ↦→ S]C ′ ) ◦ [X ↦→ S]<br />
- else if S = S1 → S2 and T = T1 → T2<br />
- then unify(C ′ ∪ {S1 = T1, S2 = T2})<br />
- else<br />
- fail<br />
Polimorfismo parametrico – p. 29
Osservazioni su unify<br />
• La frase “let {S = T}∪C ′ = C” alla seconda linea deve<br />
essere intesa come “si scelga un vincolo S = T dall’insieme<br />
C e sia C ′ l’insieme che denota i rimanenti vincoli di C.<br />
Polimorfismo parametrico – p. 30
Osservazioni su unify<br />
• La frase “let {S = T}∪C ′ = C” alla seconda linea deve<br />
essere intesa come “si scelga un vincolo S = T dall’insieme<br />
C e sia C ′ l’insieme che denota i rimanenti vincoli di C.<br />
• Le condizioni di lato X /∈ FV(T) e X /∈ FV(S) sono note<br />
come occur check. Servono per impedire all’algoritmo di<br />
generare una soluzione che contenga una sostituzione<br />
ciclica come ad esempio X ↦→ X → X.<br />
Polimorfismo parametrico – p. 30
Osservazioni su unify<br />
• La frase “let {S = T}∪C ′ = C” alla seconda linea deve<br />
essere intesa come “si scelga un vincolo S = T dall’insieme<br />
C e sia C ′ l’insieme che denota i rimanenti vincoli di C.<br />
• Le condizioni di lato X /∈ FV(T) e X /∈ FV(S) sono note<br />
come occur check. Servono per impedire all’algoritmo di<br />
generare una soluzione che contenga una sostituzione<br />
ciclica come ad esempio X ↦→ X → X.<br />
• Nel nostro caso il fallimento dell’algoritmo può avvenire o<br />
per occur check o perché tentiamo di unificare Nat = Bool.<br />
Polimorfismo parametrico – p. 30
Proprietá di unify<br />
Teorema (Milner):<br />
1. unify(C) termina per ogni C;<br />
2. se unify(C)=σ, allora σ è un unificatore per C;<br />
3. se δ è un unificatore per C, allora unify(C)=σ con σ ⊑ δ<br />
Dimostrazione: Vedi Pierce [1]<br />
Polimorfismo parametrico – p. 31
Soluzione principale<br />
Def. Soluzione e tipo principale: Una soluzione principale per<br />
(Γ, t, S, C) è una soluzione (σ, T) tale che, per ogni altra<br />
soluzione (σ ′ , T ′ ) si ha che σ ⊑ σ ′ . In tal caso T è detto il tipo<br />
principale di t.<br />
Teorema (Soluzione principale): Se (Γ, t, S, C) ha soluzione,<br />
allora ne ha una principale. L’algoritmo di unificazione può<br />
essere usato per determinare se (Γ, t, S, C) ha soluzione e, in<br />
caso positivo, per trovare la principale.<br />
Polimorfismo parametrico – p. 32
Isomorfismo di Curry-Howard<br />
È stato dimostrato che ogni derivazione nel frammento implicazionale<br />
della logica proposizionale intuizionistica corrisponde ad un termine<br />
à la Church (ovvero esplicitamente tipato) tipabile nel λ-<strong>calcolo</strong>.<br />
Polimorfismo parametrico – p. 33
Isomorfismo di Curry-Howard<br />
È stato dimostrato che ogni derivazione nel frammento implicazionale<br />
della logica proposizionale intuizionistica corrisponde ad un termine<br />
à la Church (ovvero esplicitamente tipato) tipabile nel λ-<strong>calcolo</strong>.<br />
Dato che la sintassi delle formule della logica intuizionistica è analoga<br />
a quella della logica del primo ordine, possiamo dedurre, data l’assenza<br />
dei quantificatori, che il sistema di <strong>tipi</strong> basato sui vincoli è un<br />
sottoinsieme dela logica del primo ordine: i <strong>tipi</strong> Nat e Bool sono<br />
costanti, le variabili di tipo sono variabili del <strong>calcolo</strong> e → è un simbolo<br />
funzionale binario.<br />
Polimorfismo parametrico – p. 33
Isomorfismo di Curry-Howard<br />
È stato dimostrato che ogni derivazione nel frammento implicazionale<br />
della logica proposizionale intuizionistica corrisponde ad un termine<br />
à la Church (ovvero esplicitamente tipato) tipabile nel λ-<strong>calcolo</strong>.<br />
Dato che la sintassi delle formule della logica intuizionistica è analoga<br />
a quella della logica del primo ordine, possiamo dedurre, data l’assenza<br />
dei quantificatori, che il sistema di <strong>tipi</strong> basato sui vincoli è un<br />
sottoinsieme dela logica del primo ordine: i <strong>tipi</strong> Nat e Bool sono<br />
costanti, le variabili di tipo sono variabili del <strong>calcolo</strong> e → è un simbolo<br />
funzionale binario.<br />
In questo contesto l’algoritmo di type reconstruction assume il ruolo di<br />
theorem prover.<br />
Polimorfismo parametrico – p. 33
Sistemi formali e typechecking<br />
Per approcciare la semantica formale dei <strong>tipi</strong> è possibile usare modelli<br />
matematici per i <strong>tipi</strong> (mappando ogni espressione di tipo in un insieme<br />
di valori) o definire un sistema formale di assiomi e regole di<br />
inferenza nel quale è possibile provare che un’espressione ha un<br />
qualche tipo. Questi approcci sono complementari: un modello<br />
semantico è spesso una guida nel definire un sistema formale; d’altra<br />
parte ogni sistema formale deve essere self-consistent e questo si<br />
dimostra spesso presentando un modello.<br />
Polimorfismo parametrico – p. 34
Sistemi formali e typechecking<br />
Per approcciare la semantica formale dei <strong>tipi</strong> è possibile usare modelli<br />
matematici per i <strong>tipi</strong> (mappando ogni espressione di tipo in un insieme<br />
di valori) o definire un sistema formale di assiomi e regole di<br />
inferenza nel quale è possibile provare che un’espressione ha un<br />
qualche tipo. Questi approcci sono complementari: un modello<br />
semantico è spesso una guida nel definire un sistema formale; d’altra<br />
parte ogni sistema formale deve essere self-consistent e questo si<br />
dimostra spesso presentando un modello.<br />
Poiché sintattico, il typechecking è collegato più ai sistemi formali che<br />
ai modelli. Un algoritmo di typechecking implementa un sistema<br />
formale, fornendo una procedura per dimostrare teoremi in quel<br />
sistema. Se ha successo nel trovare un tipo per una espressione, allora<br />
tale tipo può essere dedotto dal sistema di inferenza; inoltre è possibile<br />
dedurre l’algoritmo di typechecking a partire dal sistema di inferenza.<br />
Polimorfismo parametrico – p. 34
Un sistema di inferenza<br />
Esistono due distinti modi di vedere il typechecking: uno<br />
consiste nel risolvere un sistema di equazioni di tipo, l’altro<br />
consiste nel provare teoremi in un sistema formale.<br />
Cardelli [2] presenta un sistema di assiomi e regole d’inferenza,<br />
con le quali è possibile ottenere le medesime deduzioni (o<br />
derivazioni) costruite con le regole di typing con vincoli. Qui ne<br />
presentiamo una piccola parte.<br />
Polimorfismo parametrico – p. 35
Un sistema di inferenza<br />
Esistono due distinti modi di vedere il typechecking: uno<br />
consiste nel risolvere un sistema di equazioni di tipo, l’altro<br />
consiste nel provare teoremi in un sistema formale.<br />
Cardelli [2] presenta un sistema di assiomi e regole d’inferenza,<br />
con le quali è possibile ottenere le medesime deduzioni (o<br />
derivazioni) costruite con le regole di typing con vincoli. Qui ne<br />
presentiamo una piccola parte.<br />
Premessa: Indicheremo i <strong>tipi</strong> con lettere minuscole greche e gli<br />
insiemi di assunzioni xi : τi con lettere latine maiuscole.<br />
Un tipo è chiamato shallow (superficiale) se ha la forma<br />
∀α1, ..., ∀αn.τ dove n ≥ 0 senza quantificatori in τ.<br />
Siamo interessati solo ai <strong>tipi</strong> shallow.<br />
Polimorfismo parametrico – p. 35
Un sistema di inferenza<br />
A ⊢ e : τ<br />
A ⊢ e : ∀α.τ<br />
A.x : τ ⊢ x : τ [IDE]<br />
(α non libera in A) [GEN]<br />
A.x : σ ⊢ e : τ<br />
A ⊢ λx.e : σ → τ [ABS]<br />
A ⊢ e : ∀α.τ<br />
A ⊢ e : τ[σ/α]<br />
[SPEC]<br />
Polimorfismo parametrico – p. 36
Un sistema di inferenza<br />
A ⊢ e : τ<br />
A ⊢ e : ∀α.τ<br />
A.x : τ ⊢ x : τ [IDE]<br />
(α non libera in A) [GEN]<br />
A.x : σ ⊢ e : τ<br />
A ⊢ λx.e : σ → τ [ABS]<br />
A ⊢ e : ∀α.τ<br />
A ⊢ e : τ[σ/α]<br />
֒→Esempio: possiamo dedurre il tipo principale per la funzione<br />
identità nel seguente modo:<br />
x : α ⊢ x : α<br />
⊢ λx.x : α → α<br />
⊢ λx.x : ∀α.α → α<br />
[SPEC]<br />
Polimorfismo parametrico – p. 36
Un sistema di inferenza<br />
A ⊢ e : τ<br />
A ⊢ e : ∀α.τ<br />
A.x : τ ⊢ x : τ [IDE]<br />
(α non libera in A) [GEN]<br />
A.x : σ ⊢ e : τ<br />
A ⊢ λx.e : σ → τ [ABS]<br />
A ⊢ e : ∀α.τ<br />
A ⊢ e : τ[σ/α]<br />
֒→Esempio: possiamo dedurre il tipo principale per la funzione<br />
identità nel seguente modo:<br />
x : α ⊢ x : α<br />
⊢ λx.x : α → α<br />
⊢ λx.x : ∀α.α → α<br />
Un tipo specializzato per la funzione identità può essere dedotto da<br />
questo con [SPEC]:<br />
⊢ λx.x : ∀α.α → α<br />
⊢ λx.x : Int → Int<br />
[SPEC]<br />
Polimorfismo parametrico – p. 36
Il costrutto let<br />
Il costrutto let è utile sia per evitare ripetizioni che per aumentare<br />
la leggibilità del codice.<br />
Polimorfismo parametrico – p. 37
Il costrutto let<br />
Il costrutto let è utile sia per evitare ripetizioni che per aumentare<br />
la leggibilità del codice. È aggiunto al λ-<strong>calcolo</strong> aggiungendo<br />
alla sintassi dei termini<br />
e alla semantica<br />
let x = t in t<br />
let x = v1 in t2 → [x ↦→ v1]t2 (E-LETV)<br />
t1 → t ′ 1<br />
let x = t1 in t2 → let x = t ′ 1<br />
in t2<br />
(E-LET)<br />
Polimorfismo parametrico – p. 37
Il costrutto let<br />
Tali aggiunte non rappresentano un’estensione operazionalmente<br />
significativa del linguaggio, infatti let può essere simulato con<br />
una astrazione ed un’applicazione:<br />
let x = t1 in t2 ↔ (λx.t2)t1<br />
Polimorfismo parametrico – p. 38
Il costrutto let ed i <strong>tipi</strong><br />
Per quanto riguarda il sistema dei <strong>tipi</strong> otteniamo però un<br />
linguaggio più espressivo.<br />
֒→ Ad esempio, vorremmo poter tipare Id(Id) ↔ let z = (λx.x) in zz<br />
Polimorfismo parametrico – p. 39
Il costrutto let ed i <strong>tipi</strong><br />
Per quanto riguarda il sistema dei <strong>tipi</strong> otteniamo però un<br />
linguaggio più espressivo.<br />
֒→ Ad esempio, vorremmo poter tipare Id(Id) ↔ let z = (λx.x) in zz ,<br />
ma (λz.zz)(λx.x) non è tipabile.<br />
Polimorfismo parametrico – p. 39
Il costrutto let ed i <strong>tipi</strong><br />
Per quanto riguarda il sistema dei <strong>tipi</strong> otteniamo però un<br />
linguaggio più espressivo.<br />
֒→ Ad esempio, vorremmo poter tipare Id(Id) ↔ let z = (λx.x) in zz ,<br />
ma (λz.zz)(λx.x) non è tipabile. Infatti la sua derivazione è:<br />
z : Z ∈ {z : Z} z : Z ∈ {z : Z}<br />
{z : Z} ⊢ z : Z| ∅{} {z : Z} ⊢ z : Z| ∅{}<br />
{z : Z} ⊢ zz : Y| {Y}{Z = Z → Y}<br />
{} ⊢ λz : Z.zz : Z → Y| {Y}{Z = Z → Y}<br />
L’algoritmo di unificazione incorre in un fallimento per occur check:<br />
Z ∈ Var(Z → Y).<br />
Polimorfismo parametrico – p. 39
Il costrutto let ed i <strong>tipi</strong><br />
Per quanto riguarda il sistema dei <strong>tipi</strong> otteniamo però un<br />
linguaggio più espressivo.<br />
֒→ Ad esempio, vorremmo poter tipare Id(Id) ↔ let z = (λx.x) in zz ,<br />
ma (λz.zz)(λx.x) non è tipabile. Infatti la sua derivazione è:<br />
z : Z ∈ {z : Z} z : Z ∈ {z : Z}<br />
{z : Z} ⊢ z : Z| ∅{} {z : Z} ⊢ z : Z| ∅{}<br />
{z : Z} ⊢ zz : Y| {Y}{Z = Z → Y}<br />
{} ⊢ λz : Z.zz : Z → Y| {Y}{Z = Z → Y}<br />
L’algoritmo di unificazione incorre in un fallimento per occur check:<br />
Z ∈ Var(Z → Y).<br />
Modifichiamo quindi la regola di typing per il let in modo da<br />
eseguire prima del <strong>calcolo</strong> dei <strong>tipi</strong> il passo di valutazione<br />
let x = v1 in t2 → [x ↦→ v1]t2<br />
Polimorfismo parametrico – p. 39
Annotazioni di tipo implicite<br />
Per avere un let polimorfo, innanzitutto aggiungiamo alla sintassi<br />
dei termini la λ-astrazione senza annotazioni di tipo,<br />
associandole la seguente nuova regola di typing:<br />
X /∈ χ Γ, x : X ⊢ t1 : T|χC<br />
Γ ⊢ λx.t1 : X → T|χ∪{X}C<br />
(CT-ABSINF)<br />
Polimorfismo parametrico – p. 40
Il let polimorfo<br />
Adesso aggiungiamo una regola diretta di typing del let:<br />
Γ ⊢ t1 : T1 Γ ⊢ [x ↦→ t1]t2 : T2<br />
Γ ⊢ let x = t1 in t2 : T2<br />
(T-LET)<br />
Abbiamo controllato che anche t1 fosse ben tipato per tutelarci nel caso<br />
in cui x non occorre mai in t2.<br />
La regola per il sistema di vincoli è simile:<br />
Γ ⊢ t1 : T1|χ1C1 Γ ⊢ [x ↦→ t1]t2 : T2|χ2C2<br />
χ1 ∩ χ2 = ∅<br />
Γ ⊢ let x = t1 in t2 : T2|χ1∪χ2{C1 ∪ C2}<br />
(CT-LETPOLY)<br />
Polimorfismo parametrico – p. 41
Nuova type inference<br />
L’uso congiunto di CT-LETPOLY e di CT-ABSINF produce<br />
l’effetto desiderato:<br />
• CT-LETPOLY produce tutte le copie necessarie della<br />
definizione t1 e le sostituisce ovunque il nome x compare<br />
libero in t2;<br />
• la type reconstruction prosegue quindi su questa<br />
espressione espansa, dove con CT-ABSINF si assegna a<br />
ciascuna λ-astrazione una variabile di tipo diversa.<br />
Polimorfismo parametrico – p. 42
Autoapplicazione dell’identità<br />
Le nuove regole introdotte permettono di tipare<br />
l’autoapplicazione dell’identità<br />
La derivazione è la seguente:<br />
x : X ∈ {x : X}<br />
(X fresh)<br />
{x : X} ⊢ x : X| ∅{}<br />
{} ⊢ λx.x : X → X| ∅{}<br />
let z = (λx.x) in zz<br />
x : Y ∈ {x : Y}<br />
(Y fresh)<br />
{x : Y} ⊢ x : Y| ∅{}<br />
x : Z ∈ {x : Z}<br />
(Z fresh)<br />
{x : Z} ⊢ x : Z| ∅{}<br />
{} ⊢ λ x.x : Y → Y| ∅{} {} ⊢ λ x.x : Z → Z| ∅{}<br />
{} ⊢ (λ x.x)(λ x.x) : W| {W}{Y → Y = (Z → Z) → W}<br />
{} ⊢ let z = (λx.x) inzz : W| {W}{Y → Y = (Z → Z) → W}<br />
Polimorfismo parametrico – p. 43
Autoapplicazione dell’identità<br />
L’algoritmo di unificazione esegue i seguenti passi:<br />
unify({Y → Y = (Z → Z) → W})<br />
1. S = Y → Y,T = (Z → Z) → W ⇒<br />
⇒ unify({Y = Z → Z,Y = W})<br />
2. S = Y and Y /∈ Var(Z → Z) ⇒<br />
⇒ unify([Y ↦→ Z → Z]{Y = W}) ◦ [Y ↦→ Z → Z]<br />
⇒ unify({Z → Z = W}) ◦ [Y ↦→ Z → Z]<br />
3. T = W and W /∈ Var(Z → Z) ⇒<br />
⇒ unify([W ↦→ Z → Z]∅) ◦ [W ↦→ Z → Z] ◦ [Y ↦→ Z → Z]<br />
⇒ [] ◦ [W ↦→ Z → Z] ◦ [Y ↦→ Z → Z]<br />
Abbiamo quindi σ = [W ↦→ Z → Z,Y ↦→ Z → Z],<br />
da cui si deduce che il tipo principale è σW = Z → Z.<br />
Polimorfismo parametrico – p. 44
Possibili approfondimenti<br />
1) Le implementazioni pratiche del let-<strong>polimorfi</strong>smo sono<br />
riformulazioni più efficienti: evitano che per ogni occorrenza<br />
della variabile nel corpo del let venga verificato il termine<br />
sostituito.<br />
Polimorfismo parametrico – p. 45
Possibili approfondimenti<br />
1) Le implementazioni pratiche del let-<strong>polimorfi</strong>smo sono<br />
riformulazioni più efficienti: evitano che per ogni occorrenza<br />
della variabile nel corpo del let venga verificato il termine<br />
sostituito.<br />
2) È possibile pensare di astrarre i <strong>tipi</strong> “fuori” dai termini per poi<br />
inserirli in un secondo momento; questo concetto ha portato allo<br />
sviluppo (Girard, 1972; Reynolds, 1974) di una nuova estensione<br />
del λ-<strong>calcolo</strong> con <strong>tipi</strong> semplici, chiamata System F.<br />
Polimorfismo parametrico – p. 45
Possibili approfondimenti<br />
1) Le implementazioni pratiche del let-<strong>polimorfi</strong>smo sono<br />
riformulazioni più efficienti: evitano che per ogni occorrenza<br />
della variabile nel corpo del let venga verificato il termine<br />
sostituito.<br />
2) È possibile pensare di astrarre i <strong>tipi</strong> “fuori” dai termini per poi<br />
inserirli in un secondo momento; questo concetto ha portato allo<br />
sviluppo (Girard, 1972; Reynolds, 1974) di una nuova estensione<br />
del λ-<strong>calcolo</strong> con <strong>tipi</strong> semplici, chiamata System F.<br />
Tale estensione corrisponde, in base all’isomorfismo di<br />
Curry-Howard, alla logica intuizionistica del secondo ordine, la<br />
quale permette quantificazioni sui predicati (ovvero sui <strong>tipi</strong>).<br />
Polimorfismo parametrico – p. 45
Bibliografia<br />
1 Benjamin C.Pierce, Types and Programming Languages -<br />
cap. 22, MIT Press, 2002.<br />
Polimorfismo parametrico – p. 46
Bibliografia<br />
1 Benjamin C.Pierce, Types and Programming Languages -<br />
cap. 22, MIT Press, 2002.<br />
2 Luca Cardelli, Basic Polimorphic Typechecking, AT&T Bel<br />
Laboratories, 1988.<br />
Polimorfismo parametrico – p. 46
FINE<br />
Polimorfismo parametrico – p. 47
FINE<br />
Grazie per l’attenzione<br />
Polimorfismo parametrico – p. 47