Discussione:
Linguaggi statici e dinamici?
(troppo vecchio per rispondere)
lmbup
2009-12-11 00:09:45 UTC
Permalink
Ciao,

qualcuno potrebbe spiegarmi in modo semplice
qual è la differenza tra i linguaggi di programmazione
dinamici e statici.
Massimo Soricetti
2009-12-11 18:45:27 UTC
Permalink
Post by lmbup
qualcuno potrebbe spiegarmi in modo semplice
qual è la differenza tra i linguaggi di programmazione
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...
Carlo Milanesi
2009-12-12 00:42:45 UTC
Permalink
Post by lmbup
qualcuno potrebbe spiegarmi in modo semplice
qual è la differenza tra i linguaggi di programmazione
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
Enrico Franchi
2009-12-12 11:16:54 UTC
Permalink
Post by Carlo Milanesi
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.
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
Carlo Milanesi
2009-12-12 15:16:06 UTC
Permalink
Post by Enrico Franchi
Post by Carlo Milanesi
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.
Secondo questa definizione C++ non e' un linguaggio statico.
Manco Java.
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;
Forse l'esempio che avrei dovuto fare era il seguente:
Object a;
Object b;
a = 2 + 2;
b = a / 2;
a = "ciao";
b = a.substr(1, 1);
Non credo che in Java sia possibile scrive qualcosa del genere a quando
scritto sopra senza introdurre un cast che modifica staticamente il tipo
di un'espressione.
Quando si analizza l'espressione "a / 2", si fa il seguente
ragionamento. Qui si vuole applicare l'operatore "/" all'oggetto "a". Ma
l'oggetto "a" e' in grado di eseguire tale operazione? In quanto
dichiarato di tipo "Object", non si puo' asserire che tale operazione
sia ammissibile. Pero' se in fase di esecuzione viene fatta un'apposita
elaborazione, si potrebbe determinare che tale operazione e' ammissibile
e trovare il modo di fargliela fare. Se invece si determinasse che tale
operazione e' inammissibile si dovrebbero prendere drastici
provvedimenti. Questo e' il nocciolo della "dinamicita'".
Sono d'accordo che anche in C++ e in java ci sono elementi di
dinamicita', ma sono essenzialmente linguaggi statici, cioe' quasi
sempre e' possibile determinare la semantica di un'espressione
indipendentemente dai dati di input.
Post by Enrico Franchi
Ma il fatto di dover dichiarare le variabili non e' assolutamente
necessario per avere a che fare con un linguaggio statico (e viceversa).
Non ne dubito.
Post by Enrico Franchi
Inoltre, il tuo esempio presuppone che sia possibile riassegnare una
variabile, cosa che non e' affatto scontata.
E' pero' comune a molti linguaggi di ampia diffusione, cioe' quelli
imperativi, purche' la variabile non sia una costante.

Un esempio che non fa tale assunzione e' il seguente:
a = "abc"
b = a / 2
oppure, piu' semplicemente:
("abc" / 2)

Quest'ultimo caso sembra ridicolo, in quanto per qualunque dato di input
e per qualunque altra porzione di codice ci sia prima o dopo, anche
nella stessa istruzione, e' evidente che l'espressione e' errata.
Tuttavia in un linguaggio dinamico normalmente tale errore non viene
rilevato in fase di compilazione, in quanto ci si astiene dal tentativo,
spesso infruttuoso, di determinare quali operazioni sono ammissibili e
quali no.
Post by Enrico Franchi
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.
Non mi dilungo sulla teoria dei linguaggi di programmazione, anche
perche' non sono un'autorita' in materia. Volevo solo aiutare l'OP nel
capire un concetto.
Post by Enrico Franchi
Oppure ci si potrebbe legare al concetto di errore di tipo e
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.
--
Carlo Milanesi
http://digilander.libero.it/carlmila
Enrico Franchi
2009-12-13 09:01:19 UTC
Permalink
Post by Carlo Milanesi
Sono d'accordo che anche in C++ e in java ci sono elementi di
dinamicita', ma sono essenzialmente linguaggi statici, cioe' quasi
sempre e' possibile determinare la semantica di un'espressione
indipendentemente dai dati di input.
No. In C++ e in Java il tipo di un'espressione e' in generale
indecidibile a compile time. Di conseguenza la sua semantica.
Post by Carlo Milanesi
E' pero' comune a molti linguaggi di ampia diffusione, cioe' quelli
imperativi, purche' la variabile non sia una costante.
Vero.
Post by Carlo Milanesi
Tuttavia in un linguaggio dinamico normalmente tale errore non viene
rilevato in fase di compilazione, in quanto ci si astiene dal tentativo,
spesso infruttuoso, di determinare quali operazioni sono ammissibili e
quali no.
Vero.
Post by Carlo Milanesi
Usando un linguaggio tipizzato
staticamente, i tipi delle espressioni sono definiti nel programma
stesso, prima che sia mandato in esecuzione
E questo purtroppo non e' vero. Mi spiego, questa e' una buona
definizione "matematicamente", ma non coglie quello che intendiamo noi.
In tutti i linguaggi in cui hai il concetto di supertipo e di metodo
virtuale, tipicamente non sai quale e' il tipo *vero* di un oggetto.
Di conseguenza non puoi determinarne la semantica.

