Newsgroup: it.comp.java
Titolo: C++, Java, C#: qualche considerazione [lungo]

* C++, Java, C#: qualche considerazione
Come molti di voi sapranno, da qualche mese Microsoft ha annunciato un
"nuovo" linguaggio C-based, denominato C-Sharp (scritto C#, dove il '#' e'
il diesis, con un "gioco di notazione" che non sara' molto ovvio fuori dalla
cultura anglosassone, dove la lettera C indica la nota musicale 'do').
Seguendo la tradizione (che negli ultimi anni ha raggiunto il suo apice con
Java) la nascita e' stata accompagnata da un coro di acclamazioni, piu' o
meno concordanti nel sostenere le doti del C# come "C++ migliore", "Java
migliore", eccetera eccetera. Ovviamente, come si compete in questi casi,
senza fornire nessuna misura oggettiva di cosa significhi "migliore", ma
semplicemente facendo leva sul folklore popolare, secondo cui, ad esempio,
avere "friend" nel linguaggio e' una cosa cattiva, e cosi' via.
In realta', C++, Java e C# hanno alcuni aspetti palesemente simili,
soprattutto a livello sintattico (con qualche trappola semantica), un certo
numero di sottili incompatibilita' facili da gestire (una volta capite), un
certo numero di differenze del tutto arbitrarie (e anche qui ci si abitua
facilmente) ed infine alcune fratture vere e proprie negli idiomi di
programmazione piu' basilari.
In questo articolo vorrei concentrarmi solo su questi ultimi aspetti,
prendendo in considerazione due elementi di *grande* separazione "culturale"
tra C++ da un lato e Java/C# dall'altro (Java e C# si somigliano decisamente
molto). Nel fare questo, mi limitero' a considerare Java e C# a livello di
linguaggio, anche se Java e' in realta' una piattaforma e C# e' un mattone
di una piattaforma.
Come vedrete, l'articolo sara' un po' anomalo per C++ Informer, e per certi
aspetti sara' piu' vicino agli articoli della mia vecchia rubrica
Principles&Techniques su Computer Programming. Se pensate che articoli dal
taglio simile siano interessanti per voi lettori di Informer, vi invito a
mandarmi un breve commento via email.
* Una premessa essenziale
Da una newsletter dal titolo "C++ Informer" ci si dovrebbe aspettare, a
questo punto, una forma di critica piu' o meno crudele di Java e/o del C#.
Non e' invece questo il mio obiettivo.
Come accennavo poco sopra, vorrei approfondire alcuni aspetti di grande
frattura tra i linguaggi, che nei molti articoli "di comparazione" che ho
letto non vengono neppure mai menzionati. Con l'occasione, vorrei anche far
osservare come i diversi aspetti di un linguaggio siano spesso fortemente
correlati, al punto che presa una decisione alcune altre vengono in qualche
modo forzate. Anche questo e' un punto che viene spesso ignorato da chi
pensa che si possa creare il linguaggio perfetto scegliendo una lista di
feature e mettendole insieme, o "sfoltendo" in modo piu' o meno arbitrario
un linguaggio esistente.
In particolare, vorrei discutere l'influenza profonda che ha avuto, nel
passaggio da C++ a Java/C#, la decisione di avere solo oggetti heap-based.
Un aspetto apparentemente secondario, che porta invece con se' notevoli
punti di distacco sia nei linguaggi, sia (maggiormente!) negli idiomi di
programmazione da adottare.
Inoltre, vorrei dedicare un po' di spazio a discutere un aspetto poco
compreso del C++ rispetto ad altri linguaggi (come Java/C# ed altri), ovvero
la sua natura di linguaggio "a due livelli", contrapposta a quella di
linguaggi "ad un livello", e le profonde implicazioni di questa differenza
sulla cultura della programmazione.
Poiche' non tutti i lettori di Informer conoscono Java o il C#, nel seguito
faro' uso di un C++ utilizzato "in stile Java" per mostrare le differenze a
livello di codifica. Credo che tutto sommato l'articolo diventi, in questo
modo, piu' semplice da seguire per chi conosce soltanto il C++.
* Oggetti heap-based
In Java, cosi' come in C#, tutti gli oggetti sono allocati sullo heap. In
C++ gli oggetti possono essere allocati sullo stack (dichiarandoli come
variabili locali) o sullo heap (tramite una new expression); ad essere
precisi, dovremmo dire che in C++ esistono oggetti con storage automatico o
dinamico (ed anche statico, ma faro' finta di niente :-), solitamente
realizzati tramite allocazione su stack o su heap.
Vista semplicemente cosi', questa differenza puo' sembrare marginale. In
realta' e' alla radice di un forte scostamento tra i due linguaggi.
In un linguaggio con soli oggetti dinamici abbiamo due sole scelte per
definire la vita di un oggetto: lasciare al programmatore la responsabilita'
di distruggerlo (come accade in C++ usando i puntatori nativi: dobbiamo
chiamare "delete" manualmente) o lasciare questa responsabilita' al supporto
run-time del linguaggio. Quasi tutti i linguaggi con soli oggetti dinamici
scelgono la seconda possibilita', ed integrano quindi un meccanismo di
garbage collection.
Sull'efficienza dei garbage collector (o sulla sua assenza) sono state
scritte centinaia di articoli, e tutto sommato l'argomento "efficienza" non
e' importante per quanto mi accingo scrivere. Mi interessano invece le sue
implicazioni sugli idiomi di codifica.
Infatti, gestire la vita degli oggetti attraverso un garbage collector ha un
effetto collaterale importante dal punto di vista di chi usa il linguaggio:
non sappiamo il momento esatto in cui l'oggetto verra' distrutto. Questo
rende il concetto di "distruttore", come lo conosciamo in C++, molto meno
interessante. Mentre in C++ il distruttore puo' avere una responsabilita'
comportamentale importante, in un linguaggio con garbage collector il
distruttore diventa solo un meccanismo di clean-up.
Un esempio chiarira' meglio il significato di quanto sopra. Pensiamo alla
classe standard std::ofstream, che possiamo usare per scrivere su file. Un
uso come il seguente e' perfettamente lecito:
void f()
  {
  // ... codice ...
  if( something )
    {
    ofstream outFile1( "a.txt" ) ;
    outFile1 << "pippo" ;
    // (1)
    }
  // ... codice ...
  if( something_else )
    {
    // (2)
    ofstream outFile2( "a.txt", ios::app ) ;
    outFile2 << "pluto" ;
    }
  }
Notiamo che in (1) non chiudiamo lo stream: *sappiamo* che il distruttore di
outFile1 verra' *sicuramente* invocato uscendo dal blocco, e che questo
comportera' il flush e la chiusura del file. Siamo cosi' certi, in (2), di
poter aprire nuovamente lo stesso file in modalita' append e di avere il
file in uno stato consistente.
Questo non e' ovviamente il caso di un linguaggio con soli oggetti dinamici
e garbage collection: usciti dal primo blocco, outFile1 (se fosse allocato
dinamicamente) sarebbe candidato alla collection, ma questo potrebbe
avvenire in qualunque momento. Non avremmo alcuna garanzia, arrivati in (2),
di una avenuta chiusura (con flush) del file che abbiamo aperto poco sopra.
La conseguenza e' quindi immediata, e vale tanto per Java quanto per C# o
Eiffel: la funzione "equivalente" al distruttore (finalize in Java, dispose
in Eiffel, ecc) ha uno scopo di clean-up su cui il programmatore non
puo'/deve contare dal punto di vista della logica applicativa.
In un C++ usato "alla Java/C#" (cosa possibilissima: esistono dei garbage
collector, tipicamente conservativi, per C++) il codice visto sopra
diventerebbe quindi:
void f()
  {
  // ... codice ...
  if( something )
    {
    ofstream* outFile1 = NULL ;
    outFile1 = new ofstream( "a.txt" ) ;
    *outFile1 << "pippo" ;
    // (1)
    outFile1->close() ;
    }
  // ... codice ...
  if( something_else )
    {
    ofstream* outFile2 = NULL ;
    // (2)
    outFile2 = new ofstream( "a.txt" ) ;
    *outFile2 << "pluto" ;
    outFile2->close() ;
    }
  }
Notate che ho dovuto chiudere esplicitamente lo stream in entrambi i casi
(nel caso in questione, era essenziale solo in (1); ovviamente, non chiudere
il file nel secondo caso avrebbe aperto la porta a problemi in fase di
manutenzione). In altre parole, cio' che ho fatto e' stato barattare la
gestione della memoria con la gestione di *tutte* le altre risorse, ma su
questo tornero' piu' avanti.
La differenza puo' ancora sembrare marginale, anche se per chi ha piu'
esperienza dovrebbe essere piuttosto chiaro che stiamo rinunciando ad uno
degli idiomi centrali della "cultura C++", il cosiddetto "resource
acquisition is initialization". Proseguiamo pero' ad esplorare le
conseguenze della nostra scelta iniziale di avere solo oggetti dinamici e
garbage collection.
Un linguaggio "serio" deve fornire ai programmatori un buon supporto per la
gestione e la propagazione degli errori: negli anni, un approccio che si e'
dimostrato molto efficace e' quello delle eccezioni, presenti sia in C++ che
in Java e C#. In tutti e tre i linguaggi possiamo fare uso del try/catch per
gestire un errore. Java e C# hanno anche il "finally", che identifica un
blocco di codice da eseguire comunque, sia in caso di errore che di
successo; in C++ manca, ed anche questo va ricondotto alla scelta di cui
sopra.
Proviamo infatti a rendere piu' robusto il nostro codice C++ "Java-like".
Invece del listato messo sopra, potremmo inizialmente scrivere:
void f()
  {
  // ... codice ...
  if( something )
    {
    ofstream* outFile1 = NULL ;
    try
      {
      outFile1 = new ofstream( "a.txt" ) ;
      *outFile1 << "pippo" ;
      // (1)
      outFile1->close() ;
      }
    catch( ... )
      {
      // ... faccio qualcosa ...
      // (3)
      if( outFile1 != NULL )
        outFile1->close() ;
      }
    }
  // ... codice ...
  if( something_else )
    {
    ofstream* outFile2 = NULL ;
    try
      {
      // (2)
      outFile2 = new ofstream( "a.txt" ) ;
      *outFile2 << "pluto" ;
      // (4)
      outFile2->close() ;
      }
    catch( ... )
      {
      // ... faccio qualcosa ...
      // (5)
      if( outFile2 != NULL )
        outFile2->close() ;
      }
    }
  }
Notiamo che, dovendo gestire "a mano" le risorse, ho duplicato una parte di
codice (una sola riga in questo caso minimale), tra i due blocchi try ed i
due blocchi catch ((1) e' duplicato in (3), (4) e' duplicato in (5)).
Poiche' duplicare il codice va contro un principio fondamentale di buona
codifica (pensate alla manutenzione!) emerge la vera e propria *necessita'*
di avere un blocco di codice che viene comunque eseguito, sia in caso di
eccezione che di normale uscita dal blocco. Il nostro codice potrebbe cosi'
diventare:
void f()
  {
  // ... codice ...
  if( something )
    {
    ofstream* outFile1 = NULL ;
    try
      {
      outFile1 = new ofstream( "a.txt" ) ;
      *outFile1 << "pippo" ;
      }
    catch( ... )
      {
      // ... faccio qualcosa ...
      }
    finally
      {
      // (1) + (3)
      if( outFile1 != NULL )
        outFile1->close() ;
      }
    }
  // ... codice ...
  if( something_else )
    {
    ofstream* outFile2 = NULL ;
    try
      {
      // (2)
      outFile2 = new ofstream( "a.txt" ) ;
      *outFile2 << "pluto" ;
      }
    catch( ... )
      {
      // ... faccio qualcosa ...
      }
    finally
      {
      // (4) + (5)
      if( outFile2 != NULL )
        outFile2->close() ;
      }
    }
  }
Notiamo anche che se f() non fosse interessata a gestire l'eccezione (parte
catch) ma solo a fare il giusto cleanup, dovremmo comunque scrivere un
codice piuttosto prolisso: una variabile dichiarata fuori dal blocco try, il
blocco try, il blocco finally con test sulla variabile, tutto ripetuto sia
per outFile1 che outFile2. Compariamo questo con lo stile C++ "corretto",
ovvero con il primo listato visto, che usa oggetti automatici ed ha identico
significato, e noteremo una differenza sensibile.
Riprendiamo brevemente le fila: scegliendo un linguaggio con soli oggetti
dinamici, diventa comodo (e facile) introdurre un garbage collector. Questo
rende il distruttore (o il suo "analogo") un punto di solo clean-up, non di
garanzia comportamentale, e quindi invalida l'idioma "resource acquisition
is initialization". A sua volta, essendo tale idioma centrale nella gestione
degli errori, la sua assenza ci porta quasi per necessita' ad introdurre il
concetto di finally block per evitare la duplicazione del codice.
Come vedete, e' una catena logica piuttosto stretta, che lascia pochi spazi
di manovra: una scelta iniziale ha una serie di conseguenze in punti che
potrebbero sembrare anche "distanti" ad uno sguardo meno attento. La scelta
di avere solo oggetti dinamici ha anche altre conseguenze molto
interessanti, come la facilita' di implementare le chiusure, ampiamente
sfruttata in Java con le inner class. Come dire... chiudendo una
opportunita', se ne aprono altre.
Il vero punto di frattura con il C++ e' comunque la rinuncia all'idioma
"resource acquisition is initialization", che *richiede* oggetti con
lifetime automatico. Questo significa, come accennavo sopra e come vorrei
ora approfondire, barattare la gestione "gratuita" della memoria con la
gestione "manuale" di tutte le altre risorse (file, handle, finestre, lock,
qualunque cosa).
In particolare, l'uso "corretto" (tornero' piu' avanti su questo punto) di
*ogni* risorsa in C++ si riassume nella creazione di una classe apposita,
che di solito nel costruttore acquisisce l'ownership della risorsa e nel
distruttore rilascia la risorsa. Questo idioma si applica, come dicevo, ad
*ogni* risorsa, memoria inclusa; questo implica l'abbandono dei puntatori
nativi nel codice applicativo.
Iniziamo cosi' a capire la natura "a due livelli" del C++, che spieghero'
meglio al punto successivo: il C++ offre un supporto nativo low-level per
costruire astrazioni di piu' alto livello. Il programmatore dovrebbe
lavorare con queste astrazioni, costruendole quando servono, e non lavorare
costantemente al livello nativo.
La gestione della memoria, in particolare, dovrebbe sempre avvenire
attraverso una famiglia di smart pointer, che rappresentino perlomeno i tre
casi fondamentali: ownership esclusiva (gia' presente nello standard come
auto_ptr), ownership condivisa (e qui esistono molte varianti di puntatori
con reference count), navigazione senza ownership (essenziale per evitare le
dipendenze circolari che vengono spesso portate come giustificazione
dell'assoluta necessita' di un garbage collector). Gli oggetti dinamici
verranno quindi gestiti da una loro controparte (smart pointer) con lifetime
*automatica*. In un simile programma, non avremo alcuna delete expression,
se non *dentro* le astrazioni utilizzate (gli smart pointer).
Cio' significa, naturalmente, *progettare* il lifetime di ogni risorsa,
memoria inclusa, e poi utilizzare le giuste astrazioni nel codice: in
particolare, l'uso di puntatori nativi non ha posto nella programmazione C++
"moderna", se non nella realizzazione delle astrazioni in questione, in
situazioni di confine (interoperabilita' con il C o con funzioni di
sistema), o in presenza di compilatori incapaci di espandere in linea le
funzioni inline piu' elementari.
Viceversa, in Java/C# non e' teoricamente necessario progettare il lifetime
degli oggetti. Ma questa e' una falsa sicurezza, in quanto in molte
situazioni (non necessariamente in tutte) dovremo comunque gestire a mano
acquisizione e rilascio delle risorse. Ed e' piuttosto ovvio che una volta
rilasciata la risorsa "contenuta" in un oggetto, il fatto che l'oggetto
rimanga accessibile ad altri (che non sanno nulla circa il suo stato
interno) e' assai poco sensato, per cui non si puo' realmente fare a meno di
progettare il lifetime: si puo' solo fare a meno di implementare una parte
della sua gestione (vi lascio da pensare quale parte della gestione del
lifetime non si puo' fare a meno di realizzare)
Prima conclusione: pur dietro una sintassi molto simile, se confrontiamo un
programma "reale" e "ben scritto" in C++ ed uno equivalente e "ben scritto"
in Java o C# noteremo una differenza piuttosto profonda. In particolare, in
C++ faremo uso di molte piccole astrazioni (puntatori intelligenti, classi
Lock, ecc) con il solo scopo di gestire il rilascio delle risorse, e di
classi "piu' grosse" come stream, Window, ecc in cui anche il distruttore
gioca un ruolo centrale nel comportamento dell'applicazione. Viceversa, in
Java/C# ci troveremo di fronte a codice con allocazioni ed aliasing piu'
liberal :-), ma con una gestione totalmente manuale della fase di rilascio
delle risorse, spesso in un blocco finally. Mediamente, e qui sta una
sorpresa per molti, il codice C++ ben scritto sara' piu' breve, perche' la
stessa piccola astrazione verra' probabilmente riusata in molti punti
diversi, e conterra' una parte di logica che in Java/C# andra' *replicata*
in tanti blocchi finally di!
versi (da questo, chi vuole potra' trarre un giudizio su quale sia la
tecnica migliore dal punto di vista dell'ingegneria del software).
Viceversa, se confrontiamo codice C++ "mal scritto" (grande uso di puntatori
nativi, gestione degli errori assente o manuale, senza classi per le
risorse) con codice Java/C# "mal scritto" (semplicemente privo di gestione
degli errori) noteremo che il codice Java/C# risulta molto piu' snello: non
dovendo gestire la memoria evitiamo le delete expression, e trascurando la
gestione degli errori non abbiamo try/finally a mostrare il lato "debole"
degli oggetti esclusivamente dinamici. Chissa' come mai, nei tanti articoli
di comparazione C++/Java (e presto, immagino, C++/C#) il codice che viene
mostrato non si preoccupa quasi mai di gestire gli errori :-).
Purtroppo, va comunque riconosciuto che una gran parte del codice C++
esistente non e' "ben scritto" secondo l'accezione vista sopra. Non si
tratta (soltanto) di una mancanza dei programmatori: solo in tempi
relativamente recenti il C++ e' diventato sufficientemente potente da
supportare, al suo livello piu' basso, la creazione delle corrette
astrazioni da usare al livello piu' alto (come gli smart pointer in grado di
supportare il polimorfismo). Esiste, ovviamente, anche un problema
culturale; e qui diventa fondamentale capire un'altra e piu' profonda
differenza tra C++ e Java/C#, ovvero la natura di linguaggio ad uno o due
livelli.
* Application design is language design
Lo sviluppo di una applicazione complessa si puo' affrontare seguendo
approcci diversi. Un approccio che personalmente ritengo tra i migliori e'
quello architetturale, in cui la fase di design inizia con la definizione di
una macro-struttura (portante) per l'applicazione. Durante questo momento si
identificano alcune astrazioni fondamentali che finiranno con il permeare
l'intero codice applicativo. Spesso queste astrazioni diventano un vero e
proprio "linguaggio" in cui i programmatori inizieranno a pensare e a
comunicare fra loro; questo "linguaggio", nel lungo termine, diventa parte
della cultura di una azienda, o di un settore applicativo. Ad esempio, nel
settore dell'automazione esistono concetti ricorrenti come gli eventi, i
device, i comandi. Nel settore business esistono concetti ricorrenti come i
record, le regole di validazione, i report. E cosi' via. Questi concetti
ricorrenti formano, a seconda di chi li guarda, un pattern language, o un
problem frame, o semplicem!
ente, come accennavo, una "cultura di fondo" che distingue chi ha lavorato
nel settore da chi ne e' rimasto fuori.
Alcuni progettisti e programmatori si accontentano di pensare in termini di
eventi e programmare in termini di long int. Altri cercano una piu' forte
risonanza tra il "linguaggio" in cui pensano e quello in cui programmano: il
modo piu' semplice di ottenere questo effetto e' creare delle astrazioni nel
proprio linguaggio di programmazione (ad es. delle classi per chi adotta il
paradigma ad oggetti). Cio' che in realta' vorremmo avere e' una impedenza
minima tra le astrazioni che possiamo creare e gli elementi gia' presenti
nel nostro linguaggio di programmazione: vorremmo cioe', durante la
progettazione di una applicazione, *estendere* il nostro linguaggio di
programmazione per avvicinarlo al "linguaggio" tipico del nostro settore
applicativo.
Non tutti i linguaggi di programmazione supportano questo modo di operare.
Tipicamente, i linguaggi nati con obiettivi didattici (come il Pascal) fanno
una netta distinzione tra quello che puo' fare il compilatore e quello che
puo' fare il programmatore. Ad esempio, in Pascal esistono funzioni
overloaded variadiche (come read e write) ma il programmatore non puo'
crearne di nuove. Esistono dei tipi elementari che fanno uso dei comuni
operatori come + e - (gli interi, i reali), ma i tipi introdotti dal
programmatore non possono farne uso. E cosi' via. Io chiamo questi linguaggi
"ad un livello", perche' offrono astrazioni di livello intermedio, su cui il
programmatore puo' costruire nuovi elementi che tuttavia sono visibilmente
diversi da quelli built-in del linguaggio.
Il C++ e' invece in larga misura (ma non completamente) un linguaggio "a due
livelli". Espone astrazioni di livello intermedio ma anche elementi di
livello molto basso, attraverso i quali il programmatore puo' arricchire il
linguaggio per "adattarlo" al proprio ambito applicativo, con una impedenza
minima tra elementi built-in ed elementi di libreria. Vedremo qualche
esempio concreto tra poco.
Java e C# *non* seguono la strada a due livelli del C++. Sono linguaggi ad
un livello, e questo plasma in modo piuttosto profondo l'approccio del buon
programmatore.
Vediamo qualche esempio: il C# aggiunge una parola chiave "event" per
modellare il concetto (che diventa built-in, un po' come il Visual Basic) di
evento di notifica. E' evidente che nel settore applicativo che Microsoft
ipotizza per il C# il concetto di evento e' importante, ed il linguaggio
deve supportarlo in modo adeguato. Tuttavia, per supportarlo a livello di
*libreria* il linguaggio deve essere di gran lunga piu' potente e flessibile
del C#. Avendo realizzato nel corso degli anni molte applicazioni
event-driven in C++, solo in tempi recenti sono riuscito finalmente ad
implementare degli eventi soddisfacenti (con una impedenza minima rispetto
al resto del linguaggio, type-safe, thread-safe, e cosi' via). Tutto
sommato, ho dovuto "dare fondo" a molte novita' del C++, come member
template et similia. In C#, *dobbiamo* avere un supporto built-in
specializzato, perche' non possiamo costruirlo come parte di libreria senza
renderne l'uso eccessivamente "scomodo" (ovvero, tro!
ppo disomogeneo con il resto del linguaggio). Lo stesso avviene per la
keyword "delegate" del C#.
Java e' ancora piu' restrittivo del C#, che perlomeno consente un parziale
overloading degli operatori. Ad esempio, il "comune programmatore" non
potrebbe implementare una classe String come quella built-in di Java, in
quanto non potrebbe ridefinire l'operator + (e +=) in modo analogo a quanto
avviene per String.
Questa differenza tra "il comune programmatore" e "l'autore del
linguaggio/libreria standard" e' tipica dei linguaggi ad un livello, mentre
tende a svanire (in modo piu' o meno netto) nei linguaggi a due livelli.
E' tra l'altro importante capire che la natura "a due livelli" del C++ non
deriva dalla sua radice nel C. Anzi, delle astrazioni low-level che il C++
offre al programmatore, soltanto una arriva dal C: la capacita' di
manipolazione della memoria. Le altre astrazioni sono state introdotte da
Stroustrup, sia riportando inizialmente in C++ alcune caratteristiche
"interessanti" di altri linguaggi, sia strada facendo, per supportare al
meglio la creazione di librerie come STL.
I lettori piu' affezionati di Informer non faranno fatica a riconoscere, nei
vari "No Limits" che ho presentato in questi dodici numeri, un uso creativo
di building-block elementari, che come ho piu' volte sottolineato nel tempo
si dimostrano eccezionalmente flessibili e ricombinabili. Questi
building-block non sono stati ereditati dal C: sono "nati" in C++.
E' altrettanto importante notare che la "cultura C++ mainstream" non ha
ancora completamente realizzato la natura a due livelli del linguaggio.
Anche i riferimenti in letteratura sono relativamente scarsi. Il primo
articolo di cui sono a conoscenza [Alger94] e' relativamente datato, ma
nonostate in esso l'autore dica chiaramente "C++ is really 'yacc++', not so
much a language as a way of creating your own languages", pochi
programmatori devono averlo letto e capito a fondo. Altri riferimenti si
possono trovare nei lavori di Andrew Koenig, che nei suoi articoli sul JOOP
ha piu' volte ricordato una massima dei laboratori AT&T ("Library Design is
Language Design") cui ho ispirato anche il titoletto del presente paragrafo;
questi articoli sono poi stati raccolti in [KM96].
Lo stesso Stroustrup, a mio avviso, solo in tempi recenti ha completamente
"realizzato" le potenzialita' della strada intrapresa: in una recente
intervista [Nelson00], Bjarne ha risposto alla domanda "If you were to start
from scratch today and design C++ over again, what would you do
differently?" osservando, tra le altre cose (e per la prima volta, a quanto
mi risulta) "I'd try to create a relatively small core language-containing
key abstraction features along the lines of classes and templates-and put
much more of 'the language' into libraries. However, I'd try hard to make
the core language powerful enough for those libraries to be written in the
core language. I do not like the idea of having standard library writers
rely on extra-linguistic facilities not available to 'ordinary users'. I'd
work on getting this core language very precisely defined".
Questa e' l'essenza di un linguaggio "a due livelli", che il C++ si avvicina
a realizzare anche se non riesce ancora a sfruttare completamente (in un
linguaggio realmente "a due livelli", il programmatore potrebbe aggiungere
ad esempio nuove strutture di controllo come il foreach del C#; in C++ si
puo' andare vicini, ma rimane una certa impedenza).
Tutto sommato, come vedete, l'idea di fondo e' molto semplice, ma e' anche
largamente ignorata dai programmatori, e talvolta persino fraintesa (su
quest'ultimo punto tornero' piu' avanti). Pertanto, credo possa essere utile
rivedere in questa luce un paio di esempi concreti.
Nel numero 2 di C++ Informer, nella rubrica "No Limits", ho mostrato come
implementare in C++ un analogo delle funzioni Java forName() e newInstance()
(della classe built-in Class). Il risultato finale non era perfetto, in
quanto rimaneva un po' scomodo dichiarare una classe in modo che
beneficiasse del nuovo supporto; in particolare, era necessario definire uno
string literal con il nome della classe e linkage esterno, ed anche se avevo
fornito una apposita macro, rimaneva una certa impedenza rispetto ai
costrutti built-in del linguaggio.
La soluzione "ad un livello", di fronte ad un problema del genere, e'
piuttosto semplice: se si ritiene che forName/newInstance siano utili, si
aggiungono al linguaggio. La soluzione "a due livelli" e' invece diversa: se
si ritiene che forName/newInstance siano utili, e non si riescono a
realizzare "bene" nel linguaggio corrente, si cercano i building block
elementari che, aggiunti al linguaggio, consentono di costruire un
forName/newInstance ben integrato. Ad esempio, potremmo ipotizzare un
potenziamento di typeid(), che oggi restituisce un valore run-time di classe
type_info, attraverso il quale non possiamo comunque ottenere il nome
completamente qualificato della classe ma solo un nome
implementation-defined. Una versione piu' potente potrebbe consentire di
ottenere informazioni a compile-time, ovviamente riferite al tipo statico
dell'espressione. Su questo building-block piu' elementare potremmo
costruire un forName/newInstance migliore, ed in futuro anche altre funzion!
alita': piu' elementare e' il building-block, piu' e' probabile che si
presti ad utilizzi alternativi.
Lo stesso criterio vale per alcuni elementi proposti per l'inclusione nello
standard C++, come implicit_cast. E' emerso che sfruttando la qualificazione
esplicita dei template function, unitamente alla trail argument deduction,
si poteva implementare implicit_cast direttamente nel linguaggio, senza
modifiche al compilatore. Con lo stesso meccanismo, possiamo definire anche
nuovi, interessanti cast, come interpret_cast (si veda ad esempio [Henney00]
per una breve spiegazione), sempre a livello di libreria.
Ancora un esempio: in fase di standardizzazione e' stato rifiutato "typeof",
un costrutto molto utile gia' implementato da alcuni compilatori come lo
GNU. Inizialmente mi e' sembrato un errore, perche' typeof e' molto piu'
utile di quanto si tenda a credere; piu' volte ho provato a realizzarlo "nel
linguaggio" raggiungendo solo risultati molto parziali, non dissimili da
quelli presentati recentemente da Bill Gibbons sul C++ Users Journal
[Gibbons00]. In tempi successivi, ripensando meglio al problema, ho capito
che "dietro" typeof si nasconde l'opportunita' di migliorare il linguaggio
creando un building-block piu' elementare, utile anche in altri casi. Un
buon esercizio per i guru o aspiranti tali e' pensare un po' a quali
building-block potrebbero rendere semplice l'implementazione di typeof. Ne
esistono diversi, che ovviamente portano a realizzazioni e potenzialita'
differenti; in particolare, se avete letto l'articolo di Gibbons potreste
valutare l'utilita' in questa sit!
uazione dello stesso building-block cui ho accennato sopra per
forName/newInstance.
Naturalmente, non mi aspetto che la programmazione "di ogni giorno" avvenga
in termini di questi building block elementari. E spesso neppure dei
costrutti creati immediatamente sopra di essi: ad esempio, mi aspetto che
forName/newInstance sia, in molti casi, ulteriormente incapsulata da una
factory. Tuttavia, la differenza metodologica (mi verrebbe da dire
"filosofica") tra un linguaggio "a due livelli" ed un linguaggio "ad un
livello" e' veramente profonda, anche per le sue implicazioni sulle
capacita' dei programmatori che lo utilizzeranno. Un linguaggio "ad un
livello" suppone che i progettisti del linguaggio siano molto piu' in gamba
dei programmatori, e che sappiano prevedere l'esatto insieme di concetti
utili, senza esagerare in un verso o nell'altro. Il progettista di un
linguaggio "a due livelli" cerca di trovare i building-block elementari,
sopra i quali costruire elementi di libreria *indistinguibili* da elementi
del linguaggio, e concede questa possibilita' anche !
ai programmatori (pur senza *costringerli* a lavorare anche a basso
livello).
Esiste, ovviamente, un "lato oscuro" di un linguaggio "a due livelli": molti
programmatori non realizzano questa natura del linguaggio (anche perche' di
solito nessuno la spiega :-) e lo utilizzano come linguaggio "ad un
livello", facendo un uso eccessivamente diffuso delle funzionalita'
elementari. Devo purtroppo dire di aver visto questa tendenza trovare il suo
apice tra alcuni (alcuni, non tutti!!) programmatori provenienti dal C con
un background embedded/real-time, dove la giusta attenzione per le
prestazioni talvolta sfocia nella convinzione che solo lavorando *sempre* ad
un basso livello di astrazione si ottengano performance adeguate. I
programmatori piu' esperti sanno invece che una buona incapsulazione porta
spesso a codice *piu' efficiente* di quello che potremmo scrivere (senza
impazzire in sviluppo e manutenzione) lavorando esclusivamente a basso
livello. Per qualche semplice esempio potete dare un'occhiata alle slide del
mio intervento "++Efficienza" (presentat!
o al C++ Forum '97), recentemente rese disponibili sulla mia pagina web,
nella sezione "Formazione e Mentoring").
Seconda conclusione: dietro la facciata di "semplificazione del C++" di Java
e C# si cela in realta' una visione del programmatore molto diversa. Il
programmatore C++, nell'idea di Stroustrup, puo' passare dalla creazione di
librerie sfruttando concetti di livello basso (come i puntatori nativi, il
placement new, ecc) all'uso di astrazioni di livello piu' alto (da lui
stesso create, ma anche di terze parti) rimanendo sempre "dentro" il
linguaggio e in molti casi con una minima impedenza del risultato.
Al programmatore Java/C# non e' riservato lo stesso trattamento. Come il
programmatore Pascal, hanno a disposizione un linguaggio piu' "chiuso", dove
possiamo costruire nuove astrazioni (classi) su un substrato di livello
medio, ma siamo molto distanti dal creare vere e proprie estensioni al
linguaggio, indistinguibili dagli elementi built-in. Questo puo' essere
visto come un lato positivo ("il programmatore fara' meno pasticci") o
negativo ("il programmatore sara' limitato nel suo lavoro ed il risultato
inferiore"), ed anche questo rivela una frattura culturale tra un linguaggio
che ha mantenuto alcune idee del C ("il programmatore sa quello che fa") ed
altri che ne hanno mantenuto principalmente la sintassi.
* Conclusioni
Ovviamente, vi sarebbe ancora molto da dire sulle differenze semantiche tra
C++, Java, C#, che rischiano invece di essere facilmente mascherate dalle
forti similitudini sintattiche. Ho voluto affrontare, in questo numero, i
due elementi che ritengo di maggiore distacco, uno molto pragmatico, l'altro
piu' astratto/metodologico.
Se vi interessa l'argomento, vi invito a farmelo sapere via email, e magari
ne riparleremo per un futuro numero di C++ Informer.
* Bibliografia
[Alger94] Jeff Alger, "How to stop worrying and start loving C++, part 1",
IEEE computer, Vol. 27 No. 6, June 1994.
[Gibbons00] Bill Gibbons, "A Portable typeof operator", C++ Users Journal,
Vol. 18 No. 11, November 2000.
[Henney00] Kevlin Henney, "Valued Conversions", C++ Report, Vol. 12 No. 7,
July/August 2000.
[KM96] Andrew Koenig, Barbara Moo, "Ruminations on C++", Addison-Wesley,
1996.
[Nelson00] Elden Nelson, "Interview With Bjarne Stroustrup", Visual C++
Developers Journal, May 2000.
------------------------------------------------------------------------
Estratto da c++ informer - (c) carlo pescio - eptacom consulting
mi pare un confronto definitivo, ragionato e sopratutto autorevole.
Non c'è che rassegnarsi :-)
Angelo

1) l'estratto che hai inserito sul ng fa parte di una pubblicazione sotto
copyright, in cui l'autore vieta espressamente questi avvenimenti senza la
sua autorizzazione preventiva.
2) potevi condensarne il contenuto invece di fare "cut & paste" di tutte le
righe (troppe). Chi è in grado di rispondere a questo quesito ha buona
probabilità di essere già abbonato a C++ Informer dunque potrebbe evitare di
leggere *ancora* le stesse righe.
Nel merito in effetti nelle FAQ di it.comp.java c'è una sezione dedicata ad
un semplice confronto Java/C++ anche se non contempla le osservazioni
approfondite portate da C.Pescio.
Attento però che parlare di Java non significa parlare, semplicemente, di un
nuovo linguaggio di programmazione. Più congruentemente con le possibilità
introdotte dai progettisti della Sun, parlare di Java significa parlare di
una Architettura per il Mobile ed il Distribuite Computing.
Volendo invece un confronto a livello del solo linguaggio di programmazione,
ritengo tutte le osservazioni di Pescio ineccepibili: se aggiungi in
semplificazione perdi in controllo e viceversa.
Personalmente invece sono rimasto stupito nel leggere che a conti fatti
scrivendo codice C++ "corretto" (che io spero prima o poi di arrivare a
scrivere, anche senza mai arrivare alla perfezione di dei dell'olimpo come
Pescio :-) il codice da scrivere in Java/C# è molto maggiore. A dire il vero
C# in effetti non lo considero promettente per nulla. Vorrebbe essere come
Java senza essere Java; in realtà è destinato sicuramente ad una posizione
di nicchia, proprio perchè non si può far passare in secondo piano che C# è
"solo" un linguaggio (uno dei tanti) forse un "poco" migliore di altri
"linguaggi" per fare "alcune" cose su WinZozz. In particolare sembra
facilitare la scrittura di servizi COM, ma se MicroZozz guardasse un pò più
avanti del proprio naso si poteva risparmiare lo sviluppo di C# (e magari ci
rimandava il grande Anders Hejlsberg alla Borland, cioè a casa ;-) e poteva
sfruttare Python, magari arricchendolo con qualche libreria non proprietaria
per COM, dato che ormai sembra poter divenire lo standard per i servizi
CORBA.
Che altro c'è da dire? la portabilità binaria di Java è sicuramente una
buona cosa, anche se come idea è vecchia come l'acqua calda. Sun ha avuto
solo la forza commerciale, e si è trovata nel momento buono, per poter
re-introdurre una idea vecchia come l'informatica. Certo è che pensare a
quando smanettavo con l'assembler del mio 6502 per guadagnare qualche ciclo
di clock, e vedere oggi un programma Java che "affanna" il mio Athlon
900/128Mb/HDD ATA100 bhe...un pò mi rattrista :-).
Chi si dedicherà ormai più a scrivere programmi o compilatori super
ottimizzati/ottimizzanti, a spulciare l'assembler, quando si sarà diffusa la
filosofia del "Write Once Run Always" che Java promuove??
-------------
Lello Mele
Via San Sebastiano, 22
84036 Sala Consilina (SA)
P.Tel.:0347/643.35.25
HOMEPAGE:
http://www.programmazione.it
http://tcp.come.to
eMAIL:
v...@libero.it
me...@programmazione.it
PROFILE:
http://www.programmazione.it/index.php?entity=eauthor&idAuthor=83
-------------
"Esistono due modi per elaborare il progetto di un software.
 Il primo consiste nel renderlo semplice al punto tale che non
 manifesti lacune evidenti. Il secondo consiste nel renderlo
 complicato al punto tale che non manifesti lacune evidenti.
 Il primo metodo e' di gran lunga piu' difficile." [C.A.R. Hoare]

 writes:
