Linguaggio che può garantire staticamente che i campi di una mappa siano presenti

6

Se i dati sono semplici e gli oggetti sono complessi , sono curioso di sapere se esistono lingue digitate che sarebbero in grado di aumentare (?) un tipo di mappa in un tipo con campi garantiti. Mi rendo conto che in fase di esecuzione, in qualsiasi lingua questo tipo di controllo è abbastanza fattibile. Ma in teoria credo anche che questo dovrebbe essere possibile verificare con un compilatore. Fare tutto quanto in basso in Java, sarebbe un'esplosione di interfacce (HasComplete, HasTitle, HasTitleAndComplete)

Quindi qualcuno l'ha fatto? Se no, come mai? Se sì, come è stato implementato?

Questo ti permetterebbe di leggere i dati ma poi di manipolarli con almeno alcuni tipi di sicurezza:

// User input is often simply converted to a Map
function parse(json) -> Map {...} 

// result still has all user input fields, 
// but this would be a runtime exception 
// if specified fields are missing
function cast(Map, Map<fields...>) -> Map<fields...> { ... }

input = "{title:'do it', complete: false, rank: 5, subtitle: 'now'}"

// Would throw class cast exception if complete or title were missing
todo = cast(parse(input), Map<title:str, complete:bool>)     

// Runtime introspection
todo instanceOf Map -> true
todo instanceOf Map<title:str, complete:bool> -> true
todo instanceOf Map<title:str> -> true
todo instanceOf Map<complete:bool> -> true
todo instanceOf Map<complete:date> -> false
todo instanceOf Map<rank:int> -> false because cast() did not specify rank
todo instanceOf Map<title:str, complete:bool, author:str> -> false 

// Typed methods based on map with certain fields
function capitalize_title(Map<title:str>) {...}
function finish_task(Map<complete:bool>) {...}
function finish_and_rename(Map<title:str, complete:bool>, newTitle) {...}
function increase_rank(Map<rank:int>) {...}

// Static Check compile pass
todo = capitalize_title(todo)
todo = finish_task(todo)
todo = finish_and_rename(todo, "[complete]")

// Static Check Compile Fail because cast() did not specify rank
increase_rank(todo)

// Methods could return a type with added (or removed) fields
function add_uuid(Map) -> Map<+id:int> {...}
todo_with_id = add_uuid(todo)
todo_with_id instanceOf Map<title:str, complete:bool, id:int> // true
    
posta case nelson 26.04.2012 - 07:38
fonte

2 risposte

5

Ci sono le cosiddette lingue tipizzate in modo dipendente (Agda, Coq ...). Sono molto potenti e consentono di esprimere molte informazioni nel sistema di tipi. L'esempio classico è il tipo per una lista ordinata, che consente funzioni come (nella sintassi concreta immaginaria):

sort :: List of a -> Sorted List of a

Ovviamente ciò significa che devi convincere il tuo compilatore, che il tuo algoritmo di ordinamento è corretto (e termina) e devi definire cosa significa ordinato.

Un po 'meno è espressivo in linguaggi come Haskell (forse Scala, se stai cercando un linguaggio eseguibile JVM). È possibile definire un tipo di elenco che includa quella lunghezza dell'elenco nel tipo. Un'applicazione ovvia è la scrittura di una funzione che recupera il primo elemento di una lista che non può essere chiamata accidentalmente con una lista vuota. Tuttavia, questo non è ciò a cui Haskell è destinato e viene fornito con un prezzo. Solitamente gli svantaggi (le strabilianti definizioni del tipo di ordine superiore e così via) superano i benefici se il linguaggio non è progettato per gestire queste cose.

Questo si estende alle mappe. Ma l'analisi dei dati JSON potrebbe ancora produrre mappe che non sono definite per i campi che desideri. Non esiste alcun controllo su IO per il compilatore / controllo del tipo. In questo modo dovresti trovare i valori predefiniti o modificare il tipo di funzione di analisi per ottenere un valore facoltativo, rendendolo efficiente una funzione parziale. Quindi dovresti disambigarti su questo risultato (il compilatore ti costringerebbe a - cosa buona). Ma questo non è molto diverso dalla definizione di un tipo in un sistema classico (ad es. Java) che per contratto richiede che i campi siano presenti e ne verifica il runtime. La versione classica ovviamente non è modulare, l'hai visto tu stesso.

    
risposta data 26.04.2012 - 13:39
fonte
1

In teoria, un oggetto è solo una mappa statica con controllo del tipo, il che significa che qualsiasi linguaggio OOP offre un tipo di controllo statico per i campi mappa. Tuttavia, penso che ciò che chiedi sia impossibile.

Il tuo esempio di codice è, in breve, 1. Deserialize JSON in una mappa 2. Verifica statica che la mappa sia "corretta". Poiché non è possibile deserializzare JSON fino a quando non si esegue, non è possibile verificarne la struttura fino al runtime. Anche se runtime crea un oggetto (o anche una "mappa strongmente tipizzata") usando il reflection, lo stai ancora verificando in fase di esecuzione.

Un approccio più pratico a questo rispetto alla tipizzazione dipendente (non che DT non sia pratico o fantastico, ma non ci sia ancora molto supporto) potrebbe essere la generazione del codice attraverso modelli di qualche tipo. La maggior parte degli ORM fa qualcosa del genere. Leggono in uno schema da un database, quindi generano oggetti basati su tale schema. Lo stesso potrebbe essere applicato a JSON arbitrario per generare oggetti prima della compilazione.

    
risposta data 26.04.2012 - 16:15
fonte

Leggi altre domande sui tag