Perché le lingue tipizzate dinamicamente non consentono allo sviluppatore di specificare il tipo?


14

I linguaggi tipicamente dinamici che conosco non consentono mai agli sviluppatori di specificare i tipi di variabili, o almeno hanno un supporto molto limitato per questo.

JavaScript, ad esempio, non fornisce alcun meccanismo per imporre tipi di variabili quando è conveniente farlo. PHP permette di specificare alcuni tipi di argomenti del metodo, ma non c'è modo di usare tipi nativi ( int, string, ecc) per argomenti, e non c'è modo di far rispettare i tipi per qualcosa di diverso argomenti.

Allo stesso tempo, sarebbe conveniente avere la possibilità di specificare in alcuni casi il tipo di una variabile in una lingua tipizzata in modo dinamico, invece di fare il controllo del tipo manualmente.

Perché esiste una tale limitazione? È per motivi tecnici / prestazionali (suppongo che sia nel caso di JavaScript) o solo per motivi politici (che è, credo, il caso di PHP)? È un caso per altre lingue tipicamente dinamiche che non conosco?


Modifica: seguendo le risposte e i commenti, ecco un esempio per un chiarimento: diciamo che abbiamo il seguente metodo in semplice PHP:

public function CreateProduct($name, $description, $price, $quantity)
{
    // Check the arguments.
    if (!is_string($name)) throw new Exception('The name argument is expected to be a string.');
    if (!is_string($description)) throw new Exception('The description argument is expected to be a string.');
    if (!is_float($price) || is_double($price)) throw new Exception('The price argument is expected to be a float or a double.');
    if (!is_int($quantity)) throw new Exception('The quantity argument is expected to be an integer.');

    if (!$name) throw new Exception('The name argument cannot be an empty string.');
    if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
    if ($price < 0) throw new Exception('The price argument cannot be less than zero.');

    // We can finally begin to write the actual code.
    // TODO: Implement the method here.
}

Con alcuni sforzi, questo può essere riscritto come (vedi anche Programmazione per contratti in PHP ):

public function CreateProduct($name, $description, $price, $quantity)
{
    Component::CheckArguments(__FILE__, __LINE__, array(
        'name' => array('value' => $name, 'type' => VTYPE_STRING),
        'description' => array('value' => $description, 'type' => VTYPE_STRING),
        'price' => array('value' => $price, 'type' => VTYPE_FLOAT_OR_DOUBLE),
        'quantity' => array('value' => $quantity, 'type' => VTYPE_INT)
    ));

    if (!$name) throw new Exception('The name argument cannot be an empty string.');
    if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
    if ($price < 0) throw new Exception('The price argument cannot be less than zero.');

    // We can finally begin to write the actual code.
    // TODO: Implement the method here.
}

Ma lo stesso metodo verrebbe scritto come segue se PHP accettasse facoltativamente tipi nativi per argomenti:

public function CreateProduct(string $name, string $description, double $price, int $quantity)
{
    // Check the arguments.
    if (!$name) throw new Exception('The name argument cannot be an empty string.');
    if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
    if ($price < 0) throw new Exception('The price argument cannot be less than zero.');

    // We can finally begin to write the actual code.
    // TODO: Implement the method here.
}

Quale è più breve da scrivere? Quale è più facile da leggere?


1
Puoi facoltativamente specificare i tipi in alcune lingue tipizzate dinamicamente, ad esempio in Common Lisp.
SK-logic

Molte lingue tipizzate dinamicamente usano i cast per forzare un tipo ...
Trezoid,

Alcuni lo fanno. Objective-C, ad esempio, viene digitato in modo dinamico, ma puoi dichiarare un tipo per le variabili e il compilatore emetterà avvisi se non ottieni il tipo che ti aspetti.
mipadi,

1
Clojure è un esempio di un linguaggio che viene tipicamente digitato in modo dinamico ma puoi facoltativamente fornire tipi di variabili tramite "suggerimenti per il tipo" (questo di solito viene fatto solo dove necessario per ottenere i vantaggi in termini di prestazioni delle informazioni sul tipo di tempo di compilazione)
mikera

1
Groovy è un altro esempio di un linguaggio tipizzato in modo dinamico che consente di specificare un tipo.
Eric Wilson,

Risposte:


17

