EDIT: un commento sopra ha fornito il pezzo mancante. Alcune persone stanno deliberatamente giocando con lingue tutt'altro che turing. Non mi interessa esplicitamente tali lingue. Un linguaggio davvero non usurabile completo è una cosa folle e difficile da progettare. Tutto il resto si espande su ciò che accade cercando di applicare questi teoremi a un linguaggio completo.
Falso!
function f(a): forall t: Type, t->t
function g(a): forall t: Type, t->t
return (a is g) ? f : a
return a is f ? g : a
dove l' is
operatore confronta due variabili per l'identità di riferimento. Cioè, contengono lo stesso valore. Non un valore equivalente, stesso valore. Le funzioni f
e g
sono equivalenti per alcune definizioni ma non sono le stesse.
Se questa funzione viene passata, restituisce qualcos'altro; altrimenti restituisce il suo input. Qualcos'altro ha lo stesso tipo di se stesso, quindi può essere sostituito. In altre parole, f
non è l'identità, perché f(f)
ritorna g
, mentre l'identità ritornerebbef
.
Affinché il teorema lo sostenga, deve assumere la ridicola capacità di ridurre
function cantor(n, <z, a>) : forall t: t: Type int, <int, t> -> <int, t>
return n > 1 ? cantor((n % 2 > 0) ? (n + 1) : n / 2, <z + 1, a>) : <z, a>
return cantor(1000, <0, a>)[1]¹
Se sei disposto ad assumere che tu possa presumere che l'inferenza del tipo molto più semplice possa essere gestita.
Se proviamo a limitare il dominio fino a quando il teorema regge, finiamo per doverlo limitare terribilmente lontano.
- Funzionale puro (nessuno stato modificabile, nessun IO). OK, posso conviverci. Molto tempo vogliamo eseguire prove sulle funzioni.
- Libreria standard vuota. meh.
- No
raise
e noexit
. Ora stiamo iniziando a essere vincolati.
- Non esiste un tipo di fondo.
- Il linguaggio ha una regola che consente al compilatore di comprimere la ricorsione infinita assumendo che debba terminare. Il compilatore può rifiutare una banale ricorsione infinita.
- Il compilatore può fallire se presentato con qualcosa che non può essere provato in entrambi i modi. Ora la libreria standard non può accettare funzioni come argomenti. Boo.
- Non c'è
nil
. Questo sta iniziando a diventare problematico. Abbiamo esaurito i modi per gestire 1 / 0.³
- La lingua non può fare inferenze sul tipo di ramo e non ha una sostituzione per quando il programmatore può provare un'inferenza di tipo che la lingua non può. Questo è piuttosto male.
L'esistenza di entrambi gli ultimi due vincoli ha paralizzato la lingua. Mentre è ancora Turing completo, l'unico modo per far funzionare il suo scopo generale è quello di simulare una piattaforma interna che interpreta una lingua con requisiti più ampi.
¹ Se pensi che il compilatore possa dedurlo, prova questo
function fermat(z) : int -> int
function pow(x, p)
return p = 0 ? 1 : x * pow(x, p - 1)
function f2(x, y, z) : int, int, int -> <int, int>
left = pow(x, 5) + pow(y, 5)
right = pow(z, 5)
return left = right
? <x, y>
: pow(x, 5) < right
? f2(x + 1, y, z)
: pow(y, 5) < right
? f2(2, y + 1, z)
: f2(2, 2, z + 1)
return f2(2, 2, z)
function cantor(n, <z, a>) : forall t: t: Type int, <int, t> -> <int, t>
return n > 1 ? cantor((n % 2 > 0) ? (n + 1) : n / 2, <z + 1, a>) : <z, a>
return cantor(fermat(3)[0], <0, a>)[1]
² La prova che il compilatore non può farlo dipende dall'accecamento. Possiamo usare più librerie per garantire che il compilatore non possa vedere il ciclo in una volta. Inoltre, possiamo sempre costruire qualcosa in cui il programma funzioni ma non può essere compilato perché il compilatore non può eseguire l'induzione nella memoria disponibile.
³ Qualcuno pensa che tu possa avere questo valore di ritorno zero senza tipi generici arbitrari che restituiscono zero. Questo paga una brutta penalità per la quale non ho visto un linguaggio efficace in grado di pagarlo.
function f(a, b, c): t: Type: t[],int,int->t
return a[b/c]
non deve compilare. Il problema fondamentale è che l'indicizzazione dell'array di runtime non funziona più.