am> Estratto da c++ informer - (c) carlo pescio - eptacom consulting
am> mi pare un confronto definitivo, ragionato e sopratutto autorevole.
am> Non c'è che rassegnarsi :-)
Rassegnarsi ? Il C++ è un linguaggio per language lawyer. Quante volte
quella libertà  serve veramente  ? Stroustroup non  è un  semidio(*) e
anzi  gli   hanno  coniato  dietro   Sturmtruppen.  E  non   gente  da
poco. Checché si parlino dietro  questi lawyer, la gente che scrive la
più veloce libreria  per il calcolo della FFT  (vincitrice tra l'altro
di un award pluriennale) lavorano in C...
(*) Gosling si e Kernighan si.
--
Gian Uberto| main(){printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
Lauri      |                        David Korn, 1987 ioccc best one liner 
(aka saint)|-------------------------------------------------------------------
em@ail: sierra alfa india november tango AT echo november golf DOT india tango

leggere *ancora* le stesse righe.
Non esagerare, io ho apprezzato molto l'articolo se tu sei abbonato
alla rivista buon per te, e non cominciamo col dire i soldi per scaricare
100 righe invece di 20... ho già fatto i conti tempo fa!
Se vuoi 200 lire te le rimborso io :P
Bye

 (*) Gosling si e Kernighan si.
Sei sicuro di quello che dici o parli per sentito dire?? non si possono
sparare sentenze sbagliate senza motivarle!!
Prima cosa, Kernighan è sicuramente un grande ma perchè sarebbe un dio?? mi
sa che hai preso un granchio dato che chi è stato il vero progettista e
primo implementatore del C è Dennis Ritchie (ti consiglio un giretto su
http://cm.bell-labs.com/cm/cs/who/dmr
 la homepage di Ritchie).
Gosling poi sarebbe un dio perchè? perchè ha progettato un linguaggio "nella
seconda metà degli anni '90" (quando ormai tutti avevamo almeno un Pentium e
32 Mb di ram!!!) sfoltendo e aggiungendo feature al C++ e sfruttando la
forza commerciale di Sun per affermare un linguaggio multipiattaforma,
riscoprendo l'acqua calda? anche lo splendido Locomotive Basic del 1990 (o
giù di lì) che girava su GEM, una interfaccia grafica della Digital Research
per MS-DOS, prima dell'avvento di Win, era compilabile su tutte le
piattaforme senza modifiche al sorgente, ed aveva un ambiente 4GL da far
impallidire molti Visual qualcosa. Solo che lui, come molti altri prima e
dopo, era nato troppo presto per i tempi.
E ovviamente tu da ignorante (nel senso che ignori) non sai che il C++ è un
linguaggio real-world progettato ed usato già dagli
anni 80 (vent'anni fa bello mio, tu ce li hai vent'anni dato che ignori?),
quando i PC si chiamavano al massimo Commmodore 64 o Zx Spectrum (mettilo lì
Java e VM a girare!!).
Da quando è stato progettato il C++ è servito a migliaia di programmatori
per implementare OGNI tipo di software, software veloce ed affidabile, e su
macchine che del java non avrebbero potuto far girare neanche la VM.
Java oggi è davvero una splendida piattaforma applicativa, ma il fatto che
lo sia diventata dipende anche e soprattutto dai tempi maturi per un
prodotto del genere, dalla forza commerciale di Sun, e sopra ogni cosa dal
successo che ha ottenuto grazie alla diffusione di internet. Poi trascinato
da questo successo e supporto (perchè per sviluppare ci vogliono anche è
soprattutto i soldi) è maturato in breve tempo (in quanto a funzionalità e
libreria) ed è potuto diventare quello che oggi è. Anzi, lo sai che java era
nato (ad uno stato embrionale) all'interno di un progetto hardware poi
abbandonato da Sun perchè ritenuto non interessante? altro che dio..c'è
mancato poco che gli tagliassero i fondi!
Scusate lo sfogo. Ma quando è troppo è troppo.
LM