Il punto di avere la tipizzazione statica è la capacità di dimostrare staticamente che il tuo programma è corretto per quanto riguarda i tipi (nota: non completamente corretto in tutti i sensi). Se si dispone di un sistema di tipo statico, è possibile rilevare errori di tipo per la maggior parte del tempo.

Se disponi solo di informazioni parziali sul tipo, puoi controllare solo le piccole parti di un grafico di chiamata in cui le informazioni sul tipo risultano complete. Ma hai speso tempo e fatica a specificare le informazioni sul tipo per parti incomplete, dove non possono aiutarti ma dare un falso senso di sicurezza.

Per esprimere informazioni sul tipo, è necessaria una parte del linguaggio che non può essere eccessivamente semplice. Presto scoprirai che informazioni come intnon sono sufficienti; vorrai qualcosa di simile List<Pair<Int, String>>, quindi tipi parametrici, ecc. Può essere abbastanza confuso anche nel caso piuttosto semplice di Java.

Quindi, dovrai gestire queste informazioni durante la fase di traduzione e di esecuzione, perché è sciocco controllare solo gli errori statici; l'utente si aspetterà che i vincoli di tipo siano sempre validi se specificati affatto. I linguaggi dinamici non sono troppo veloci come sono e tali controlli rallenteranno ulteriormente le prestazioni. Un linguaggio statico può impegnarsi seriamente a controllare i tipi perché lo fa solo una volta; un linguaggio dinamico non può.

Ora immagina di aggiungere e mantenere tutto questo solo in modo che le persone a volte abbiano facoltativamente usato queste funzionalità, rilevando solo una piccola parte degli errori di tipo. Non credo valga la pena.

Il punto centrale dei linguaggi dinamici è avere un framework molto piccolo e molto malleabile, all'interno del quale puoi facilmente fare cose che sono molto più coinvolte quando eseguite in un linguaggio statico: varie forme di patching delle scimmie che vengono utilizzate per metaprogrammazione, derisione e test, sostituzione dinamica del codice, ecc. Smalltalk e Lisp, entrambi molto dinamici, lo hanno portato ad un punto estremo tale da spedire immagini di ambiente invece di costruire dalla fonte. Ma quando vuoi assicurarti che determinati percorsi di dati siano sicuri per i tipi, aggiungi affermazioni e scrivi più unit test.


1
+1, anche se i test possono solo dimostrare che in determinate situazioni non si verificano errori. Sono una sostituzione scadente per una prova che gli errori (di tipo) sono impossibili.
Ingo,

1
@Ingo: sicuramente. Ma i linguaggi dinamici sono ottimi per armeggiare e prototipare rapidamente, dove esprimi idee relativamente semplici molto velocemente. Se si desidera un codice di produzione antiproiettile, è possibile passare a un linguaggio statico in seguito, dopo aver estratto alcuni componenti core stabili.
9000

1
@ 9000, non dubito che siano fantastici. Volevo solo sottolineare che la scrittura di 3 o 4 test zoppi non lo è, e non può garantire la sicurezza del tipo .
Ingo,

2
@ 9000, Vero, e la cattiva notizia è che anche allora, è praticamente impossibile. Anche il codice Haskell o Agda si basa su ipotesi, come, ad esempio, che la libreria utilizzata nel runtime sia corretta. Detto questo, in un progetto con circa 1000 LOC distribuiti su una dozzina di file di codice sorgente, è così bello quando puoi cambiare qualcosa e sai che il compilatore punterà su ogni riga in cui la modifica ha un impatto.
Ingo,

4
Test scritti male non sostituiscono il controllo statico del tipo: sono inferiori. Anche i test ben scritti non sostituiscono il controllo statico del tipo: sono superiori.
Rein Henrichs,

8

Nella maggior parte dei linguaggi dinamici, puoi almeno testare dinamicamente il tipo di un oggetto o valore.

E ci sono inferenziatori di tipo statico, pedine e / o esecutori per alcuni linguaggi dinamici: ad es

E Perl 6 supporterà un sistema di tipi opzionale con tipizzazione statica.


Ma suppongo che la linea di fondo sia che molte persone usano i linguaggi in modo dinamico perché sono tipizzati in modo dinamico, e per loro la tipizzazione statica opzionale è molto "ho hum". E molte altre persone li usano perché sono "facili da usare per i non programmatori", in gran parte come conseguenza della digitazione dinamica della natura perdona. Per loro, la digitazione opzionale è qualcosa che o non capiranno o non saranno disturbati da usare.

