Che cos'è un sistema di tipi?


50

sfondo

Sto progettando una lingua, come un progetto collaterale. Ho un assemblatore funzionante, un analizzatore statico e una macchina virtuale per questo. Dato che posso già compilare ed eseguire programmi non banali usando l'infrastruttura che ho costruito, ho pensato di fare una presentazione alla mia università.

Durante il mio discorso ho detto che la VM fornisce un sistema di tipi, mi è stato chiesto "A cosa serve il tuo sistema di tipi? ". Dopo aver risposto, sono stato deriso dalla persona che ha posto la domanda.

Pertanto, anche se quasi sicuramente perderò la reputazione per aver posto questa domanda, mi rivolgo ai programmatori.

La mia comprensione

A quanto ho capito, i sistemi di tipi vengono utilizzati per fornire un ulteriore livello di informazioni sulle entità in un programma, in modo che il runtime, il compilatore o qualsiasi altro macchinario, sappia cosa fare con le stringhe di bit su cui opera. Aiutano anche a mantenere i contratti - il compilatore (o analizzatore di codice, o runtime o qualsiasi altro programma) può verificare che in qualsiasi momento il programma funzioni su valori su cui i programmatori si aspettano di operare.

I tipi possono anche essere usati per fornire informazioni a quei programmatori umani. Ad esempio, trovo questa dichiarazione:

function sqrt(double n) -> double;

più utile di questo

sqrt(n)

Il primo fornisce molte informazioni: l' sqrtidentificatore è una funzione, accetta un singolo doublecome input e ne produce un altro doublecome output. Quest'ultimo ti dice che probabilmente è una funzione che accetta un singolo parametro.

La mia risposta

Quindi, dopo essere stato chiesto "A cosa serve il tuo sistema di tipi?" Ho risposto come segue:

Il sistema dei tipi è dinamico (i tipi sono assegnati ai valori, non alle variabili che li trattengono), ma forte senza sorprendere le regole di coercizione (non è possibile aggiungere una stringa all'intero in quanto rappresentano tipi incompatibili, ma è possibile aggiungere un numero intero al numero in virgola mobile) .

Il sistema di tipi viene utilizzato dalla VM per garantire che gli operandi per le istruzioni siano validi; e può essere utilizzato dai programmatori per garantire che i parametri passati alle loro funzioni siano validi (cioè di tipo corretto).
Il sistema di tipi supporta il sottotipo e l'ereditarietà multipla (entrambe le funzionalità sono disponibili per i programmatori) e i tipi vengono considerati quando si utilizza l'invio dinamico di metodi su oggetti: la VM utilizza i tipi per verificare mediante quale funzione viene implementato un determinato messaggio per un determinato tipo.

La domanda di follow-up era "E come viene assegnato il tipo a un valore?". Quindi ho spiegato che tutti i valori sono inscatolati e hanno un puntatore che punta a una struttura di definizione del tipo che fornisce informazioni sul nome del tipo, su quali messaggi risponde e da quali tipi eredita.

Dopodiché, sono stato deriso e la mia risposta è stata respinta con il commento "Questo non è un vero sistema tipografico".

Quindi, se ciò che ho descritto non si qualifica come un "vero sistema tipografico", cosa farebbe? Quella persona aveva ragione nel dire che ciò che fornisco non può essere considerato un sistema di tipi?


19
Quando le persone parlano di sistemi di tipo, di solito parlano di tipizzazione statica. La digitazione dinamica non è molto interessante per il tipo di persone che si preoccupano dei sistemi di tipi perché non garantisce quasi nulla. Ad esempio, che tipo di valore può contenere la variabile x? Nulla.
Doval,

7
Sarei curioso di sapere cosa avrebbero dovuto dire per difendere / spiegare la loro reazione.
Newtopian,

18
@Doval La digitazione dinamica può garantire di non entrare in uno stato senza senso facendo qualcosa come aggiungere 5 al tuo gatto. Certo, non ti impedirà di provare , ma almeno può impedirgli di accadere realmente e darti la possibilità di capire cosa è andato storto e intraprendere azioni correttive, cose che un linguaggio davvero senza tipizzazione non può.
settembre

10
La persona ha contestato la tua risposta a "E come viene assegnato il tipo a un valore?". Volevano conoscere le regole di digitazione, non i diagrammi box-e-pointer. Ridere era assolutamente scortese, però.
gardenhead

10
La persona che ride è molto probabilmente un fanatico di un particolare linguaggio (famiglia) con un sistema di tipo forte (Haskell sembra popolare), e ridicolizzerebbe qualsiasi cosa meno forte (e quindi un giocattolo) di quello, o più forte (e quindi poco pratico), o semplicemente diverso. Impegnarsi in discussioni con zeloti è pericoloso e inutile. Ridere così è così maleducato che indica questo tipo di problemi più profondi. Sei fortunato che non abbiano iniziato a predicare ...
hyde,

Risposte:


30

Tutto ciò sembra una bella descrizione del tipo di sistema fornito. E la tua implementazione sembra abbastanza ragionevole per quello che sta facendo.

Per alcune lingue, non avrai bisogno delle informazioni di runtime poiché la tua lingua non esegue l'invio di runtime (o fai l'invio singolo tramite vtables o un altro meccanismo, quindi non hai bisogno delle informazioni sul tipo). Per alcune lingue, è sufficiente avere un simbolo / segnaposto poiché ti preoccupi solo dell'uguaglianza dei tipi, non del suo nome o eredità.