[...]
Quest'affermazione e' quantomeno discutibile, e richiede tante di quelle 
condizioni al contorno da essere praticamente inutile. Java e' ben lungi da 
essere un linguaggio perfetto (da ogni punto di vista), ma la distanza per 
arrivare da zero a codice corretto (leggi: produttivita') e' decisamente minore 
che per C++. E le dimensioni del codice (in cosa: classi? LOC? caratteri?) non 
mi paiono particolarmente maggiori, in media (anche se ovviamente si dovrebbero 
fare delle statistiche).
In quanto al buon Stroustrup, che comunque rimane nella mia opinione 
definitivamente in gamba, la sua nota frase (proprio nell'intervista di Carlo 
Pescio) 
<quote>
Time will tell how it will fare as a general-purpose language, and how the 
majority of programmers and managers will react when they discover that the 
"security" of Java *and in particular of Javascript* leaves much to be desired. 
</quote>
(gli asterischi sono miei) dimostra che per tutti quanti la tentazione di 
parlare di cio' che non si conosce a fondo e' talvolta irresistibile. Nessuno, 
per fortuna, e' un dio. ;-)
-- 
God is Real, unless declared Integer.
ObjectZone - 
http://space.tin.it/computer/csadun

 -------------
mi permetto una critica "costruttiva" sulla tua firma (interessante,
seria e professionale, ma con un difetto: ) di 23 righe: avevo sentito
dire che piu` di 4 e` "maleducazione"... o non c'e` piu` nessuno che ci
fa` caso, cosi` come non ci sono piu` programmatori assembler? ;-)
-- 
Ciao,  |  I play Russian roulette everyday, a man's sport,
Matteo |  With a bullet called life, yeah called life, (SUGAR)

 Gosling poi sarebbe un dio perchè?
la fede e` fede e non si discute! :-)
scherzi a parte (visto che di "fede" nell'informatica e` sempre bene
averne poca...), anche il trovarsi al posto giusto al momento giusto con
l'idea giusta (originale, vecchia?) puo` essere un merito. Con la
fortuna ci puoi vincere una lotteria, non creare un linguaggio di
successo: questo e` un merito. Sfruttare la forza commerciale di SUN e
riuscire a contrastare lo strapotere di M$ da una parte e offrire
un'alternativa (in alcuni campi, certo, chi lo nega?) a linguaggi
consolidati come C++, C e PERL (ci dimentichiamo le CGI/Servlet?) e` un
merito, non una colpa.
Da come mi ricordo io la storia Gosling non s'era messo in testa di
sviluppare un bel linguaggio di successo con cui diventare famoso: stava
lavorando ad un progetto particolare x il quale gli serviva un
linguaggio con certe caratteristiche, e visto che tutto quello che gli
serviva non c'era in alcun linguaggio, ha pensato bene di crearne uno
nuovo mettendo insieme il meglio di quello che c'era in giro, sfruttando
un'esperienza trentennale nello sviluppo di linguaggi di programmazione,
imparando dagli errori altri (e, in seguito, dai propri). Magari se il
suo scopo fosse stato progettare il miglior linguaggio del mondo avrebbe
agito diversamente, ma da uomo pratico (xche` diciamocelo, non e` poi lo
stesso modus operandi che seguiamo nel nostro piccolo tutti noi tutti i
giorni?) cosi` ha deciso e cosi` e` andata, accontentiamoci.
Insomma: riscoprire l'acqua calda lo possono fare tutti, ma e`
convincere anche la gente ad usarla che e` difficile! C'e` chi
nonostante tutto preferisce le docce fredde: questione di gusti. C'e`
chi la usa pure a tavola al posto della minerale: contento lui... 
diciamocelo: che poi te la vendano come una novita`, questo e` il
mercato: se non dici che e` nuovo nessuno ne parla, e non puoi sperare
di vendere niente a nessuno. ...ma non e` che tutti quelli che comprano
siano degli sprovveduti!
Che poi il miglior linguaggio del mondo e` ancora da inventare (o
-peggio ancora- e` uno dei migliaia di progetti ormai falliti e
dimenticati) siamo d'accordo.


 artefice dell'ambiente x il quale e` nato: UNIX. O sbaglio?
