1: int "a": string (1,"a"): int*string
(fun x -> x): int -> intSi può usare una funzione facendo una applicazione (funzione argumento). Per esempio:
(fun f -> (fun x -> (f (x+1))+1)): (int->int)->int->intInfine si può dare un nome ad una funzione usando il let:
let incr = fun x -> x+1Adesso (incr 1) vale 2. Una sintassi piú frequente per la dichiarazione precedente è:
let incr x = x+1
let id = (fun x -> x)definisce la funzione identità che si può applicare a tutti i tipi di oggetto. Ciò viene reso esplicito mettendo come prefisso al tipo il carattere '. Per esempio,
id: 'a -> 'avuole dire che l'identità prende un oggetto di un qualunque tipo e restituisce un oggetto dello stesso tipo.
Facciamo altri esempi:
let pair = (fun x -> (fun y -> (x,y)))La funzione pair prende due oggetti e restituisce una coppia. Il tipo di pair è
pair: 'a -> 'b -> 'a*'b
let fst = (fun x -> match x with (a,b) -> a)La funzione fst prende una coppia e restituisce il primo elemento. Il tipo di fst è
fst: 'a*'b -> 'a
let snd = (fun x -> match x with (a,b) -> b)La funzione snd prende una coppia e restituisce il secondo elemento. Il tipo di snd è
snd: 'a*'b -> 'b
let swap = (fun x -> (pair (snd x) (fst x)))Usando i tipi di fst, snd e pair, si può dedurre il tipo di swap:
|
||||||||||||
¯fun | ||||||||||||
('a * 'b) -> ('b * 'a) |
Ciò è esattamente l'equivalente della prova:
A ∧ B B
A ∧ B A
B ∧ A
(A ∧ B) ⇒ (B ∧ A)
Con le tabelle di corrispondenza:
Tipo | Formula |
'a * 'b | A ∧ B |
'a -> 'b | A ⇒ B |
Funzione | Regola |
fst | ∧ eliminazione 1 |
snd | ∧ eliminazione 2 |
pair | ∧ introduzione |
fun | ⇒ introduzione |
Una prova di A ∧ B è dunque vista come una coppia costituita da una prova di A e da una prova di B.
Una prova di A ⇒ B è vista come una funzione che prende una prova di A e restituisce una prova di B. Adesso si può dare l'equivalente dell'eliminazione dell'implicazione:
A ⇒ B A B
Tale regola corrisponde all'applicazione: per avere una prova di B
è sufficiente applicare alla funzione che prende una prova di A
e restituisce una prova di B una prova di A.
In Caml, si scrive:
let app = fun f -> (fun x -> (f x))Il tipo di questa funzione è ('a->'b)->'a->'b.
Adesso proviamo a trovare una prova di
((A ⇒ B) ∧ (C ⇒ D)) ⇒ ((A ∧ C) ⇒ (B ∧ D))
Usando l'isomorfismo ciò è equivalente a trovare un progamma
che ha il tipo:
(('a -> b) * ('c -> 'd)) -> (('a * 'c) -> ('b * 'd))
Una possibilità è la seguente:
(fun x -> (fun y -> (pair ((fst x) (fst y)) ((snd x) (snd y)))))La prova corrispondente è:
(A ⇒ B) ∧ (C ⇒ D) A ⇒ B
A ∧ C A
B
(A ⇒ B) ∧ (C ⇒ D) C ⇒ D
A ∧ C C
D
B ∧ D (A ∧ C) ⇒ (B ∧ D)
((A ⇒ B) ∧ (C ⇒ D)) ⇒ ((A ∧ C) ⇒ (B ∧ D))
Proviamo a dare una prova di
A ⇒ ((A ⇒ B) ⇒ B)
Vuole dire trovare una funzione di tipo 'a -> (('a -> 'b) -> 'b). Una possibilità è:
(fun x -> (fun f -> (f x)))che corrisponde a
A ⇒ B A B
(A ⇒ B) ⇒ B
A ⇒ ((A ⇒ B) ⇒ B)
Adesso proviamo a dare una prova di
(A ⇒ B) ⇒ A ⇒ B
Vuole dire trovare una funzione di tipo ('a -> b) -> 'a -> 'b. Una possibilità è:
(fun f -> (fun x -> (f x)))che corrisponde a
A ⇒ B A B
A ⇒ B
(A ⇒ B) ⇒ A ⇒ B
C'è una prova più semplice:
A ⇒ B (A ⇒ B) ⇒ A ⇒ B
che corrisponde alla funzione identità:
(fun x -> x)Ma il tipo di questa funzione è troppo generale 'a -> 'a. Per avere il tipo esatto, bisogna dare esplicitamente il tipo di x
(fun (x:'a->'b) -> x)
(fun x -> [x]): 'a ->'a listPerò una lista è monomorfica: tutti i suoi elementi sono dello stesso tipo. Dunque non si può creare una lista come [1;"a"]. Per avere qualcosa di simile in un linguaggio come CAML, bisogna creare degli incapsulatori (wrapper). Nel nostro caso ciò vuol dire creare un nuovo tipo sum:
type sum = Inl of int | Inr of stringsum può essere visto con un tipo che permette di avere sia oggetti di tipo int che oggetti di tipo string.
Adesso abbiamo un tipo, ma come si scrivono delle funzioni su questo tipo? Si può usare una costruzione generica: supponendo di avere una funzione che prende un int e un'altra funzione di un tipo simile ma che prende un string, si può creare una funzione su sum:
let f_sum c f1 f2 = match c with Inl a -> (f1 a) | Inr a -> (f2 a)Il tipo di tale funzione è sum-> (int -> 'a) -> (string -> 'a) -> 'a. Per esempio si può avere
let is_int_null i = (i = 0) let is_string_null s = (s = "") let is_sum_null c = f_sum c f1 f2 let rec is_list_null l = match l with [] -> true | (a::l1) -> if (is_sum_null a) then (is_list_null l1) else falseL'ultima funzione verifica se una lista di sum è nulla, i.e. contiene solo 0 e "".
La costruzione che abbiamo fatto per i tipi int e string può essere fatta a partire da due tipi arbitrari usando il polimorfismo:
type ('a,'b) sum = Inl of 'a | Inr of 'b
Adesso possiamo aggiungere altre righe alle nostre tabelle:
Tipo | Formula |
'a * 'b | A ∧ B |
'a -> 'b | A ⇒ B |
('a,'b) sum | A ∨ B |
Funzione | Regola |
fst | ∧ eliminazione 1 |
snd | ∧ eliminazione 2 |
pair | ∧ introduzione |
fun | ⇒ introduzione |
app | ⇒ eliminazione |
Inl | ∨ introduzione 1 |
Inr | ∨ introduzione 2 |
L'interpretazione di una prova di A ∨ B è che è una prova di A o una prova di B.
Per convincersi della nuova corrispondenza consideriamo la funzione:
let swap_sum = (fun x -> f_sum x Inr Inl)Il suo tipo è ('a,'b) sum -> ('b,'a) sum che corrisponde alla formula (A ∨ B) ⇒ (B ∨ A).
| |||||||||
B ∨ A |
False |
A |
type False =Non si può avere tale tipo in CAML. La regola d'eliminazione corrisponderebbe alla funzione
let f = (fun x -> match x with )Non si potrebbe discutere sul valore di x, in qunto non esiste un costruttore. Dunque il tipo di tale funzione sarebbe False -> 'a.
[A] . . False ¬A
che è equivalente a
e
[A] . . False A ⇒ False
A ¬A False
che è equivalente a
A A ⇒ False False
(P c) |
∀x. (P x) |
(P c) |
∀x. (P x) |
| ||||||||||
B |
La differenza fra un'implicazione e una quantificazione universale è solo il fatto che l'attuale valore del parametro compare nel tipo risultato. Si parla di tipo dipendente.
Non è possibile costruire tale tipo in CAML. Per dare un'idea consideriamo la funzione:
let rec array n a = match n with 0 -> [] | _ -> a:: (array (n-1) a)Il suo tipo è int -> 'a -> 'a list. Applicando tale funzione con 0 e "a", il risultato è []. Con 1 e "a", il risultato è ["a"]. Con 2 e "a", il risultato è ["a";"a"]. Con i tipi dipendenti si potrebbe avere un tipo più preciso dicendo che la funzione prende due argomenti e restituisce una lista la cui lunghezza è uguale al valore del primo parametro. Dunque il tipo sarebbe qualcosa come ∀x:int. 'a -> 'a (list x) dove (list n) rappresenta il tipo delle liste di lunghezza n.