Newsgroup: it.comp.programmare
Titolo: Linguaggi statici e dinamici?

Ciao,
qualcuno potrebbe spiegarmi in modo semplice
qual ï¿œ la differenza tra i linguaggi di programmazione
dinamici e statici.

 dinamici e statici.
Quando il computer esegue un programma scritto in un linguaggio 
statico, il programma si mette lï¿œ, lavora lï¿œ e non si muove. E se lo fa 
lo fa poco.
Quando il computer esegue un programma scritto in un linguaggio 
dinamico, il programma prima va un pï¿œ in giro di qua e di lï¿œ, poi trova 
il posto che gli piace e ci si ferma per lavorare, ma anche allora 
continua a muoversi un pï¿œ a fisarmonica, e comunque molto di piï¿œ di un 
programma scritto con un linguaggio statico.
Piï¿œ semplice di cosï¿œ, nin zo...

 dinamici e statici.
La dinamicita' e la staticita' di un linguaggio sta essenzialmente nei 
tipi delle variabili. Quindi una denominazione appropriata sarebbe 
"linguaggi tipizzati dinamicamente" e "linguaggi tipizzati staticamente".
Prendi il seguente codice:
a = 2 + 2
a = "abc"
Se un linguaggio consente l'esecuzione delle due righe suddette ï¿œ un 
linguaggio tipizzato dinamicamente, in quanto permette di assegnare alla 
stessa variabile ("a") prima un valore numerico e poi un valore 
alfanumerico.
Se invece un linguaggio non consente tale doppia assegnazione, si tratta 
di un linguaggio tipizzato staticamente.
Qualunque fosse lo stato di "a" prima della prima assegnazione, appena 
dopo tale assegnazione, tale variabile rappresenta un valore numerico. 
Appena dopo la seconda assegnazione, tale variabile rappresenta invece 
un valore alfanumerico.
Alcune operazioni hanno senso solamente su valori numerici, come la 
divisione. Altre operazioni hanno senso solamente su valori 
alfanumerici, come la ricerca di sottostringhe.
Appena dopo la prima assegnazione, posso eseguire la seguente istruzione:
b = a / 2
mentre, a meno di uno sforzo di fantasia, non posso eseguire tale 
istruzione dopo la seconda assegnazione.
D'altra parte, dopo la seconda assegnazione, posso eseguire la seguente 
istruzione:
b = estrai(a, 1, 1)
che estrai il primo carattere contenuto in "a".
mentre, a meno di uno sforzo di fantasia, non posso eseguire tale 
istruzione appena dopo la prima assegnazione.
Concettualmente, in un linguaggio dinamico una variabile non ha sempre 
lo stesso tipo.
Un programma scritto in un linguaggio dinamico potrebbe contenere delle 
istruzioni di controllo di flusso (come "if" o "while") dipendenti da 
dati esterni, che fanno si' che non sia possibile prevedere se verra' 
eseguita un'istruzione che assegna a una variabile un valore numerico o 
un valore alfanumerico. Pertanto, in quei casi, finche' il programma non 
e' in esecuzione, non e' possibile asserire quale sara' il tipo della 
variabile in questione. Quindi il tipo di tale variabile si 
materializzera' solamente in fase di esecuzione, a seconda dei dati che 
vengono elaborati. Questo concetto si esprime dicendo che la 
tipizzazione avviene in fase di esecuzione (o a run-time).
In un linguaggio statico invece per ogni variabile, e in realta' per 
ogni espressione, e' possibile asserire quale sara' il tipo della 
variabile qualunque siano i dati elaborati, e qualunque siano le 
strutture di controllo di flusso utilizzate. Questo concetto si esprime 
dicendo che la tipizzazione avviene in fase di compilazione (o a 
compile-time).
-- 
Carlo Milanesi
http://digilander.libero.it/carlmila

 strutture di controllo di flusso utilizzate.
Secondo questa definizione C++ non e' un linguaggio statico.
Manco Java.
Anche il tuo esempio iniziale:
a = 2 + 2
a = "abc"
potrebbe funzionare in un linguaggio statico, a patto che a
sia un supertipo sia di interi che di stringhe. Nota che e' 
piu' una questione "sintattica" che semantica. 
In Java per esempio funziona, certo, devi dichiarare a.
Object a;
a = "ciao";
a = 2 + 2;
Ma il fatto di dover dichiarare le variabili non e' assolutamente
necessario per avere a che fare con un linguaggio statico (e viceversa).
Inoltre, il tuo esempio presuppone che sia possibile riassegnare una
variabile, cosa che non e' affatto scontata. 
Nei linguaggi statici, i tipi sono associati alle *variabili*,
in quelli dinamici sono associati agli "oggetti". In realta' 
questa definizione non e' estremamente robusta, ci sono
linguaggi staticamente tipizzati che associano il valore 
*anche* alle variabili.
Forse potremmo dire che nei linguaggi dinamici i tipi *non*
sono associati alle variabili. Ora tu questo lo hai spiegato
ampiamente nel tuo post e di conseguenza non mi soffermo.
Oppure ci si potrebbe legare al concetto di errore di tipo e
andare a chiedersi quando tali errori sono rilevati.
-- 
-riko

 andare a chiedersi quando tali errori sono rilevati.