Sbagli :-). Unix è stato realizzato da Ken Thompson in linguaggio B, aiutato
in seguito da Ritchie. kernighan faceva parte del team è contribui a
scrivere il libro!

 CORBA.
Innanzitutto ciao a tutti, è la prima volta che scrivo su questo ng.
Volevo solo precisare, conoscendo bene Python, che esistono già librerie
free per COM/Python, che permettono sia di utilizzare da Python oggetti COM
dotati di interfaccia IDispatch sia di realizzare (in maniera peraltro
semplicissima) server COM in Python...
Per quanto riguarda C#, a mio avviso si tratta più che altro di un tentativo
di MS di "combattere" Java con qualche cosa di cui ha pieno controllo...
Ciao, Mario

> di un award pluriennale) lavorano in C...
"> può anche darsi (tu sei sicuro che il codice C++ che ha concorso era scritto
"> correttamente?? i moderni compilatori C++ sono molto ottimizzanti (n.d.r.
"> leggi C++ informer #1 o #2 p.e.), e consentono di ottenere prestazioni
"> simili se non superiori agli equivalenti programmi C (ed io sono prima un
"> programmatore C che C++)
Si  che sono sicuro  :). 
www.fftw.org
  se vuoi  i dettagli.  Credo che
potrebbe demolire alcune delle tue convinzioni...
Sai, quella è  gente (CS Theory al MIT) che lavora  su macchine il cui
costo di esercizio è notevole (Intel Paragon, CM5, Sun 10000 StarFire)
e per loro _ogni_ ciclo macchina costa...


