PDA

View Full Version : John Carmack on Haskell, Scheme, and game engine architecture.


shinya
29-08-2013, 19:47
Se non l'avete ancora visto...

https://www.youtube.com/watch?v=1PhArSujR_A&list=PLqSz8wYk5VJTsadQnU9EId6G0AJWA6o0q&t=2m5s

banryu79
30-08-2013, 11:54
Interessante l'idea di Carmack spiegata verso la fine del video. Forse, al riguardo, potrebbe farsi un giro su Erlang...

+1 per Haskell comunque.
La purezza ha mietuto un'altra vittima :D

e +1 pure per i Garbage Collector :asd:

P.S.:
Certo che se uno non ha mai neanche fatto un tutorial con uno dei linguaggi di cui parla, è difficile afferrare/apprezzare l'essenza di quello che dice, anche se si è programmatori.

P.P.S.:ah, e dimenticavo... +1 pure per lo Static Typing :)


@shinya: A proposito di linguaggi esotici, hai già provato Rust (http://www.rust-lang.org/)?

marco.r
30-08-2013, 15:57
Se uno non capisce quel che dice Carmack in questo video, a maggior ragione dovrebbe almeno provare per un po' i linguaggi menzionati.

giustamente Haskell viene criticato piu' per la laziness che la funzionalita'... e in effetti prevedere le latenze e tempi di esecuzione diventa problematico.

Io sono in attesa ancora di un linguaggio con l'immutabilita' di Haskell, le macro di un Lisp e una maggior facilita' di interfacciamento con le librerie C/C++... Rust sembra avvicinarsi un po', ma me lo devo prima guardare per bene. (D non e' male in tal senso, anche se ancora lontano).

banryu79
30-08-2013, 16:08
... Rust sembra avvicinarsi un po', ma me lo devo prima guardare per bene. (D non e' male in tal senso, anche se ancora lontano).
Oggi ho avuto il pomeriggio libero, e me lo sto guardando.
Sembra promettente, ma è ancora crudo. Sicuramente da tenere d'occhio.

Cmq, a proposito di Carmack: è bello vedere che nonostante l'esperienza ha ancora voglia di sperimentare e mettersi in gioco.

marco.r
30-08-2013, 23:04
Oggi ho avuto il pomeriggio libero, e me lo sto guardando.
Sembra promettente, ma è ancora crudo. Sicuramente da tenere d'occhio.

Ah certo, d'altra parte e' alla versione 0.7 dopo neanche due anni di sviluppo.
Le idee pero' sono buone (o meglio, sono quelle che piacciono a me :D).
Mi sono sempre chiesto perche' gran pochi linguaggi implementino il concetto di immutabilita' o perlomeno di const-correctness. E' una di quelle cose che rende pure qualcosa come il C++ piu' usabile da un un essere unmano..



Cmq, a proposito di Carmack: è bello vedere che nonostante l'esperienza ha ancora voglia di sperimentare e mettersi in gioco.
Ah si', d'altra parte al di la' del fatto di programmare in se', ritengo che per una persona che deve guidare dei team di sviluppo, sia importante interessarsi a queste cose e cercare di capire cosa puo' aiutare i programmatori ad essere piu' produttivi.

cdimauro
31-08-2013, 06:53
Ah certo, d'altra parte e' alla versione 0.7 dopo neanche due anni di sviluppo.
Le idee pero' sono buone (o meglio, sono quelle che piacciono a me :D).
Mi sono sempre chiesto perche' gran pochi linguaggi implementino il concetto di immutabilita' o perlomeno di const-correctness. E' una di quelle cose che rende pure qualcosa come il C++ piu' usabile da un un essere unmano..
Per me è l'esatto contrario: il C++ è già abbastanza complicato di per sé, per cui garantire la const-correctness di un programma richiede uno sforzo ulteriore da parte del programmatore.

Poi alla fine bisogna anche capire qual è l'obiettivo di un'applicazione. Se, ad esempio, ho a che fare con thread et similia, certamente un programma const-corrected (? :stordita:) aiuta nello gestire la condivisione delle risorse in maniera più safe e più scalabile.

Diversamente da casi come questi, però, devi spendere risorse (umane) per qualcosa di cui magari non hai strettamente bisogno.
Certamente questo strumento può aiutare nell'indivuazione di problematiche relative alla modifica accidentale o scorretta di dati, ma usando TDD e unit testing mi chiedo quanto possa essere utile se alla fine dovrei comunque scrivere dei test che formalizzino il comportamento desiderato (e quindi individuino questi problemi), e ciò a prescindere che il linguaggio metta a disposizione o meno strumenti per la const-correctness.

Tornando al concetto di immutabilità, in Python è presente, per cui spesso ne faccio uso. Ma quando ho a che fare con problematiche intrinsecamente mutabili, beh, non posso fare altro che di ricorrere a oggetti come liste o set, tanto per citare i più noti, o gli usatissimi dizionari (elemento chiave / fondante del linguaggio).
Di questi ultimi, peraltro, non esistono versioni immutabili (per lo meno nella libreria standard, che io ricordi), ma in ogni caso avrei difficoltà a immaginare l'utilità di un dizionario immutabile (se non negli stessi termini di un frozenset, che è l'equivalente immutabile di un set).