A seconda del tuo ambiente, la persona potrebbe aver desiderato più formalismo nel tuo sistema di tipi. Vogliono sapere cosa puoi provare con esso, non cosa possono fare i programmatori con esso. Purtroppo questo è abbastanza comune nel mondo accademico. Sebbene gli accademici facciano queste cose perché è abbastanza facile avere difetti nel tuo sistema di tipi che permettono alle cose di sfuggire alla correttezza. È possibile che abbiano individuato uno di questi.

Se hai ulteriori domande, Tipi e Linguaggi di programmazione è il libro canonico sull'argomento e può aiutarti a imparare un po 'del rigore necessario agli accademici, così come una parte della terminologia per aiutare a descrivere le cose.


3
"A seconda del tuo ambiente, la persona potrebbe aver desiderato più formalismo nel tuo sistema di tipi." Questo è probabilmente. Non mi sono concentrato su ciò che posso dimostrare con il sistema di tipi, ma piuttosto ci ho pensato come uno strumento. Grazie per il consiglio sul libro!
Mael,

1
@Mael Alcuni tipi di sistemi sono usati come logiche (vedi framework logici ). quindi fondamentalmente il tipo fornisce le formule e i programmi ne sono la prova (ad es. il tipo di funzione a -> bpuò essere visto come un implicito b , cioè se mi dai un valore di tipo aposso ottenere un valore di tipo b). Tuttavia, affinché ciò sia coerente, la lingua deve essere totale e quindi non completa di Turing. Quindi, tutti i sistemi di tipo di vita reale in realtà definire una logica incoerente.
Bakuriu,

20

Mi piace la risposta di @ Telastyn soprattutto per il riferimento all'interesse accademico per il formalismo.

Consentitemi di aggiungere alla discussione.

Che cos'è un sistema di tipi?

Un sistema di tipi è un meccanismo per definire, rilevare e prevenire stati di programma illegali. Funziona definendo e applicando i vincoli. Le definizioni dei vincoli sono tipi e le applicazioni dei vincoli sono usi di tipi , ad esempio nella dichiarazione delle variabili.

Le definizioni dei tipi in genere supportano gli operatori di composizione (ad es. Varie forme di congiunzione, come nelle strutture, nella sottoclasse e, nella disgiunzione, come negli enum, nei sindacati).