> (*) Gosling si e Kernighan si.
"> Sei sicuro di quello che dici o parli per sentito dire?? non si possono
"> sparare sentenze sbagliate senza motivarle!!
"> Prima cosa, Kernighan è sicuramente un grande ma perchè sarebbe un dio?? mi
"> sa che hai preso un granchio dato che chi è stato il vero progettista e
"> primo implementatore del C è Dennis Ritchie (ti consiglio un giretto su
"> 
http://cm.bell-labs.com/cm/cs/who/dmr
 la homepage di Ritchie).
Effettivamente per Khernighan c'è un errore (a te consiglio di andarti
a leggere l'hacker jargon file.  Sopratutto se pensi che un hacker sia
uno che si diverte ad entrare  nelle macchine altrui - nota, io non mi
ritengo all'altezza di tale titolo), i nomi erano Ritchie & Thompson:
   :demigod:  n.  A  hacker  with years  of  experience, a  world-wide
reputation,  and a  major  role in  the  development of  at least  one
design, tool, or game used by or known to more than half of the hacker
community.   To  qualify  as   a  genuine  demigod,  the  person  must
recognizably identify with the  hacker community and have helped shape
it.   Major   demigods  include   Ken  Thompson  and   Dennis  Ritchie
(co-inventors of  {{Unix}} and {C}), Richard M.  Stallman (inventor of
{EMACS}), Larry Wall (inventor of {Perl}), Linus Torvalds (inventor of
{Linux}), and  most recently James Gosling (inventor  of Java, {NeWS},
and {GOSMACS}) and Guido van  Rossum (inventor of {Python}).  In their
hearts  of hearts,  most hackers  dream of  someday  becoming demigods
themselves, and more  than one major software project  has been driven
to completion  by the author's  veiled hopes of apotheosis.   See also
{net.god}, {true-hacker}.
				(Hacker Jargon File 4.2.0)