Sottolineo, comunque, che parlo da programmatore NON avvezzo alla programmazione funzionale, dove il concetto di immutabilità è molto caro.

C'è da dire, per concludere, che certe a problematiche difficili o troppo costose da gestire a livello software, è possibile che provvederanno i processori, mettendo a disposizione nuovi strumenti. Penso al modello transazionale della memoria, che è arrivato di recente. Altro magari ci sarà in futuro...
Ah si', d'altra parte al di la' del fatto di programmare in se', ritengo che per una persona che deve guidare dei team di sviluppo, sia importante interessarsi a queste cose e cercare di capire cosa puo' aiutare i programmatori ad essere piu' produttivi.
Non posso che essere d'accordo. Altrimenti si perde il contatto con la realtà, con le esigenze e le problematiche con cui devono sbattere la testa i programmatori.

Se si vuole essere utili al proprio team, bisogna essere i primi utenti / programmatori del progetto...

P.S. Il video non ho avuto tempo di vederlo, e dubito che ne troverò. Al momento mi sto smazzando altro e ho tante cose da fare...

banryu79
31-08-2013, 10:08
P.S. Il video non ho avuto tempo di vederlo, e dubito che ne troverò. Al momento mi sto smazzando altro e ho tante cose da fare...
E' un peccato perchè è davvero interessante, e ti serve come contesto in cui mettere in prospettiva i commenti poco più sopra.

E sì, so che tu non hai il lusso di poter spendere del tempo per seguire il primo grillo che ti salta in testa, ma davvero: prima o poi prova a farti un tutorial su Haskell. Sono quasi certo che ti piacerà molto :D

Tommo
31-08-2013, 12:31
E via, vediamoci il nostro Carmack post-risveglio-alle-13-del-sabato :D
L'autodocumentazione del codice tipo assert, const, strong/static typing, pureness (https://twitter.com/ID_AA_Carmack/status/373441426136129536) etc mi convince, e mi piace come la pensa Carmack.