Se fossi cinico, potresti dire che la tipizzazione statica opzionale offre il peggio di entrambi i mondi. Per un fanatico di tipo statico, non impedisce tutti gli errori di tipo dinamico. Per un fan di tipo dinamico, è ancora una giacca dritta ... anche se le cinghie non sono ben strette.


2
Va notato che il controllo dei tipi da soli è disapprovato dalla maggior parte delle comunità nella maggior parte dei casi. Usa il polimorfismo (in particolare la "tipizzazione delle anatre") quando hai a che fare con le gerarchie di oggetti, se possibile / sensato costringi al tipo previsto. Rimangono alcuni casi in cui non ha senso consentire qualsiasi tipo, ma in molte lingue si ottiene comunque un'eccezione nella maggior parte di questi casi, quindi il controllo del tipo è raramente utile.

4
"le persone in genere usano le lingue in modo dinamico perché sono digitate in modo dinamico" : viene utilizzato JavaScript perché è l'unica lingua supportata dalla maggior parte dei browser. PHP è usato perché è popolare.
Arseni Mourzenko,

2

Javascript aveva in programma di includere una tipizzazione statica opzionale e sembra che molti linguaggi dinamici maturi stiano andando in quel modo-

Il motivo è che al primo codice, vuoi essere veloce e digitato in modo dinamico. Una volta che il codice è solido, funzionante e ha molti usi (r), vuoi bloccare il design per ridurre gli errori. (questo è vantaggioso sia per gli utenti che per gli sviluppatori, in quanto il primo riceverà un errore durante il controllo delle chiamate e il secondo non interromperà accidentalmente le cose.

Per me ha un senso, dal momento che di solito trovo troppo controllo dei tipi all'inizio di un progetto, troppo poco alla fine della sua vita, indipendentemente dalla lingua che uso;).


Non conosco questi piani per includere la digitazione statica opzionale in JavaScript; ma spero che non fossero così atroci come quelli in ActiveScript. il peggio di JavaScript e Java.
Javier,

Era previsto per JS 4 (o ECMAscript 4) ma quella versione è stata abbandonata a causa di polemiche. Sono sicuro che qualcosa di simile apparirà in futuro, in qualche lingua. (In Python puoi farlo con i decoratori, tra l'altro.)
Macke

1
I decoratori aggiungono il controllo dinamico del tipo, una sorta di affermazioni. Non è possibile ottenere un controllo statico completo in Python, per quanto ci si provi, a causa dell'estremo dinamismo del linguaggio.
9000,

@ 9000: è corretto. Tuttavia, non penso che il controllo dinamico del tipo sia negativo (ma preferirei confronti di tipo anatra ala JS4), specialmente se combinato con test unitari e la tipizzazione dei decoratori potrebbe essere più utile supportare IDE / controllori di lanugine se fossero dove standardizzato.
Macke,

ovviamente non è male! Questa non è una questione di moralità. Ad un certo punto, il tipo deve essere "controllato" in un modo o nell'altro. Se scrivi * ((double *) 0x98765E) in C, la CPU lo farà e controllerà se 0x98765E è effettivamente un puntatore a un doppio.
Ingo,

2

Oggetti Python fare avere un tipo.

Si specifica il tipo quando si crea l'oggetto.

Allo stesso tempo, sarebbe conveniente avere la possibilità di specificare in alcuni casi il tipo di una variabile in una lingua tipizzata in modo dinamico, invece di fare il controllo del tipo manualmente.

In realtà, un controllo manuale del tipo in Python è quasi sempre una perdita di tempo e codice.

È semplicemente una cattiva pratica scrivere codice di controllo del tipo in Python.

Se un tipo inappropriato è stato utilizzato da un sociopatico dannoso, i metodi ordinari di Python solleveranno un'eccezione ordinaria quando il tipo non è appropriato.

Non scrivi codice, il tuo programma non riesce ancora con a TypeError .

Ci sono casi molto rari in cui è necessario determinare il tipo in fase di esecuzione.

Perché esiste una tale limitazione?

Dal momento che non è una "limitazione", la domanda non è una vera domanda.


2
"Gli oggetti Python hanno un tipo." - ohh davvero? Proprio come gli oggetti perl, gli oggetti PHP e ogni altro elemento di dati nel mondo. La differenza tra la tipizzazione statica e dinamica è solo quando il tipo verrà controllato, ovvero quando si manifestano errori di tipo. Se appaiono come errori del compilatore, è una digitazione statica, se appaiono come errori di runtime, è dinamico.
Ingo,

@Ingo: grazie per il chiarimento. Il problema è che gli oggetti C ++ e Java possono essere trasmessi da un tipo all'altro, rendendo il tipo di oggetto un po 'oscuro e quindi rendendo un po' oscuro anche il "controllo del tipo" in quei compilatori. Dove il controllo del tipo di Python - anche se è in fase di esecuzione - è molto meno oscuro. Inoltre, la domanda si avvicina quasi a dire che le lingue tipizzate in modo dinamico non hanno tipi. La buona notizia è che non commette questo errore comune.
S.Lott

1
Hai ragione, digita cast (al contrario di conversioni di tipo, ovvero ((doppio) 42)) sovvertire la digitazione statica. Sono necessari quando il sistema di tipi non è abbastanza potente. Prima di Java 5, Java non aveva tipi parametrizzati, non si poteva vivere senza cast di tipi allora. Oggi è molto meglio, eppure al sistema dei tipi mancano ancora tipi di tipo più elevato, per non parlare del polimorfismo di livello superiore. Penso che sia possibile che i linguaggi tipizzati dinamicamente godano di così tanti follower proprio perché ne liberano uno da sistemi di tipo troppo ristretti.
Ingo,

2

Il più delle volte, non è necessario, almeno non a livello di dettaglio che stai suggerendo. In PHP, gli operatori che usi chiariscono perfettamente cosa ti aspetti dagli argomenti; è un po 'una svista di progettazione sebbene PHP lanci i tuoi valori, se possibile, anche quando passi un array a un'operazione che prevede una stringa e, poiché il cast non è sempre significativo, a volte ottieni strani risultati ( e questo è esattamente dove i controlli del tipo sono utili). Oltre a questo, non importa se si aggiunge interi 1e 5o stringhe "1"e"5" - il semplice fatto che si sta utilizzando il+l'operatore segnala a PHP che vuoi trattare gli argomenti come numeri e PHP obbedirà. Una situazione interessante è quando ricevi i risultati delle query da MySQL: molti valori numerici vengono semplicemente restituiti come stringhe, ma non ti accorgerai dal momento che PHP li lancia per te ogni volta che li tratti come numeri.

Python è un po 'più severo nei confronti dei suoi tipi, ma a differenza di PHP, Python ha avuto eccezioni fin dall'inizio e lo utilizza in modo coerente. Il paradigma "più facile chiedere perdono che permesso" suggerisce di eseguire semplicemente l'operazione senza controllo del tipo e di fare affidamento sull'eccezione sollevata quando i tipi non hanno senso. L'unico aspetto negativo di ciò a cui riesco a pensare è che a volte scoprirai che da qualche parte un tipo non corrisponde a quello che ti aspetti, ma trovare il motivo può essere noioso.

E c'è un altro motivo da considerare: i linguaggi dinamici non hanno una fase di compilazione. Anche se si hanno vincoli di tipo, possono attivarsi solo in fase di esecuzione, semplicemente perché non c'è tempo di compilazione . Se i tuoi controlli comportano comunque errori di runtime, è molto più semplice modellarli di conseguenza: come controlli espliciti (come is_XXX()in PHP o typeofin javascript) o generando eccezioni (come fa Python). Funzionalmente, si ha lo stesso effetto (viene segnalato un errore in fase di esecuzione quando un controllo del tipo fallisce), ma si integra meglio con il resto della semantica della lingua. Semplicemente non ha senso trattare errori di tipo fondamentalmente diversi dagli altri errori di runtime in un linguaggio dinamico.


0

Potresti essere interessato a Haskell: il suo sistema di tipi infonde i tipi dal codice e puoi anche specificare i tipi.


5
Haskell è un'ottima lingua. È in qualche modo l'opposto dei linguaggi dinamici: passi molto tempo a descrivere i tipi, e di solito una volta che hai capito i tuoi tipi il programma funziona :)
9000

@ 9000: davvero. Una volta compilato, di solito funziona. :)
Macke,