Nei linguaggi dinamici questo e' semplicemente il caso "normale", non lo
puoi *mai* fare.
Post by Carlo Milanesi
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
Diciamo che si sceglie di non rilevarlo. Per dire, WingIDE e' un IDE
Python che fa autocompletion molto sveglia. Per fare questo ha
sostanzialmente un inferitore di tipi *molto* ben fatto.
Post by Carlo Milanesi
inoltre, per alcuni dati di input tale errore
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
Carlo Milanesi
2009-12-13 11:48:24 UTC
Permalink
Post by Enrico Franchi
Post by Carlo Milanesi
Sono d'accordo che anche in C++ e in java ci sono elementi di
dinamicita', ma sono essenzialmente linguaggi statici, cioe' quasi
sempre e' possibile determinare la semantica di un'espressione
indipendentemente dai dati di input.
No. In C++ e in Java il tipo di un'espressione e' in generale
indecidibile a compile time. Di conseguenza la sua semantica.
Questo vale in modo molto limitato, almeno in C++.
Un'espressione come "3 + 2" ha un tipo decidibile.
Un'espressione come a.f() ha un tipo decidibile se "f" rende un valore
non di tipo classe, e se rende un valore di tipo classe rimane indeciso
solo se e' una data classe o una sua sottoclasse. E comunque le
operazioni ad esso applicabili sono solamente quelle dichiarate nella
classe base. Qualunque tentativo di applicare a tale espressione
un'operazione non definita nella classe base e' illegale (staticamente).
Post by Enrico Franchi
Post by Carlo Milanesi
inoltre, per alcuni dati di input tale errore
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.
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.
--
Carlo Milanesi
http://digilander.libero.it/carlmila
Enrico Franchi
2009-12-13 17:58:03 UTC
Permalink
Post by Carlo Milanesi
Post by Enrico Franchi
No. In C++ e in Java il tipo di un'espressione e' in generale
indecidibile a compile time. Di conseguenza la sua semantica.
Questo vale in modo molto limitato, almeno in C++.
Un'espressione come "3 + 2" ha un tipo decidibile.
Un'espressione come a.f() ha un tipo decidibile se "f" rende un valore
non di tipo classe, e se rende un valore di tipo classe rimane indeciso
solo se e' una data classe o una sua sottoclasse. E comunque le
operazioni ad esso applicabili sono solamente quelle dichiarate nella
classe base. Qualunque tentativo di applicare a tale espressione
un'operazione non definita nella classe base e' illegale (staticamente).
Certo. Resta il fatto che il problema *generale* di decidere il tipo di
un'espressione e' indecidibile. E di conseguenza non necessariamente sai
il comportamento di molte operazioni (e delle chiamate dei metodi).

Limitato o meno, e' indecidibile. Viceversa non e' cosi' in altri
linguaggi statici con un typesystem un po' piu' carino.
Post by Carlo Milanesi
if true then
print "abc" / 2
end
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.
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
Carlo Milanesi
2009-12-13 18:40:21 UTC
Permalink
Post by Enrico Franchi
Limitato o meno, e' indecidibile. Viceversa non e' cosi' in altri
linguaggi statici con un typesystem un po' piu' carino.
Stai parlando del linguaggi C, Pascal, COBOL, Fortran, e in generale di
tutti i linguaggi statici che non hanno funzioni virtuali? In questi
linguaggi il tipo di un'espressione e' sempre decidibile.
Post by Enrico Franchi
Post by Carlo Milanesi
if true then
print "abc" / 2
end
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.
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.
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".
--
Carlo Milanesi
http://digilander.libero.it/carlmila
Enrico Franchi
2009-12-13 19:39:54 UTC
Permalink
Post by Carlo Milanesi
Stai parlando del linguaggi C, Pascal, COBOL, Fortran, e in generale di
tutti i linguaggi statici che non hanno funzioni virtuali? In questi
linguaggi il tipo di un'espressione e' sempre decidibile.
Io non parlavo di quelli, anche se, in effetti hanno la proprieta' che
dici. Ma la hanno perche' hanno un sistema di tipi meno espressivo, mica
per altro. :)

Comunque e' vero... non avendo il concetto di sottoclasse. Ah, con C
intendiamo i nuovi C, ovviamente. Pascal lo conosco a livello molto
accademico, FORTAN meno e COBOL quasi per nulla. Per cui non mi esprimo
riguardo al loro typesystem, che esula pesantemente dal livello di
dettaglio con cui li conosco.
Post by Carlo Milanesi
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.
Si parla, si parla. Non si fa l'ottimizzazione, questo no. Ma il
concetto di "codice morto" prescinde dall'ottimizzazione. E'
semplicemente una proprieta' dei cammini di computazione. Se nessun
cammino passa per quel codice sotto nessuna ipotesi, quel codice e'
morto anche se il linguaggio e' dinamico.

Quello che sta scritto e' che l'espressione "abc" / 2 da errore *se*
viene valutata. Non vedo nessun motivo sensato per cui dovrebbe dare
errore *se* non viene valutata. Aspettarsi che il runtime segnali un
errore che non avviene mi pare eccessivo, che dici? :)
Post by Carlo Milanesi
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.
Quello non e' codice morto. In quanto esiste, come fai notare tu stesso,
un caso in cui quel codice viene eseguito. Quel codice non e' morto e
l'interprete ruby immancabilmente lancera' un eccezione in tutti i casi
in cui quel codice viene eseguito *ed* e' un errore. Non lancera' errore
quando quel codice *non* viene eseguito (tutto bene).
Post by Carlo Milanesi
#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".
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
Continua a leggere su narkive:
Loading...