Tra l'altro di Java hai capito un po' pochino.
Cominciamo  col  dire  che Java  non  è  nato  per i  pentium  (orrida
architettura)  ed è  nato quando  era  tempo che  nascesse. Non  credo
proprio  che sia  nato  perché Sun  (una  ditta fondata  e gestita  da
Hackers molti dei  quali già al MIT AI Lab)  ma sia stato riconosciuto
valido da Sun, così come è stato per News.
Java non è  nulla di innovativo.  E' un condensato  di esperienze e di
buone  idee, alcune  "antiche" (ad  esempio l'essere  a byte  code con
memorizzazione solo  sullo heap  può essere fatto  risalire al  LISP -
rispetto al  quale supera alcuni problemi di  esistenza delle funzioni
oltre  lo  shutdown  della  LISP  machine)  messo  insieme  con  molta
saggezza.
Nota : Prima  dell'avvento del C, il LISP  era il linguaggio preferito
da quella gente per programmare, e devo dire a ragione.
La cosa  che mi è piaciuta  di più di  Java è stata la  coerenza della
progettazione,  non  ci vedo  punti  poco  definiti  e il  nucleo  del
linguaggio  è  snello  e  semplice  (non facile,  semplice).  C'è  una
_montagna_ di librerie (packages) nel JDK, ma sono librerie.
Il C++ non lo è. L'articolo che era stato riportato contiene una frase
emblematica  di  Stroustroup  :  "cercherei  di  fare  il  nucleo  del
linguaggio  molto  piccolo".  C++  è  un  linguaggio  che richiede  un
Language Lawyer e  sebbene offra una libertà di  azione estrema non lo
fa in un modo -opinione personale, ma non mi sembra di essere l'unico-
buona o  per lo  meno bella.  Un esempio può  essere la  dimensione in
caratteri di  un libro  come lo Stroustroup  confrontato col K&R  o il
Gosling.
Credici o no, io lavoro  quotidianamente con una LISP machine sotto le
dita, mi permette di  essere molto produttivo (probabilmente in alcuni
campi  ho raggiunto  una produttività  che la  gente non  si aspettava
grazie a questa LISP machine). Sono fermamente convinto che quello che
faccio col LISP ora lo potrei fare col Java, compreso definire al volo
del codice  che mi risolva in  fretta un problema  di aggiornamento di
alcune  decine di file  sorgente. (Purtroppo  non ho  il tempo  per il
progetto - La  LISP machine che uso  è alla versione 20 e  passa ed ha
circa 20 anni di sviluppo alle  spalle, io dovrei partire da quasi 0).
Quello che faccio col LISP e potrei fare col Java, col C (o il C++) lo
potrei fare solo col concorso di un'altro linguaggio.
Sicuramente utilizzerei il Java (compilato nativo) per i programmi che
attualmente  mi  tocca  scrivere   in  VB,  sicuramente  ora,  potendo
scegliere, sceglierei  o JSP  o PHP per  l'applicazione che  invece ho
sviluppato in ASP (circa 40k righe).
D'altro canto  ci sono  cose che  in Java non  farei mai,  nemmeno con
quello compilato nativo a meno  di avere un processore Java in silicio
- e forse ancora lo scarterei. Quelle le farei in C, o in C++.
Morale: nessun linguaggio è universale.
Errore grosso di  tanti guru del C++ che  oramai "hanno perso contatti
con la realtà" e vivono in un mondo teorico, quello che il mio docente
di Applicazioni del Calcolatore Elettronico (ora Elaborazione Numerica
del Segnale) chiama "il mondo della lavagna".
<folklore>
Io adoro il C. Potendo lavorerei  sempre in C.  Ho dimostrato di saper
scrivere  codice dannatamente  efficente arrivando  a superare  con un
algoritmo banale codificato in maniera spinta un algoritmo decisamente
superiore dal punto di vista teolrico.
</folklore>