@Macke - per diversi valori di solito , ovviamente. :-) Per me, il più grande vantaggio del sistema di tipi e del paradigma funzionale è, come ho sottolineato altrove, che non ci si deve preoccupare se una modifica da qualche parte abbia un impatto silenzioso su un codice dipendente altrove - il compilatore indicherà errori di tipo e lo stato mutevole non esiste.
Ingo,

0

Come hanno accennato le altre risposte, ci sono due approcci alla digitazione quando si implementa un linguaggio di programmazione.

  1. Chiedi al programmatore di dirti quali sono le variabili e le funzioni utilizzate dai tipi. Idealmente, verifichi anche che le specifiche del tipo siano accurate. Quindi, poiché sai quale tipo di cosa sarà presente in ogni luogo, puoi scrivere il codice che presume che ci sia la cosa appropriata e utilizzare qualsiasi struttura di dati usi per implementare direttamente quel tipo.
  2. Collegare un indicatore di tipo ai valori che verranno memorizzati in variabili e passati e restituiti da funzioni. Ciò significa che il programmatore non dovrà specificare alcun tipo per variabili o funzioni, poiché i tipi appartengono effettivamente agli oggetti a cui fanno riferimento variabili e funzioni.

Entrambi gli approcci sono validi e quali utilizzare dipendono in parte da considerazioni tecniche come le prestazioni e in parte da ragioni politiche come il mercato di riferimento per la lingua.