I vincoli, gli usi dei tipi, a volte consentono anche agli operatori di composizione (ad esempio almeno questo, esattamente questo, questo o quello, a condizione che qualcos'altro valga).

Se il sistema di tipi è disponibile nella lingua e applicato in fase di compilazione verso l'obiettivo di poter emettere errori in fase di compilazione, è un sistema di tipo statico; questi impediscono la compilazione di molti programmi illegali e tanto meno l'esecuzione, quindi impediscono gli stati dei programmi illegali.

(Un sistema di tipo statico interrompe l'esecuzione di un programma indipendentemente dal fatto che sia noto (o indecidibile) che il programma raggiungerà mai quel codice non corretto di cui si lamenta. Un sistema di tipo statico rileva alcuni tipi di assurdità (violazioni dei vincoli dichiarati) e giudica il programma in errore prima che venga mai eseguito.)

Se un sistema di tipi viene applicato in fase di esecuzione, è un sistema di tipo dinamico che impedisce gli stati di programma illegali: ma arrestando il programma a metà corsa, invece di impedirne l'esecuzione in primo luogo.

Un'offerta di sistema di tipo abbastanza comune consiste nel fornire funzionalità sia statiche che dinamiche.


Non credo che i cosiddetti sistemi di tipo ibrido siano molto comuni. Quali lingue hai in mente?
gardenhead

2
@gardenhead, la possibilità di effettuare il downcast non è una funzionalità di sistema di tipo statico, pertanto viene solitamente controllata in fase di esecuzione in modo dinamico.
Erik Eidt,

1
@gardenhead: la maggior parte dei linguaggi tipizzati staticamente ti consente di rinviare la digitazione al runtime, sia semplicemente con i void *puntatori di C (molto deboli), gli oggetti dinamici di C # o i GADT quantificati esistenzialmente di Haskell (che ti danno garanzie più forti dei valori tipizzati staticamente nella maggior parte degli altri le lingue).
lasciato il

È vero, mi sono dimenticato del "casting". Ma il casting è solo una stampella per un sistema di tipo debole.
gardenhead

@gardenhead Oltre ai linguaggi statici che forniscono opzioni dinamiche, molti linguaggi dinamici forniscono una digitazione statica. Ad esempio, Dart, Python e Hack, hanno tutti modalità o strumenti per eseguire analisi statiche basate sul concetto di "digitazione graduale".
IMSoP,

14

Oh amico, sono entusiasta di provare a rispondere a questa domanda nel miglior modo possibile. Spero di riuscire a mettere correttamente in ordine i miei pensieri.

Come accennato da @Doval e sottolineato dall'interrogatore (anche se sgarbatamente), non hai davvero un sistema di tipi. Hai un sistema di controlli dinamici che usa i tag, che è generalmente molto più debole e anche molto meno interessante.

La questione di "cos'è un sistema di tipi" può essere piuttosto filosofica e potremmo riempire un libro con diversi punti di vista sull'argomento. Tuttavia, poiché questo è un sito per programmatori, cercherò di mantenere la mia risposta il più pratica possibile (e in realtà, i tipi sono estremamente pratici nella programmazione, nonostante ciò che alcuni potrebbero pensare).

Panoramica

Cominciamo con una comprensione dei vantaggi di un sistema di tipo, prima di immergerci nelle basi più formali. Un sistema di tipo impone la struttura ai nostri programmi . Ci dicono come possiamo collegare varie funzioni ed espressioni insieme. Senza struttura, i programmi sono insostenibili e selvaggiamente complessi, pronti a causare danni al minimo errore del programmatore.

Scrivere programmi con un sistema di tipi è come guidare una cura in perfette condizioni: i freni funzionano, le portiere si chiudono in sicurezza, il motore è oliato, ecc. Scrivere programmi senza un sistema di tipi è come guidare un motociclo senza casco e con ruote fatte fuori dagli spaghetti. Non hai assolutamente alcun controllo sul tuo.

Per fondare la discussione, diciamo che abbiamo un linguaggio con espressione letterale num[n]e str[s]che rappresenta il numero n e la stringa s, rispettivamente, e le funzioni primitive pluse concat, con il significato previsto. Chiaramente, non vuoi essere in grado di scrivere qualcosa di simile plus "hello" "world"o concat 2 4. Ma come possiamo impedirlo? A priori , non esiste un metodo per distinguere il numero 2 dal "mondo" letterale stringa. Quello che vorremmo dire è che queste espressioni dovrebbero essere usate in contesti diversi; hanno diversi tipi.

Lingue e tipi

Facciamo un passo indietro: cos'è un linguaggio di programmazione? In generale, possiamo dividere un linguaggio di programmazione in due livelli: la sintassi e la semantica. Questi sono anche chiamati statica e dinamica , rispettivamente. Si scopre che il sistema di tipi è necessario per mediare l'interazione tra queste due parti.

Sintassi

Un programma è un albero. Non lasciarti ingannare dalle righe di testo che scrivi su un computer; queste sono solo le rappresentazioni leggibili dall'uomo di un programma. Il programma stesso è un albero di sintassi astratto . Ad esempio, in C potremmo scrivere:

int square(int x) { 
    return x * x;
 }

Questa è la sintassi concreta per il programma (frammento). La rappresentazione dell'albero è:

     function square
     /     |       \
   int   int x    return
                     |
                   times
                  /    \
                 x      x

Un linguaggio di programmazione fornisce una grammatica che definisce gli alberi validi di quel linguaggio (è possibile utilizzare una sintassi concreta o astratta). Questo di solito viene fatto usando qualcosa come la notazione BNF. Suppongo che tu l'abbia fatto per la lingua che hai creato.

Semantica

OK, sappiamo cos'è un programma, ma è solo una struttura ad albero statico. Presumibilmente, vogliamo che il nostro programma calcoli effettivamente qualcosa. Abbiamo bisogno della semantica.

La semantica dei linguaggi di programmazione è un ricco campo di studio. In generale, ci sono due approcci: semantica denotazionale e semantica operativa . La semantica denotazionale descrive un programma mappandolo in una struttura matematica sottostante (ad es. Numeri naturali, funzioni continue, ecc.). che fornisce significato al nostro programma. La semantica operativa, al contrario, definisce un programma descrivendo in dettaglio come viene eseguito. Secondo me, la semantica operativa è più intuitiva per i programmatori (incluso me stesso), quindi continuiamo con questo.

Non esaminerò come definire una semantica operativa formale (i dettagli sono un po 'coinvolti), ma fondamentalmente vogliamo regole come le seguenti:

  1. num[n] è un valore
  2. str[s] è un valore
  3. Se num[n1]e num[n2]valuta numeri interi n_1$ and $n_2$, thenpiù (num [n1], num [n2]) `restituisce l'intero $ n_1 + n_2 $.
  4. If str[s1]e restituisce str[s2]le stringhe s1 e s2, quindi concat(str[s1], str[s2])restituisce la stringa s1s2.

Ecc. Le regole sono in pratica molto più formali, ma hai capito bene. Tuttavia, presto incontriamo un problema. Cosa succede quando scriviamo quanto segue:

concat(num[5], str[hello])

Hm. Questo è un vero enigma. Non abbiamo definito una regola da nessuna parte per come concatenare un numero con una stringa. Potremmo tentare di creare una tale regola, ma sappiamo intuitivamente che questa operazione non ha senso. Non vogliamo che questo programma sia valido. E così siamo condotti inesorabilmente ai tipi.

tipi

Un programma è un albero definito dalla grammatica di una lingua. Ai programmi viene dato un significato dalle regole di esecuzione. Ma alcuni programmi non possono essere eseguiti; cioè alcuni programmi non hanno senso . Questi programmi sono mal digitati. Pertanto, la digitazione caratterizza programmi significativi in ​​una lingua. Se un programma è ben scritto, possiamo eseguirlo.

Facciamo alcuni esempi. Ancora una volta, come per le regole di valutazione, presenterò le regole di battitura in modo informale, ma possono essere rese rigorose. Ecco alcune regole:

  1. Un token del modulo num[n]ha tipo nat.
  2. Un token del modulo str[s]ha tipo str.
  3. Se espressione e1ha tipo nate espressione e2ha tipo nat, quindi espressione plus(e1, e2)ha tipo nat.
  4. Se espressione e1ha tipo stre espressione e2ha tipo str, quindi espressione concat(e1, e2)ha tipo str.

Pertanto, secondo queste regole, esiste plus(num[5], num[2])un tipo nat, ma non è possibile assegnare un tipo a plus(num[5], str["hello"]). Diciamo che un programma (o espressione) è ben tipizzato se possiamo assegnarlo a qualsiasi tipo, e altrimenti è mal digitato. Un sistema di tipi è valido se tutti i programmi ben digitati possono essere eseguiti. Haskell è sano; C non lo è.

Conclusione

Esistono altre visualizzazioni sui tipi. I tipi in qualche modo corrispondono alla logica intuizionista e possono anche essere visti come oggetti nella teoria delle categorie. Comprendere queste connessioni è affascinante, ma non è essenziale se si vuole semplicemente scrivere o progettare un linguaggio di programmazione. Tuttavia, comprendere i tipi come strumento per il controllo delle formazioni di programmi è essenziale per la programmazione e lo sviluppo del linguaggio. Ho solo graffiato la superficie di ciò che i tipi possono esprimere. Spero che tu valga la pena di incorporarli nella tua lingua.


4
+1. Il più grande trucco che gli appassionati di digitazione dinamica abbiano mai tirato è stato convincere il mondo che potresti avere "tipi" senza un sistema di tipi. :-)
ruakh

1
Poiché non è possibile verificare automaticamente nulla di interessante per programmi arbitrari, ogni sistema di tipo deve fornire un operatore del cast (o l'equivalente morale), altrimenti sacrifica la completezza di Turing. Questo include Haskell , ovviamente.
Kevin,

1
@Kevin Sono ben consapevole del teorema di Rice, ma non è così rilevante come potresti pensare. Innanzitutto, la grande maggioranza dei programmi non richiede ricorsioni illimitate. Se lavoriamo in una lingua che ha solo una ricorsione primitiva, come Godel's System T, allora possiamo verificare proprietà interessanti usando un sistema di tipi, incluso l'arresto. La maggior parte dei programmi nel mondo reale sono piuttosto semplici: non riesco a pensare all'ultima volta in cui ho avuto davvero bisogno del casting. La completezza di Turing è sopravvalutata.
gardenhead

9
"Digitare in modo dinamico non è davvero digitare" mi è sempre sembrato come un musicista classico che dice "la musica pop non è davvero musica", o gli evangelici che dicono "i cattolici non sono veramente cristiani". Sì, i sistemi di tipo statico sono potenti, affascinanti e importanti e la digitazione dinamica è qualcosa di diverso. Ma (come descrivono le altre risposte) c'è una serie di cose utili oltre ai sistemi di tipo statico che sono tradizionalmente chiamati tipizzazione e che condividono tutti importanti punti in comune. Perché la necessità di insistere sul nostro tipo di battitura come la vera battitura?
Peter LeFanu Lumsdaine,

5
@IMSoP: per qualcosa di più corto di un libro, il saggio di Chris Smith Cosa sapere prima di discutere i sistemi di tipi è grandioso, spiegando perché la tipizzazione dinamica è davvero molto diversa dalla tipizzazione statica.
Peter LeFanu Lumsdaine,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.