implementazione dell'inferenza del tipo


91

Vedo qui alcune discussioni interessanti sulla digitazione statica e dinamica. In genere preferisco la digitazione statica, a causa del controllo del tipo di compilazione, del codice meglio documentato, ecc. Tuttavia, sono d'accordo che ingombrano il codice se fatto nel modo in cui lo fa Java, per esempio.

Quindi sto per iniziare a costruire un mio linguaggio di stile funzionale e l'inferenza del tipo è una delle cose che voglio implementare. Capisco che sia un argomento importante e non sto cercando di creare qualcosa che non sia stato fatto prima, solo inferenze di base ...

Qualche suggerimento su cosa leggere che mi aiuterà in questo? Preferibilmente qualcosa di più pragmatico / pratico rispetto a testi di teoria delle categorie / teoria dei tipi più teorici. Se c'è un testo di discussione sull'implementazione là fuori, con strutture / algoritmi di dati, sarebbe semplicemente adorabile.


1
Esattamente la domanda che stavo cercando, con alcune ottime risposte!
Paul Hollingsworth,

Risposte:


90

Ho trovato utili le seguenti risorse per comprendere l'inferenza di tipo, in ordine di difficoltà crescente:

  1. Capitolo 30 (Type Inference) del libro PLAI , Programming Languages: Application and Interpretation, disponibile gratuitamente , inferenza di tipo basata sull'unificazione.
  2. Il corso estivo Interpretare i tipi come valori astratti presenta eleganti valutatori, controllori di caratteri, ricostruttori di caratteri e inferitori che utilizzano Haskell come metalinguaggio.
  3. Capitolo 7 (Tipi) del libro EOPL , Essentials of Programming Languages .
  4. Capitolo 22 (tipo ricostruzione) del del TAPL libro , Tipi e linguaggi di programmazione , e il corrispondente OCaml implementazioni di ricognizione e fullrecon .
  5. Capitolo 13 (Type Reconstruction) del nuovo libro DCPL , Design Concepts in Programming Languages .
  6. Selezione di articoli accademici .
  7. TypeInference del compilatore di chiusura è un esempio dell'approccio dell'analisi del flusso di dati all'inferenza del tipo, che è più adatto ai linguaggi dinamici che l'Hindler Milner si avvicina.

Tuttavia, poiché il modo migliore per imparare è fare, suggerisco caldamente di implementare l'inferenza del tipo per un linguaggio funzionale giocattolo lavorando attraverso un compito a casa di un corso di linguaggi di programmazione.

Consiglio questi due compiti a casa accessibili in ML, che puoi completare entrambi in meno di un giorno:

  1. Interprete PCF ( una soluzione ) per riscaldarsi.
  2. PCF Type Inference ( una soluzione ) per implementare l'algoritmo W per l'inferenza di tipo Hindley-Milner.

Questi compiti provengono da un corso più avanzato:

  1. Implementazione di MiniML

  2. Tipi polimorfici, esistenziali, ricorsivi (PDF)

  3. Controllo del tipo bidirezionale (PDF)

  4. Sottotipazione e oggetti (PDF)


2
Sono solo io o la descrizione PLAI è in gran parte errata / incompleta? La lezione aggiunge qualcosa in più, ma apparentemente non abbastanza per farlo funzionare.
Rei Miyasaka

Inoltre, non sono riuscito a ottenere l'algoritmo nella versione 2012 del libro PLAI. Non ci sono sostituzioni all'elenco dei vincoli. Invece, ho implementato l'algoritmo di inferenza del tipo nella versione 2003-7 del libro PLAI, sembra funzionare meglio e si adatta bene anche al polimorfismo let.
heykell

28

È un peccato che gran parte della letteratura sull'argomento sia molto densa. Anch'io ero nei tuoi panni. Ho avuto la mia prima introduzione all'argomento da Linguaggi di programmazione: applicazioni e interpretazione

http://www.plai.org/

Cercherò di riassumere l'idea astratta seguita da dettagli che non ho trovato subito evidenti. In primo luogo, si può pensare che l'inferenza di tipo generi e quindi risolva i vincoli. Per generare vincoli, si ricorre all'albero della sintassi e si generano uno o più vincoli su ogni nodo. Ad esempio, se il nodo è un +operatore, gli operandi e i risultati devono essere tutti numeri. Un nodo che applica una funzione ha lo stesso tipo del risultato della funzione e così via.

Per una lingua senza let, puoi risolvere ciecamente i vincoli di cui sopra per sostituzione. Per esempio:

(if (= 1 2) 
    1 
    2)

qui, possiamo dire che la condizione dell'istruzione if deve essere booleana e che il tipo dell'istruzione if è lo stesso del tipo delle sue clausole thene else. Poiché conosciamo 1e 2siamo numeri, per sostituzione sappiamo che l' ifaffermazione è un numero.

Dove le cose si mettono male, e quello che non ho potuto capire per un po ', è avere a che fare:

(let ((id (lambda (x) x)))
    (id id))

Qui, ci siamo associati ida una funzione che restituisce tutto ciò che hai passato, altrimenti nota come funzione di identità. Il problema è che il tipo di parametro della funzione xè diverso a ogni utilizzo di id. Il secondo idè una funzione del tipo a -> a, dove apuò essere qualsiasi cosa. Il primo è di tipo (a -> a) -> (a -> a). Questo è noto come let-polimorfismo. La chiave è risolvere i vincoli in un ordine particolare: prima risolvere i vincoli per la definizione di id. Questo sarà a -> a. Quindi copie fresche e separate del tipo di idpossono essere sostituite nei vincoli per ogni luogo idviene utilizzato, ad esempio a2 -> a2e a3 -> a3.

Ciò non è stato prontamente spiegato nelle risorse online. Menzioneranno l'algoritmo W o M ma non il modo in cui funzionano in termini di risoluzione dei vincoli, o il motivo per cui non si applica al polimorfismo letale: ciascuno di quegli algoritmi impone un ordine sulla risoluzione dei vincoli.

Ho trovato questa risorsa estremamente utile per collegare l'algoritmo W, M e il concetto generale di generazione e risoluzione dei vincoli tutti insieme. È un po 'denso, ma migliore di molti:

http://www.cs.uu.nl/research/techreps/repo/CS-2002/2002-031.pdf

Anche molti altri giornali sono carini:

http://people.cs.uu.nl/bastiaan/papers.html

Spero che questo aiuti a chiarire un mondo un po 'oscuro.


7

Oltre a Hindley Milner per i linguaggi funzionali, un altro approccio popolare all'inferenza del tipo per il linguaggio dinamico è abstract interpretation.

L'idea dell'interpretazione astratta è quella di scrivere un interprete speciale per la lingua, invece di mantenere un ambiente di valori concreti (1, falso, chiusura), funziona su valori astratti, alias tipi (int, bool, ecc.). Dato che interpreta il programma su valori astratti, ecco perché si chiama interpretazione astratta.

Pysonar2 è un'elegante implementazione dell'interpretazione astratta per Python. Viene utilizzato da Google per analizzare i progetti Python. Fondamentalmente utilizza visitor patternper inviare la chiamata di valutazione al nodo AST pertinente. La funzione visitatore transform accetta il contextnodo in cui verrà valutato e restituisce il tipo di nodo corrente.



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.