Intendevo proprio questo, con la differenza che sostituirei la parola 
"rilevati" con la parola "rilevabili". Usando un linguaggio tipizzato 
staticamente, i tipi delle espressioni sono definiti nel programma 
stesso, prima che sia mandato in esecuzione, e questo permette la 
rilevazione degli errori di tipo semplicemente analizzando il programma. 
Usando linguaggio tipizzato dinamicamente invece i tipi delle 
espressioni sono definiti solamente in esecuzione, cioe' e' possibile 
che un'espressione non abbia un tipo fino a quando il programma non 
viene eseguito e quindi un eventuale errore di tipo non e' rilevabile 
fino all'esecuzione; inoltre, per alcuni dati di input tale errore 
potrebbe non essere mai rilevato.


 potrebbe non essere mai rilevato.
No, questa e' la definizione di tipizzazione debole. 
I linguaggi dinamici piu' usati hanno tipizzazione forte, ovvero gli
errori di tipo sono *sicuramente* rilevati, a runtime. Es. Python, Ruby.
In PHP per esempio succede come dici, l'errore potrebbe non essere
rilevato.
-- 
-riko

 rilevato.
Mi spiego. Il seguente programma Ruby genera un errore:
   if true then
     print "abc" / 2
   end
Mentre il seguente non genera nessun errore:
   if false then
     print "abc" / 2
   end
Questo lo interpreto con il fatto che in Ruby gli errori di tipo non 
sono *sicuramente* rilevati a runtime.


 sono *sicuramente* rilevati a runtime.
Perche' quell'errore non *deve* essere rilevato, visto che non puo'
accadere. Un errore di tipo deve essere rilevato *se* avviene, questo
e' interessante. Se nel codice morto ci sono degli errori, poco importa.
Ribadisco: quell'errore "di tipo" non deve essere rilevato. Il programma
che lo contiene non e' "scorretto" (e di conseguenza quello non e' un
errore), in quanto *non* viene *mai* eseguito.
Esattamente come un analizzatore statico per errori aritmetici *non*
acchiappa questo errore:
if(x == 0) {
        y / x;
}
e nota, non *deve* farlo. La consegna di un analizzatore sound e' non
lasciare che un errore che puo' accadere non sia rilevato. Ora
*ovviamente* sarebbe lecito segnalarlo, ma facendo analisi sui risultati
si considerebbe quella segnalazione un falso positivo, in quanto
quell'errore non puo' *MAI* avvenire. :)
-- 
-riko

 e' interessante. Se nel codice morto ci sono degli errori, poco importa.
Nei linguaggi dinamici normalmente non si parla di codice morto in 
quanto normalmente non viene fatta l'eliminazione del codice morto, ma 
soprattutto perche' nella specifica di tali linguaggi non sta scritto 
che il codice morto puo' contenere dei costrutti che non sarebbero 
ammessi in un codice vivo.
Ecco un esempio di codice Ruby che non contiene codice morto:
   if gets.to_i == 0
     print "abc" / 2
   end
Se l'utente inserisce "1", il programma non fa niente, mentre se 
inserisce "0" va in errore. Si tratta di un errore di tipo che c'e' 
sempre, ma non sempre viene rilevato, perche' viene rilevato solamente 
quando l'istruzione che lo contiene viene eseguita.
Il corrispondente in linguaggio C e':
   #include <stdio.h>
   int main() {
   char buf[100];
   if (atoi(gets(buf)) == 0)
     puts("abc" / 2);
   }
Questo programma e' illegale, e lo e' anche se si elimina la parola "puts".
Entrambi i casi diventano legali se si elimina la porzione "/ 2".


 Entrambi i casi diventano legali se si elimina la porzione "/ 2".
E quindi? Abbiamo due diversi typesystem, in un programma un codice e'
illegale e in un altro no. Ok. Tutto bene, sono linguaggi diversi. Tra
l'altro lo stesso codice che esibisoci *potrebbe* essere undefined
behaviour anche togliendo il famigerato / 2.
-- 
-riko