Segnalo anche questo qua (http://www.youtube.com/watch?v=VUxcVzpeFqc) che per chi fa i giochetti e' un bel ripassone.

cdimauro
31-08-2013, 13:20
E' un peccato perchè è davvero interessante, e ti serve come contesto in cui mettere in prospettiva i commenti poco più sopra.
Qualcosa l'ho intuita, ma in ogni caso non è un ambito applicativo che m'interessa più di tanto.
E sì, so che tu non hai il lusso di poter spendere del tempo per seguire il primo grillo che ti salta in testa, ma davvero: prima o poi prova a farti un tutorial su Haskell. Sono quasi certo che ti piacerà molto :D
Mi spiace deluderti. L'avevo visto già parecchi anni fa, e non l'avevo digerito. Al contrario di quel che proclama il sito ufficiale, personalmente non trovo leggibile un programma scritto con un linguaggio funzionale, eccetto per alcune cose che richiamano alla mente alcuni concetti / sintassi prettamente matematici.

Haskell è molto lontano dai miei canoni. D'altra parte ogni programmatore ha un proprio background e propri gusti, ed è abbastanza difficile che li cambi se è da un bel pezzo che programma. Ad esempio C e, soprattutto, C++ continuo a non digerirli anche se sono anni che li conosco (specialmente il primo) e ho a che farci tutti i giorni...

Tommo
31-08-2013, 13:57
Visto. Sbaglio o parla perlopiu' dei cavoli suoi?
Non ne ho tratto granche' di utile, pero' era comunque interessante :asd:

Probabilmente lo spunto migliore e' quello sulla purity/static and strong typing.

marco.r
01-09-2013, 17:04
Per me è l'esatto contrario: il C++ è già abbastanza complicato di per sé, per cui garantire la const-correctness di un programma richiede uno sforzo ulteriore da parte del programmatore.

la const-correctness in C++ e' una delle cose che richiede meno sforzo per quello che rendono; e' piu' una questione di abituarsi e poi diventa facile: l'argomento o il risultato non va modificato ? const. Se le cose diventano incasinate vuol dire che c'e' qualcosa che non va nel codice.


Poi alla fine bisogna anche capire qual è l'obiettivo di un'applicazione. Se, ad esempio, ho a che fare con thread et similia, certamente un programma const-corrected (? :stordita:) aiuta nello gestire la condivisione delle risorse in maniera più safe e più scalabile.

Diversamente da casi come questi, però, devi spendere risorse (umane) per qualcosa di cui magari non hai strettamente bisogno.
Certamente questo strumento può aiutare nell'indivuazione di problematiche relative alla modifica accidentale o scorretta di dati, ma usando TDD e unit testing mi chiedo quanto possa essere utile se alla fine dovrei comunque scrivere dei test che formalizzino il comportamento desiderato (e quindi individuino questi problemi), e ciò a prescindere che il linguaggio metta a disposizione o meno strumenti per la const-correctness.

Se il linguaggio non ti permette di modificare un oggetto passato come argomento e' un test in meno che devo scrivere (perche' scrivere un test per formalizzare quello che e' gia' formalizzato nel codice?), per cui se anche spendo risorse da una parte le risparmio dall'altra.
Un caso particolarmente rilevante e' quello in cui il valore di ritorno e' costante, ovvero non dovrebbe essere cabiato dal chiamate. E' piu' difficile da testare perche' non riguarda il codice del metodo in questione, ma il codice chiamante, questo vuol dire che dovrei scrivere un test per ogni chiamante, ricordarmi di farlo se aggiungo del nuovo codice e soprattutto farlo tenendo conto del resto del codice.
In pratica la soluzione piu' semplice e' quella di fare una copia dei dati per andare sul sicuro, e quindi scrivere piu' codice (lasciando stare il fatto che questo vuol dire duplicare le informazioni e magari in alcuni casi non e' quello che voglio).
Poi chiaro, a differenza dell'immutabilita' si tratta di una proprieta' statica che non ha senso in un linguaggio dinamico come puo' essere Python, ma nel momento in cui ho un linguaggio con tipizzazione statica, come puo' essere Java o C#, secondo me e' una mancanza che si sente.


Tornando al concetto di immutabilità, in Python è presente, per cui spesso ne faccio uso. Ma quando ho a che fare con problematiche intrinsecamente mutabili, beh, non posso fare altro che di ricorrere a oggetti come liste o set, tanto per citare i più noti, o gli usatissimi dizionari (elemento chiave / fondante del linguaggio).
Di questi ultimi, peraltro, non esistono versioni immutabili (per lo meno nella libreria standard, che io ricordi), ma in ogni caso avrei difficoltà a immaginare l'utilità di un dizionario immutabile (se non negli stessi termini di un frozenset, che è l'equivalente immutabile di un set).

