Sto lavorando su un piccolo compilatore di calcoli lambda che ha un sistema di inferenza di tipo Hindley-Milner funzionante e ora supporta anche un carattere ricorsivo (non nel codice collegato), che comprendo dovrebbe essere sufficiente per renderlo completo .
Il problema ora è che non ho idea di come renderli elenchi di supporto o se li supporta già e ho solo bisogno di trovare un modo per codificarli. Mi piacerebbe essere in grado di definirli senza dover aggiungere nuove regole al sistema di tipi.
Il modo più semplice in cui riesco a pensare a un elenco x
è come qualcosa che è null
(o l'elenco vuoto) o una coppia che contiene sia un x
che un elenco x
. Ma per fare questo devo essere in grado di definire coppie e o, che credo siano il prodotto e i tipi di somma.
Sembra che posso definire le coppie in questo modo:
pair = λabf.fab
first = λp.p(λab.a)
second = λp.p(λab.b)
Dato che pair
avrebbe il tipo a -> (b -> ((a -> (b -> x)) -> x))
, dopo aver passato, diciamo, an int
e a string
, produrrebbe qualcosa con type (int -> (string -> x)) -> x
, che sarebbe la rappresentazione di una coppia di int
e string
. Ciò che mi preoccupa qui è che se ciò rappresenta una coppia, perché ciò non è logicamente equivalente, né implica la proposizione int and string
? Tuttavia, equivale a (((int and string) -> x) -> x)
, come se potessi avere solo tipi di prodotto come parametri per le funzioni. Questa rispostasembra affrontare questo problema, ma non ho idea di cosa significhino i simboli che usa. Inoltre, se questo non codifica realmente un tipo di prodotto, c'è qualcosa che posso fare con i tipi di prodotto che non potrei fare con la mia definizione di coppie sopra (considerando che posso anche definire n-tuple allo stesso modo)? In caso contrario, ciò non contraddirebbe il fatto che non è possibile esprimere la congiunzione (AFAIK) usando solo l'implicazione?
Inoltre, che ne dici del tipo di somma? Posso in qualche modo codificarlo usando solo il tipo di funzione? In tal caso, basterebbe definire le liste? Altrimenti, c'è un altro modo per definire elenchi senza dover estendere il mio sistema di tipi? E se no, quali cambiamenti dovrei fare se voglio mantenerlo il più semplice possibile?
Tieni presente che sono un programmatore di computer ma non uno scienziato informatico né un matematico e piuttosto cattivo nel leggere la notazione matematica.
Modifica: non sono sicuro di quale sia il nome tecnico di ciò che ho implementato finora, ma tutto quello che ho è fondamentalmente il codice che ho collegato sopra, che è un algoritmo di generazione di vincoli che utilizza le regole per applicazioni, astrazioni e variabili prese dall'algoritmo Hinley-Milner e quindi un algoritmo di unificazione che ottiene il tipo principale. Ad esempio, l'espressione \a.a
produrrà il tipo a -> a
e l'espressione \a.(a a)
genererà un errore di verifica si verifica. Inoltre, non esiste esattamente una let
regola ma una funzione che sembra avere lo stesso effetto che consente di definire funzioni globali ricorsive come questo pseudo-codice:
GetTypeOfGlobalFunction(term, globalScope, nameOfFunction)
{
// Here 'globalScope' contains a list of name-value pair where every value is of class 'ClosedType',
// meaning their type will be cloned before unified in the unification algorithm so that they can be used polymorphically
tempType = new TypeVariable() // Assign a dummy type to `tempType`, say, type 'x'.
// The next line creates an scope with everything in 'globalScope' plus the 'nameOfFunction = tempType' name-value pair
tempScope = new Scope(globalScope, nameOfFunction, tempType)
type = TypeOfTerm(term, tempScope) // Calculate the type of the term
Unify(tempType, type)
return type
// After returning, the code outside will create a 'ClosedType' using the returned type and add it to the global scope.
}
Fondamentalmente il codice ottiene il tipo del termine come al solito, ma prima di unificarlo, aggiunge il nome della funzione che viene definita con un tipo fittizio nell'ambito del tipo in modo che possa essere utilizzato dall'interno in modo ricorsivo.
Modifica 2: Mi sono appena reso conto che avrei bisogno anche di tipi ricorsivi, che non ho, per definire un elenco come voglio.
let func = \x -> (func x)
) ottieni quello che ho.