Risposte:
Qual è la differenza tra una lingua fortemente tipizzata e una lingua tipicamente statica?
Una lingua tipizzata staticamente ha un sistema di tipi che viene verificato in fase di compilazione dall'implementazione (un compilatore o un interprete). Il controllo del tipo rifiuta alcuni programmi e i programmi che superano il controllo di solito hanno delle garanzie; ad esempio, il compilatore garantisce di non utilizzare istruzioni aritmetiche su numeri in virgola mobile.
Non esiste un vero accordo sul significato di "fortemente tipizzato", sebbene la definizione più ampiamente utilizzata nella letteratura professionale sia che in un linguaggio "fortemente tipizzato" non è possibile per il programmatore aggirare le restrizioni imposte dal sistema di tipi . Questo termine è quasi sempre usato per descrivere lingue tipicamente statiche.
L'opposto di un tipo statico è "un tipo dinamico", il che significa che
Ad esempio, Lua , una lingua tipizzata in modo dinamico, ha un tipo di stringa, un tipo di numero e un tipo booleano, tra gli altri. In Lua ogni valore appartiene esattamente a un tipo, ma questo non è un requisito per tutte le lingue tipizzate dinamicamente. In Lua, è consentito concatenare due stringhe, ma non è consentito concatenare una stringa e un valore booleano.
L'opposto di "fortemente tipizzato" è "tipizzato debolmente", il che significa che è possibile aggirare il sistema di tipi. C è notoriamente tipizzato debolmente perché qualsiasi tipo di puntatore è convertibile in qualsiasi altro tipo di puntatore semplicemente lanciando. Pascal doveva essere fortemente tipizzato, ma una svista nel design (record di varianti senza tag) ha introdotto una scappatoia nel sistema di tipi, quindi tecnicamente è tipizzato debolmente. Esempi di linguaggi fortemente tipizzati includono CLU, Standard ML e Haskell. ML standard ha infatti subito diverse revisioni per rimuovere le lacune nel sistema di tipi che sono state scoperte dopo che la lingua è stata ampiamente diffusa.
Nel complesso, risulta essere poco utile parlare di "forte" e "debole". Se un sistema di tipo ha una scappatoia è meno importante del numero esatto e della natura delle scappatoie, quanto è probabile che si verifichino nella pratica e quali sono le conseguenze dello sfruttamento di una scappatoia. In pratica, è meglio evitare i termini "forte" e "debole" del tutto , perché
I dilettanti spesso li confondono con "statico" e "dinamico".
Apparentemente la "tipizzazione debole" è usata da alcune persone per parlare della prevalenza relativa o dell'assenza di conversioni implicite.
I professionisti non possono concordare esattamente cosa significano i termini.
Nel complesso, è improbabile che tu informi o illumini il tuo pubblico.
La triste verità è che quando si tratta di sistemi di tipo, "forte" e "debole" non hanno un significato tecnico universalmente concordato. Se si desidera discutere della forza relativa dei sistemi di tipo, è meglio discutere esattamente quali garanzie sono e non vengono fornite. Ad esempio, una buona domanda da porsi è questa: "è garantito che ogni valore di un dato tipo (o classe) sia stato creato chiamando uno dei costruttori di quel tipo?" In C la risposta è no. In CLU, F # e Haskell è sì. Per C ++ non ne sono sicuro, vorrei saperlo.
Al contrario, la digitazione statica significa che i programmi vengono controllati prima di essere eseguiti e che un programma potrebbe essere rifiutato prima di avviarsi. La digitazione dinamica significa che i tipi di valori vengono controllati durante l' esecuzione e un'operazione tipizzata male può causare l'interruzione del programma o la segnalazione di un errore in fase di esecuzione. Un motivo principale per la tipizzazione statica è di escludere i programmi che potrebbero avere tali "errori di tipo dinamico".
L'uno implica l'altro?
A livello pedante, no, perché la parola "forte" in realtà non significa nulla. Ma in pratica, le persone fanno quasi sempre una delle due cose:
Usano (erroneamente) "forte" e "debole" per indicare "statico" e "dinamico", nel qual caso (erroneamente) usano "fortemente tipizzato" e "tipicamente statico" in modo intercambiabile.
Usano "forte" e "debole" per confrontare le proprietà dei sistemi di tipo statico. È molto raro sentire qualcuno parlare di un sistema di tipo dinamico "forte" o "debole". Ad eccezione di FORTH, che in realtà non ha alcun tipo di sistema di tipi, non riesco a pensare a un linguaggio tipizzato in modo dinamico in cui il sistema di tipi possa essere sovvertito. Ordinati per definizione, questi controlli sono inseriti nel motore di esecuzione e ogni operazione viene verificata per ragioni di integrità prima di essere eseguita.
Ad ogni modo, se una persona chiama una lingua "fortemente tipizzata", è molto probabile che quella persona stia parlando di una lingua tipizzata staticamente.
Questo è spesso frainteso, quindi lasciatemi chiarire.
La tipizzazione statica è dove il tipo è associato alla variabile . I tipi vengono controllati in fase di compilazione.
La tipizzazione dinamica è dove il tipo è associato al valore . I tipi vengono controllati in fase di esecuzione.
Quindi in Java per esempio:
String s = "abcd";
s
sarà "per sempre" a String
. Durante la sua vita può indicare diverse String
s (poiché s
è un riferimento in Java). Potrebbe avere un null
valore ma non farà mai riferimento a a Integer
o a List
. Questa è la tipizzazione statica.
In PHP:
$s = "abcd"; // $s is a string
$s = 123; // $s is now an integer
$s = array(1, 2, 3); // $s is now an array
$s = new DOMDocument; // $s is an instance of the DOMDocument class
Questa è una digitazione dinamica.
(Modifica avviso!)
La tipizzazione forte è una frase senza un significato ampiamente concordato. La maggior parte dei programmatori che usano questo termine per indicare qualcosa di diverso dalla tipizzazione statica lo usano per implicare che esiste una disciplina di tipo che viene applicata dal compilatore. Ad esempio, CLU ha un sistema di tipo forte che non consente al codice client di creare un valore di tipo astratto se non utilizzando i costruttori forniti dal tipo. C ha un sistema di tipi piuttosto forte, ma può essere "sovvertito" in una certa misura perché un programma può sempre trasmettere un valore di un tipo di puntatore a un valore di un altro tipo di puntatore. Quindi, ad esempio, in C puoi prendere un valore restituito malloc()
e gettarlo allegramente in FILE*
, e il compilatore non cercherà di fermarti, o addirittura avvertirti che stai facendo qualcosa di pericoloso.
(La risposta originale diceva qualcosa su un valore "che non cambiava il tipo in fase di esecuzione". Ho conosciuto molti progettisti di lingue e scrittori di compilatori e non ne ho conosciuto uno che parlava di valori che cambiano tipo in fase di esecuzione, tranne forse qualche ricerca molto avanzata nel tipo sistemi, in cui questo è noto come "forte problema di aggiornamento".)
La digitazione debole implica che il compilatore non impone una discline di battitura o che l'applicazione può essere facilmente sovvertita.
L'originale di questa risposta combinava la digitazione debole con la conversione implicita (talvolta chiamata anche "promozione implicita"). Ad esempio, in Java:
String s = "abc" + 123; // "abc123";
Questo codice è un esempio di promozione implicita: 123 viene convertito implicitamente in una stringa prima di essere concatenato "abc"
. Si può sostenere che il compilatore Java riscriva quel codice come:
String s = "abc" + new Integer(123).toString();
Considera un classico problema "inizia con" di PHP:
if (strpos('abcdef', 'abc') == false) {
// not found
}
L'errore qui è che strpos()
restituisce l'indice della corrispondenza, essendo 0. 0 viene forzato in booleano false
e quindi la condizione è effettivamente vera. La soluzione è utilizzare ===
invece di ==
evitare la conversione implicita.
Questo esempio mostra come una combinazione di conversione implicita e tipizzazione dinamica può portare fuori strada i programmatori.
Confronta quello con Ruby:
val = "abc" + 123
che è un errore di runtime perché in Ruby l' oggetto 123 non viene convertito implicitamente solo perché sembra essere passato a un +
metodo. In Ruby il programmatore deve rendere esplicita la conversione:
val = "abc" + 123.to_s
Il confronto tra PHP e Ruby è una buona illustrazione qui. Entrambi sono linguaggi tipizzati dinamicamente, ma PHP ha molte conversioni implicite e Ruby (forse sorprendentemente se non si ha familiarità con esso) no.
Il punto qui è che l'asse statico / dinamico è indipendente dall'asse forte / debole. Le persone li confondono probabilmente in parte perché la tipizzazione forte contro debole non è solo meno chiaramente definita, non esiste un vero consenso su cosa si intenda per forte e debole. Per questo motivo la tipizzazione forte / debole è molto più di una sfumatura di grigio piuttosto che nera o bianca.
Quindi, per rispondere alla tua domanda: un altro modo di vedere ciò che è per lo più corretto è quello di dire che la tipizzazione statica è la sicurezza del tipo in fase di compilazione e la tipizzazione forte è la sicurezza del tipo di runtime.
La ragione di ciò è che le variabili in una lingua tipizzata staticamente hanno un tipo che deve essere dichiarato e che può essere verificato in fase di compilazione. Un linguaggio fortemente tipizzato ha valori che hanno un tipo in fase di esecuzione ed è difficile per il programmatore sovvertire il sistema di tipi senza un controllo dinamico.
Ma è importante capire che un linguaggio può essere statico / forte, statico / debole, dinamico / forte o dinamico / debole.
"abc" + 123
è un errore di runtime , non un errore di compilazione in ruby. Se si trattasse di un errore di compilazione, il rubino verrebbe digitato staticamente.
Entrambi sono poli su due assi diversi:
Significa fortemente tipizzato , a non verrà convertito automaticamente da un tipo a un altro. Digitato debolmente è l'opposto: Perl può usare una stringa come "123"
in un contesto numerico, convertendolo automaticamente in int 123
. Un linguaggio fortemente tipizzato come Python non lo farà.
Significato tipicamente significa, il compilatore capisce il tipo di ogni variabile al momento della compilazione. I linguaggi tipizzati dinamicamente determinano solo i tipi di variabili in fase di esecuzione.
Caratterizzato in modo forte significa che ci sono restrizioni tra le conversioni tra tipi. Digitato staticamente significa che i tipi non sono dinamici: non è possibile modificare il tipo di una variabile dopo che è stata creata.
Coercizione dei dati non significa necessariamente tipicamente debolmente perché a volte il suo zucchero sintattico:
L'esempio sopra di Java viene debolmente digitato a causa di
String s = "abc" + 123;
Non è un esempio debolmente tipizzato perché sta davvero facendo:
String s = "abc" + new Integer(123).toString()
Anche la coercizione dei dati non viene tipizzata debolmente se si sta costruendo un nuovo oggetto. Java è un pessimo esempio di digitazione debole (e qualsiasi linguaggio che abbia una buona riflessione probabilmente non verrà digitato debolmente). Perché il runtime della lingua sa sempre qual è il tipo (l'eccezione potrebbe essere i tipi nativi).
Questo è diverso da C. C è uno dei migliori esempi di caratteri deboli. Il runtime non ha idea se 4 byte sono un numero intero, uno struct, un puntatore o un 4 caratteri.
Il runtime del linguaggio definisce davvero se è tipicamente debolmente scritto altrimenti è davvero solo un'opinione.
EDIT: dopo ulteriori considerazioni questo non è necessariamente vero in quanto il runtime non deve avere tutti i tipi reificati nel sistema di runtime per essere un sistema fortemente tipizzato. Haskell e ML hanno un'analisi statica così completa che possono potenzialmente ottenere informazioni sul tipo di ommit dal runtime.
La risposta è già stata data sopra. Cercando di distinguere tra concetto forte vs settimana e concetto statico vs dinamico.
Caratterizzato fortemente: non verrà convertito automaticamente da un tipo a un altro
In Go o Python come i linguaggi fortemente tipizzati "2" + 8 genererà un errore di tipo, perché non consentono la "coercizione di tipo".
Digitato debolmente (liberamente): verrà automaticamente convertito in un tipo in un altro: linguaggi debolmente digitati come JavaScript o Perl non genereranno un errore e in questo caso JavaScript genererà '28' e perl risulterà 10.
Esempio Perl:
my $a = "2" + 8;
print $a,"\n";
Salvalo su main.pl ed esegui perl main.pl
e otterrai l'output 10.
Nella programmazione, il programmatore definisce la tipizzazione statica e la digitazione dinamica rispetto al punto in cui vengono controllati i tipi di variabili. Le lingue tipizzate statiche sono quelle in cui viene eseguita la verifica del tipo in fase di compilazione, mentre le lingue tipizzate dinamiche sono quelle in cui viene eseguita la verifica del tipo in fase di esecuzione.
Cosa significa questo?
In Vai controlla digitato prima del runtime (controllo statico). Ciò significa che non solo traduce e verifica il codice che sta eseguendo, ma eseguirà la scansione di tutto il codice e verrebbe generato un errore di tipo prima ancora che il codice venga eseguito. Per esempio,
package main
import "fmt"
func foo(a int) {
if (a > 0) {
fmt.Println("I am feeling lucky (maybe).")
} else {
fmt.Println("2" + 8)
}
}
func main() {
foo(2)
}
Salvare questo file in main.go ed eseguirlo, verrà visualizzato un messaggio di compilazione non riuscita per questo.
go run main.go
# command-line-arguments
./main.go:9:25: cannot convert "2" (type untyped string) to type int
./main.go:9:25: invalid operation: "2" + 8 (mismatched types string and int)
Ma questo caso non è valido per Python. Ad esempio, il seguente blocco di codice verrà eseguito per la prima chiamata foo (2) e non riuscirà per la seconda chiamata foo (0). È perché Python è digitato in modo dinamico, traduce e verifica solo il codice su cui è in esecuzione. Il blocco else non viene mai eseguito per foo (2), quindi "2" + 8 non viene mai nemmeno guardato e per foo (0) call tenterà di eseguire quel blocco e fallirà.
def foo(a):
if a > 0:
print 'I am feeling lucky.'
else:
print "2" + 8
foo(2)
foo(0)
Vedrai il seguente output
python main.py
I am feeling lucky.
Traceback (most recent call last):
File "pyth.py", line 7, in <module>
foo(0)
File "pyth.py", line 5, in foo
print "2" + 8
TypeError: cannot concatenate 'str' and 'int' objects
Uno non implica l'altro. Per una lingua da digitare staticamente significa che i tipi di tutte le variabili sono conosciuti o dedotti al momento della compilazione.
Un linguaggio fortemente tipizzato non ti consente di utilizzare un tipo come un altro. C è una lingua tipicamente debolmente ed è un buon esempio di ciò che le lingue fortemente tipizzate non consentono. In C puoi passare un elemento di dati del tipo sbagliato e non ti lamenterà. In lingue fortemente tipizzate non puoi.
La tipizzazione forte significa probabilmente che le variabili hanno un tipo ben definito e che esistono regole rigide sulla combinazione di variabili di tipi diversi nelle espressioni. Ad esempio, se A è un numero intero e B è un float, la regola rigida su A + B potrebbe essere che A viene trasmesso a un float e il risultato restituito come float. Se A è un numero intero e B è una stringa, la regola rigida potrebbe essere che A + B non è valido.
La tipizzazione statica probabilmente significa che i tipi sono assegnati al momento della compilazione (o il suo equivalente per i linguaggi non compilati) e non possono cambiare durante l'esecuzione del programma.
Nota che queste classificazioni non si escludono a vicenda, anzi mi aspetto che si verifichino frequentemente insieme. Molte lingue fortemente tipizzate sono anche tipizzate staticamente.
E nota che quando uso la parola "probabilmente" è perché non ci sono definizioni universalmente accettate di questi termini. Come avrai già visto dalle risposte finora.