>>>>> """ == "  <Lello> writes:
"> Sbagli :-). Unix è stato realizzato da Ken Thompson in linguaggio B,
Credo  che la  prima  versione abbia  fatto  largo uso  dell'assembler
PDP-7, adesso non ho sottomano il K&P con la storia. La prima versione
riscritta in C dovrebbe essere del '72 (vado a memoria - volatile :) )
"> aiutato
"> in seguito da Ritchie. kernighan faceva parte del team è contribui a
"> scrivere il libro!
Non solo. Adesso tu mi dici quale programma ha scritto insieme ad Aho e
Weimberger :)


  e il  nucleo  del  linguaggio  è  snello  e  semplice  (non facile,
semplice).
Perdonami, ma il nucleo del linguaggio di java non è c++?? ok forse è più
corretto dire che
in nucleo del linguaggio di java è costituito *solo* dal 95% di c++.
Cordialmente
LM

>(
http://www.mediamente.rai.it/home/bibliote/intervis/m/mcnealy.htm
)
Questa mi può interessare, quando avrò tempo...
"> O se preferisci puoi leggere le note storiche p.e. su "Usare Java - T.
"> Ritchey - Jackson - 1996" (ndr: il libro è del 1996, data in cui l'ho
"> acquistato)
Hai mica l'editore originale per l'inglese ? Non mi piace Jackson come
editore. Traduce  decisamente male. Vedi ad esempio  la prima edizione
del K&R coi commenti innestati ...
"> *almeno* meglio di te)
Puoi  avere   ragione  (a  Java  mi  sono   interessato  tardi,  prima
sinceramente non ne sentivo l'esigenza) ma puoi avere torto.
"> Perdonami, ma il nucleo del linguaggio di java non è c++?? ok forse è più
"> corretto dire che in nucleo del linguaggio di java è costituito *solo* 
"> dal 95% di c++.
Credo che  il paragone col C++  sia fuorviante, anche se  il fatto che
usa la sintassi del C induce a questa considerazione.
Vero che  C++ è più  potente, ovvero permette  più cose e  quindi java
offre  un subset  delle possibilità  del C++.  Ma dire  che Java  è un
subset del C++ non è proprio corretto.
Java è un linguaggio che esclude  a priori cose che altri linguaggi OO
permettono e non ci piove ma dall'altro canto prevede come intrinseche
al linguaggio cose che altri linguaggi non prevedono, come i thread.
A proposito, il programmino scritto da Aho, Weinberger e Kernighan :)?


 A proposito, il programmino scritto da Aho, Weinberger e Kernighan :)?
Ci siamo riddotti ai giochi a quiz come ultima spiaggia vedo! :-))
Rassegnati...AWK non è un programmino (o pensi che sia un *programmino*??)
ma un bellissimo linguaggio di script unix (con vari porting) utilizzabile
per tante
pregevoli cose, (p.e. io lo utilizzavo come filtro x file di testo, tu?).
Eccoti uno stralcio di codice per estrarre mittente e soggetto dalle email:
#!/usr/bin/awk -f
  # filtro AWK per estrarre mittente e soggetto da una mail
  ($1 == "From:")    { print(substr($0,7)) } # mittente
  ($1 == "Subject:") { print(substr($0,10)) } # soggetto
...e dopo di ciò restò il vuoto.....:-)..bye bye!!
LM

(lo Zingarelli please!)
[...]
I due punti di domanda sono scorretti, dopo un punto di domanda ci va
la maiuscola, subset e' un anglicismo inutile dato che al suo posto
puoi usare il corrispondente termine *italiano* "sottoinsieme" e
infine, almeno se ci fermiamo ad analizzare le perle migliori che hai
infilato nell'ultima frase, mi pare arrogante ed *offensivo*
rivolgersi all'interlocutore invitandolo continuamente a leggersi lo
Zingarelli, soprattutto se, come nel tuo caso, lo Zingarelli non lo si
e' nemmeno mai aperto.
Michele

> A proposito, il programmino scritto da Aho, Weinberger e Kernighan :)?
"> Ci siamo riddotti ai giochi a quiz come ultima spiaggia vedo! :-))
Quelli adatti a te :)
"> #!/usr/bin/awk -f
">   # filtro AWK per estrarre mittente e soggetto da una mail
">   ($1 == "From:")    { print(substr($0,7)) } # mittente
">   ($1 == "Subject:") { print(substr($0,10)) } # soggetto
Banale. E con questo è provato che da te non ho nulla da imparare.


 e' nemmeno mai aperto.
Tu sei un grande, davvero :-)
Massimo

Anzi semmai avrà fatto pubblicità! :P
C++ Informer e' una newsletter gratuita.
--
Andrea Laforgia (ICQ 87230455)
http://www.geocities.com/AndreaLaforgia
"ci sono tre tipi di persone nel mondo : quelli che
 sono bravi in matematica e quelli che non lo sono."

Tu sei un grande, davvero :-)
La grandezza e' sempre relativa al *campione* usato per fare il
confronto :-)
Ad ogni modo credo che il mio 1.83 sia ormai poco piu' della media
attuale, media che continua a crescere nel tempo: e' per questo che si
notano sempre di piu' quelli che sono notevolmente piu' piccoli
rispetto a questa media ... ;-)
ciao, Michele

 Il primo metodo e' di gran lunga piu' difficile." [C.A.R. Hoare]
Ehi ! ma questa era la mia firma !!
E l'hai usata senza chiedermene il permesso ! :)


 I due punti di domanda sono scorretti, dopo un punto di domanda ci va


 Michele
Hai sicuramente ragione, ma io ho una formazione ingegneristica non
letteraria. Dunque più che rispondere sui punti e virgola, ho fatto delle
precisazioni tecniche e storiche. Il fatto che, probabilmente, sono stato
arrogante e *offensivo*, come dici tu, si adegua al tono dei messaggi
postati dal soggetto in questo thread; anche perchè leggere continuamente
messaggi al limite delle guerre di religione tra i vari linguaggi
(soprattutto quando supportati da affermazioni completamente fasulle o
travisate) diventa a lungo andare noioso. Così come è noioso il soggetto.
Anche tu in realtà sei noioso. Il tuo è il tipico messaggio (tipico anche
nell'attaccarsi alla lingua) di chi non ha niente da dire nel merito, ma
vuole (o deve?) comunque parlare.
Dai fiato alle trombe allora.
Cordialmente.
LM

 Banale. E con questo è provato che da te non ho nulla da imparare.
Chi ha mai voluto insegnare qualcosa a qualcuno?
vedo che ci sei rimasto male, :-(, avevi quasi accarezzato l'idea di
avermi messo in difficoltà, e invece ti ho risposto addirittura "in-topic"!
Capita. Puoi sempre riprovare fra una decina d'anni.
LM

it.politica.lega-nord e vieni a romperli qui?
Sei OT, oltre che maleducato: cosa c'entra la politica adesso?
Ma hai letto il newsgroup sul quale stiamo postanto?
i-t.-c-o-m-p.-j-a-v-a!
Michele

LM
ciao, Michele

 wrote:
LM> Hai sicuramente ragione, ma io ho una formazione ingegneristica non
LM> letteraria.
Postare un  articolo, peraltro interessante, che sotto  sotto dice <si
ma  noi  cor C++  avemo  er mejo>  (opinione  opinabile  da guerra  di
religione)  in  un gruppo  di  un  altro  linguaggio e  proporlo  come
<opinione autorevole che chiude i discorsi> mi pare un gesto per nulla
amichevole, io  di solito reagisco  in maniera iconoclasta:  nella mia
formazione ingegneristica  (non proprio standard) c'è  il concetto che
nulla in informatica  è assoluto (se non nel  "mondo della lavagna") e
che ci sono sempre  almeno due modi per fare ogni cosa,  e non è detto
che uno sia migliore dell'altro.


 ha scritto nel
messaggio news:3a6fd577...@news.goldnet.it...


Io l'ho letto, e tu? infatti siamo su it.comp.java NON su
i-t.-c-o-m-p.-i-t-a-l-i-a-n-o. Se vuoi imparare qualcosa LEGGI e stai ZITTO,
invece di rompere a chi sa più di te e probabilmente scrive anche perchè
degli zoticoni come Te e G.U. Lauro (un altro "genio"!) impariate qualcosa,
utilizzando parte del suo tempo. Lello scrive su 
www.programmazione.it
 e su
The Coder's Primer, voi invece rompete, rompete, ...mamma quanto ROMPETE!!
Francesco

i-t.-c-o-m-p.-i-t-a-l-i-a-n-o. 
Vedo che continui a non capire: il rispetto della netiquette e' un
argomento IN TEMA, *qualsiasi* sia l'oggetto di interesse di un
determinato gruppo. Il mio intervento voleva precisare proprio questo:
al di la' delle differenti opinioni che ognuno di noi puo' avere a
riguardo di certe questioni, il fondamento affinche' ci sia un dialogo
costruttivo e' che ognuno si sforzi di rispettare le *elementari*
regole del vivere civile, come peraltro hai giustamente ma
involontariamente suggerito tu:
"e scrivi queste quattro PUTTANATE che anche mio figlio alle
elementari saprebbe evidenziarle"
Regole delle quali, per quanto elementari e scontate, tu sembri invece
farti un baffo.
Capisci cosa intendo dire? Vatti a leggere la netiquette (*non* lo
Zingarelli, il Galarateo di Emily postnews ad esempio potrebbe fare al
caso tuo), e poi potremo parlare anche di Java, sempre che questo ti
interessi, cosa di cui, a questo punto, dubito molto.
In caso contrario invece, fatti e facci un piacere, stai zitto tu, che
ti risparmi solo brutte figure.
Michele
p.s. imposto il follow-up a it.discussioni.litigi nel caso ti
interessasse continuare ancora questa interessante discussione: li'
finalmente potrai parlare a tuo agio, senza nessun pericolo di essere
OT.




 writes:
FDV> degli zoticoni come Te e G.U. Lauro (un altro "genio"!) 
Beh, tu con la lettura hai qualche problemino :)


 ha scritto nel messaggio
FDV> news:upuhcnj3y.fsf@MERCURIO_115.eng.it...
FDV> GRANDISSIMO LELLO!!
FDV> Dalle mie parti si dice che lo hai proprio STUTATO!
Stuà chi, ocon ?
Vabbé ognuno  sceglie i  miti che preferisce...  Io da Lello  a quanto
pare non ho nulla da imparare, ergo gradirei piantarla.


 pare non ho nulla da imparare, ergo gradirei piantarla.
MHA DAVERO?
- hai fatto una figura di merda perchč non sai un caxxo (anche solo
storicamente) di C o C++ o JAVA
- hai fatto una figura di merda con quella domandina trabocchetto (modello
elemantari-materne) su AWK
- sei entrato in talmente tante contraddizioni da dover inventare
addirittura storie con la tua mailbox
E NON HAI NIENTE DA IMPARARE??
FAI BENE, PERCHE' SAREBBE TROPPO DIFFICILE.
P.S.: si č capito che ti č "bruciato" il buchetto del sedere comunque!!
fattelo bruciare ancora meglio andando su 
www.lellomele.supereva.it
!!
---
Francesco De Vito

 invece di rompere a chi sa più di te
pace e bene... e *plonk*
-- 
Ciao,  |  I play Russian roulette everyday, a man's sport,
Matteo |  With a bullet called life, yeah called life, (SUGAR)