Ah ma non sono un difensore dell'immutabilita' a tutti i costi; sono pero' convinto che la tendenza sara' a farne un maggiore uso perche' aiuta a scrivere codice piu' sicuro, seppure al costo di abituarsi a programmare in modo un po' diverso.

Tommo
01-09-2013, 18:06
Di questi ultimi, peraltro, non esistono versioni immutabili (per lo meno nella libreria standard, che io ricordi), ma in ogni caso avrei difficoltà a immaginare l'utilità di un dizionario immutabile (se non negli stessi termini di un frozenset, che è l'equivalente immutabile di un set).

IMO l'immutabilita' non dovrebbe essere una proprieta' dell'oggetto in se' ma una proprieta' della relazione tra oggetto e codice chiamante (non e' una supercazzola giuro :asd: ) nel senso che tutti gli oggetti dovrebbero essere intrinsecamente mutabili ma solo scopes che lo richiedono esplicitamente dovrebbero avere il "privilegio" di scriverci sopra, mentre il resto dovrebbe essere read-only di default.
const ref in C++ ci si avvicina, ma la maggior parte degli altri ha sta distinzione mostruosa tra oggetti mutabili/immutabili copincollati (NSString e NSMutableString, NSDictionary e NSMutableDictionary, String e StringBuffer, ma che davero?) e per me e' dovuto al guardare al problema nel modo sbagliato.
Altrimenti hai roba orrenda tipo Java, dove per essere sicuro che chi chiama un getter non ti modifica un membro della classe devi tornargli una copia :muro:

Per me il problema sarebbe risolvibile in modo molto semplice: il codice che crea un oggetto ha una reference read/write (eg. la classe di cui e' membro, lo scope locale e tutti i figli) e ogni volta che viene passata una reference all'esterno questa di default e' read-only, a meno che non venga scritto esplicitamente... una cosa tipo (in un eretico mix di C++ e java)

shared String plsMakeMeAString( String a ) {
a[1] = 'p'; //error: cannot modify 'a' without ownership (operator[] is non-const)
return new String(a);
}
String a( "derp" );
auto s = plsMakeMeAString( a );
s[0] = 'h'; //valido

dove l'ownership di scrittura e' di default del metodo che chiama new, ma il modificatore "shared" dice che la String appartiene anche a chi chiama, che ci puo' scrivere.
Per il compilatore immagino sarebbe piuttosto semplice trovare in automatico i metodi che non si possono chiamare su una ref non-shared perche' modificano l'oggetto, basta controllare che non modifichino membri e cosi' via.
Sinceramente la faccenda dei permessi e' cosi' dappertutto in informatica che mi stupisce che nessuno ci abbia pensato prima :D

Tra l'altro un linguaggio che usa questi modifiers potrebbe fare a meno di un GC, perche' questa cosa mappa anche piuttosto bene l'ownership... basterebbe aggiungere una keyword "move" che toglie il permesso di scrittura a chi ha creato l'oggetto.
Ad esempio, se volessi creare la stringa sullo stack e returnarla come move/shared il compilatore mi darebbe errore perche' l'ownership di un oggetto locale non e' trasferibile in quanto viene distrutto all'uscita dallo scope.
[move|shared] String plsMakeAString( String global )
String local;
local = global; //lo scope locale puo' scrivere sulla sua roba
return local; //error: cannot return a reference to a local object

In tutti i linguaggi esistenti che io sappia o non si puo' proprio creare sullo stack o il compilatore ti fa ritornare un puntatore senza dire niente.

marco.r
01-09-2013, 23:31
IMO l'immutabilita' non dovrebbe essere una proprieta' dell'oggetto in se' ma una proprieta' della relazione tra oggetto e codice chiamante (non e' una supercazzola giuro :asd: ) nel senso che tutti gli oggetti dovrebbero essere intrinsecamente mutabili ma solo scopes che lo richiedono esplicitamente dovrebbero avere il "privilegio" di scriverci sopra, mentre il resto dovrebbe essere read-only di default.

Sono due cose un po' differenti. L'immutabilita' ha alcuni vantaggi (e anche svantaggi chiaramente) rispetto alla "C++-style constness". Quando hai un oggetto const sai che non puoi modificarlo, quando e' immutabile sai che nessuno te lo puo' modificare, e puoi quindi "condividere" uno stesso dato tra piu' parti del programma senza preoccuparti di "controllare" gli accessi all'oggetto.
Proprio per questo e' piu' semplice la condivisione tra piu' thread degli stessi dati; in particolare non hai bisogno di lock e mutex. (Devi trovare una soluzione diversa se vuoi modificarli, ma l'alternativa e' solitamente piu' sicura).
Poi se hai a che fare con funzioni pure e' molto piu' semplice per il compilatore fare common subexpression elimination su chiamate a funzioni.
Infine dati immutabili aiutano la garbage collection: gli oggetti immutabili non potranno mai contenere riferimenti ad oggetti appartenenti ad una generazione piu' giovane. Questo vuol dire ad esempio che se sto ripulendo la nursery e un puntatore mi scappa in una generazione piu' vecchia allora posso smettere di controllare.
Ma il vantaggio maggiore che ho trovato io e' che e' molto molto molto piu' semplice fare ragionamenti sul codice. Guardi la funzione/metodo e puoi fare tutte le valutazioni senza dover pensare a cosa succede nel resto del programma; e' difficile fare paragoni in questo caso perche' bisogna cambiare anche il modo in cui si struttura il programma.


Per me il problema sarebbe risolvibile in modo molto semplice: il codice che crea un oggetto ha una reference read/write (eg. la classe di cui e' membro, lo scope locale e tutti i figli) e ogni volta che viene passata una reference all'esterno questa di default e' read-only, a meno che non venga scritto esplicitamente... una cosa tipo (in un eretico mix di C++ e java)

shared String plsMakeMeAString( String a ) {
a[1] = 'p'; //error: cannot modify 'a' without ownership (operator[] is non-const)
return new String(a);
}
String a( "derp" );
auto s = plsMakeMeAString( a );
s[0] = 'h'; //valido

dove l'ownership di scrittura e' di default del metodo che chiama new, ma il modificatore "shared" dice che la String appartiene anche a chi chiama, che ci puo' scrivere.
Per il compilatore immagino sarebbe piuttosto semplice trovare in automatico i metodi che non si possono chiamare su una ref non-shared perche' modificano l'oggetto, basta controllare che non modifichino membri e cosi' via.
Sinceramente la faccenda dei permessi e' cosi' dappertutto in informatica che mi stupisce che nessuno ci abbia pensato prima :D

Mi sembra che proponi di mettere di default il passaggio per argomenti const con l'aggiunta di un controllo su oggetti memorizzati sullo stack... in realta' il primo lo fa gia' Rust e il secondo lo fa gia' D.


Tra l'altro un linguaggio che usa questi modifiers potrebbe fare a meno di un GC, perche' questa cosa mappa anche piuttosto bene l'ownership... basterebbe aggiungere una keyword "move" che toglie il permesso di scrittura a chi ha creato l'oggetto.

Occhio che stai mescolando le due cose. La condizione che poni tu non e' sufficiente. Se la funzione che "possiede" l'oggetto (in scrittura) termina non puoi assumere che l'oggetto sia eliminabile, in quanto qualche altra chiamata con l'oggetto non modificabile.
Ci sono altre tecniche per eliminare (o cmq ridurre) l'uso della GC, che sfruttano una escape analysis per allocare la memoria in un blocco unico che viene liberato all'uscita della chiamata (in sostanza uno heap locale in cui viene allocata tutta la memoria che si e' dimostrato non verra' mai ritornata al chiamante).
Per inciso questo e' un altro caso in cui avere dati immutabili o almeno costanti aiuta, perche' se un argomento non lo posso modificare,sicuramente non ci scappera' dentro un oggetto che alloco nella funzione corrente...

In tutti i linguaggi esistenti che io sappia o non si puo' proprio creare sullo stack o il compilatore ti fa ritornare un puntatore senza dire niente.
Come dicevo sopra questa cosa la fa D; se guarti Rust invece ha tutta una serie di funzionalita' per la gestione della memoria in modo safe.

cdimauro
03-09-2013, 06:44
la const-correctness in C++ e' una delle cose che richiede meno sforzo per quello che rendono; e' piu' una questione di abituarsi e poi diventa facile: l'argomento o il risultato non va modificato ? const. Se le cose diventano incasinate vuol dire che c'e' qualcosa che non va nel codice.
Sì, credo anch'io sia una questione di abitudine. C++ è un linguaggio complesso e che mette a disposizione parecchie funzionalità, per cui necessita di tempo per assimilarle e metterle in pratica; dopodiché diventano parte naturale del processo di sviluppo del codice.
Se il linguaggio non ti permette di modificare un oggetto passato come argomento e' un test in meno che devo scrivere (perche' scrivere un test per formalizzare quello che e' gia' formalizzato nel codice?), per cui se anche spendo risorse da una parte le risparmio dall'altra.
Perché i test vengono scritti per controllare il comportamento che dovrebbe avere il codice (oltre che per documentarlo), e perché per definizione non ci si può fidare del lavoro di un programmatore. :)

Tanto per fare un esempio diverso, se scrivo una classe in cui faccio uso di una lista internamente che inizializzo mettendola vuota nel costruttore, in uno dei primi test che controllano il comportamento della nuova classe io metto lo stesso un controllo per vedere se la lista è vuota, anche se lo so che è così perché il codice l'ho scritto io. Ma non posso prevedere eventuali errori nell'averlo fatto, o se un domani rimettendoci mano per sbaglio modifico proprio quella parte. Per questo esplicito nel test il comportamento che mi aspetto; e mi è pure utile per documentarlo, questo comportamento.
Un caso particolarmente rilevante e' quello in cui il valore di ritorno e' costante, ovvero non dovrebbe essere cabiato dal chiamate. E' piu' difficile da testare perche' non riguarda il codice del metodo in questione, ma il codice chiamante, questo vuol dire che dovrei scrivere un test per ogni chiamante, ricordarmi di farlo se aggiungo del nuovo codice e soprattutto farlo tenendo conto del resto del codice.
Sì, mi rendo conto che testare situazioni come queste può essere complicato e richiedere parecchio tempo. Ma... va fatto per il discorso di cui sopra.

Ci sono linguaggi che consentono di testare i cosiddetti invarianti. Non è il caso di Python, purtroppo, per cui risolvo effettuando una copia dell'oggetto restituito e scrivendo un metodo, usato nei test in cui serve, che controlla che l'oggetto che è stato restituito sia identico alla copia che è stata fatta.
In pratica la soluzione piu' semplice e' quella di fare una copia dei dati per andare sul sicuro, e quindi scrivere piu' codice (lasciando stare il fatto che questo vuol dire duplicare le informazioni e magari in alcuni casi non e' quello che voglio).
Capisco.

Nel mio caso la copia la faccio solo nei test, per cui sposto il problema da un'altra parte.
Poi chiaro, a differenza dell'immutabilita' si tratta di una proprieta' statica che non ha senso in un linguaggio dinamico come puo' essere Python, ma nel momento in cui ho un linguaggio con tipizzazione statica, come puo' essere Java o C#, secondo me e' una mancanza che si sente.
Lo è certamente, perché è senz'altro utile.
Ah ma non sono un difensore dell'immutabilita' a tutti i costi; sono pero' convinto che la tendenza sara' a farne un maggiore uso perche' aiuta a scrivere codice piu' sicuro, seppure al costo di abituarsi a programmare in modo un po' diverso.
Ne sono consapevole e concordo.