0

Innanzitutto i linguaggi dinamici sono stati creati principalmente per la facilità d'uso. Come hai accennato, è davvero utile prendere automaticamente la conversione del tipo e fornirci meno costi generali. Ma allo stesso tempo manca di problemi di prestazioni.

Puoi rimanere fedele ai linguaggi dinamici, nel caso in cui non ti preoccupi della performance. Supponiamo ad esempio che JavaScript sia più lento quando deve eseguire una conversione di molti tipi nel programma, ma aiuta a ridurre il numero di righe nel codice.

E per citare, ci sono anche altri linguaggi dinamici che consentono al programmatore di specificare il tipo. Ad esempio Groovy è uno dei famosi linguaggi dinamici che girano su JVM. Ed è stato molto famoso anche negli ultimi giorni. Si noti che le prestazioni di Groovy sono le stesse di Java.

Spero che ti aiuti.


-1

Semplicemente non ha senso farlo.

Perché?

Perché il sistema di tipi di DTL è esattamente tale che i tipi non possono essere determinati al momento della compilazione. Pertanto, il compilatore non è nemmeno riuscito a verificare che il tipo specificato avrebbe senso.


1
Perché? Ha perfettamente senso suggerire a un compilatore quali tipi aspettarsi. Non contraddirà nessuno dei vincoli di sistema del tipo.
SK-logic

1
SK-logic: se digitato in modo dinamico significa che ogni funzione / metodo / operazione accetta oggetti di tipo "Dinamico", "Qualsiasi" o qualsiasi cosa e restituisce "Dinamico", "Qualsiasi" qualunque, in generale non c'è modo di dire che un certo valore sarà sempre un numero intero, per esempio. Pertanto, il codice di runtime deve comunque verificare la presenza di numeri non interi, proprio come se il tipo fosse "Dinamico" in primo luogo. Questo è esattamente ciò che fa un sistema di tipo statico: rende possibile dimostrare che una certa variabile, campo o metodo restituito sarà sempre di un certo tipo.
Ingo,

@Ingo, no, c'è un modo. Guarda come viene implementato in Common Lisp, ad esempio. È particolarmente utile per le variabili locali: è possibile aumentare notevolmente le prestazioni introducendo tutti quei suggerimenti per la digitazione.
SK-logic

@ SK-logic: forse puoi dirmi quando e come vengono rilevati errori di tipo in CL? Comunque, @MainMa ha riassunto abbastanza bene lo status quo nella sua domanda: è proprio quello che ci si potrebbe aspettare da linguaggi "puramente" dinamici.
Ingo,

@Ingo, cosa ti fa pensare che i tipi siano utili solo per dimostrare staticamente la correttezza? Non è vero nemmeno per le lingue come C, dove hai un casting di tipo non controllato. Le annotazioni dei tipi nei linguaggi dinamici sono utili soprattutto come suggerimenti del compilatore che migliorano le prestazioni o specificano una rappresentazione numerica concreta. Concordo sul fatto che nella maggior parte dei casi le annotazioni non dovrebbero cambiare la semantica del codice.
SK-logic,

-1

Dai un'occhiata a Go, in superficie è tipizzato staticamente, ma quei tipi possono essere interfacce che sono essenzialmente dinamiche.

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.