Quale linguaggio di programmazione genera meno bug difficili da trovare? [chiuso]


54

Quale lingua, secondo te, consente al programmatore medio di produrre funzionalità con il minor numero di bug difficili da trovare? Questa è ovviamente una domanda molto ampia, e sono interessato a risposte e saggezza molto ampie e generali.

Personalmente trovo che passo pochissimo tempo a cercare strani bug nei programmi Java e C #, mentre il codice C ++ ha il suo set distinto di bug ricorrenti e Python / simile ha il suo set di bug comuni e sciocchi che verrebbero rilevati dal compilatore in altre lingue.

Inoltre trovo difficile considerare i linguaggi funzionali in questo senso, perché non ho mai visto un programma grande e complesso scritto in un codice completamente funzionale. Il tuo contributo per favore.

Modifica: chiarimento completamente arbitrario del bug difficile da trovare: occorrono più di 15 minuti per la riproduzione o più di 1 ora per trovare la causa e correggere.

Scusami se questo è un duplicato, ma non ho trovato nulla su questo argomento specifico.


10
Mi piacerebbe vedere alcune ricerche fatte su questo argomento! Non solo "le mie prove aneddotiche suggeriscono che l'unica lingua che conosco sia il re", ma i tassi di bug di grandi progetti e così via.
Frank Shearar,

@Frank .. se avessi MOLTO tempo (e intendo MOLTO MOLTO tempo), probabilmente potresti estrarre alcune statistiche da ohloh, a condizione che tu possa identificare patch che correggessero bug da migliaia di repository di codice.
Tim Post

Quello in cui sono ammessi solo i commenti. Nessun'altra istruzione :)
Victor Hurdugaci il

4
Penso che "difficile da trovare" debba essere chiarito. La tua domanda e la maggior parte delle risposte sembrano definire "difficili da trovare" come equivalenti a "non colti dal compilatore". Accenni a Python di avere bug stupidi che sarebbero stati rilevati dal compilatore. Giusto. Ma quegli stupidi bug di solito non sono così difficili da trovare. Certamente, non sono nella stessa categoria dei bug C ++ derivanti dal dire di liberare memoria troppo presto.
Winston Ewert,

@Winston Accetto.
Magnus Wolffelt,

Risposte:


58

Più potente è il sistema di tipi del linguaggio, più bug verranno colti al momento della compilazione stessa.

La figura seguente confronta alcuni dei linguaggi di programmazione ben noti in termini di potenza, semplicità e sicurezza dei loro sistemi di tipi. [ Fonte ]

testo alternativo

* Fattorizzazione nella capacità di usare costrutti non sicuri.

C # viene inserito nella riga non sicura a causa della parola chiave "non sicura" e del macchinario puntatore associato. Ma se vuoi pensare a questi come a una sorta di meccanismo di funzione estranea in linea, sentiti libero di alzare C # verso il cielo.

Ho contrassegnato Haskell '98 come puro ma GHC Haskell come non puro a causa della famiglia di funzioni non sicure *. Se disabiliti * non sicuro, salta GHC Haskell di conseguenza.


8
È brillante!

7
Non riuscivo a trovare Common Lisp nella grafica: (let ((itbe '())) ... )...
duros

7
Che cosa? Non c'era spazio sul lato sinistro per PHP?
pestaa,

7
C # consente puntatori sicuri : viene verificata tutta l'aritmetica dei puntatori. Pertanto, credo che dovrebbe essere lassù con Java.
Alex ten Brink

11
@missingfaktor: penso che C # non sia ben posizionato. C # consente e promuove stili di programmazione successivi. Non dovrebbe essere misurato dalla cosa peggiore che consente.
back2dos

20

A mio avviso, Haskell ti aiuta a evitare alcune comuni fonti di errori:

  • è puramente funzionale: le funzioni non possono avere effetti collaterali (non intenzionali) e questo rende la programmazione multicore più semplice e meno soggetta ad errori
  • è fortemente tipizzato: ad esempio non è possibile mescolare accidentalmente i valori bool, char, int e float
  • è tipizzato staticamente: molti errori di programmazione vengono colti al momento della compilazione
  • nullnon fa parte delle definizioni del tipo di valore: in questo modo si evita l' errore di miliardi di dollari
  • ci sono molte funzioni di ordine superiore già pronte che puoi riutilizzare invece di scrivere le tue implementazioni, possibilmente difettose
  • ha un garbage collector: gli errori di memoria sono quasi eliminati (ad eccezione di "perdite di spazio" a causa della sua strategia di valutazione pigra)

11
Non voglio sottovalutare questo, ma sono tentato perché non soddisfa i criteri. Dovrebbe consentire "al programmatore medio di produrre funzionalità" con un conteggio minimo di bug, ma il programmatore medio, per dirla senza mezzi termini, non può in primo luogo fare testa o croce di Haskell.
Mason Wheeler,

5
Molti punti positivi, ma durante la rimozione di alcune classi di errore (effetti collaterali non intenzionali, conversioni di tipo impreviste) Haskell aggiunge una nuova classe di errore: perdite di spazio. (Anche se non c'è null, c'è undefined, che è un membro di ogni tipo.)
j_random_hacker

5
@Mason: se non ci scrivi, non puoi avere bug in esso :)
Michael K,

2
Penso che non c'è alcuna cosa come un pranzo libero - si può avere facile da trovare bug, o facile da scrivere codice, ma non entrambi :)
Benjol

6
@ Mason cos'è esattamente un "programmatore medio"? La domanda inizia con "quale linguaggio di programmazione ...", non "quale tra C, C ++ e java ...";)
Agos,

18

Tradizionalmente i bug più difficili da trovare sono le condizioni di gara nelle applicazioni multi-thread così come sono

  • quasi impossibile da riprodurre
  • può essere molto sottile

Quindi hai bisogno di lingue che gestiscano il parallelismo per te il più possibile e in modo non intrusivo. Questi non sono ancora mainstream. Java ne fa un po ', ma ti lascia con la parte difficile.

A quanto ho capito, hai bisogno di un linguaggio funzionale poiché "nessun effetto collaterale" è la cosa che in primo luogo fa sparire i due punti elenco. Ho visto che sono in corso lavori per rendere trasparente Haskell un efficiente linguaggio multi-thread e credo che Fortress sia progettato da zero per essere un linguaggio parallelo efficiente.


Modifica: in Java Executorsgestisce ancora più parti rigide. È necessario rendere le singole attività conformi Callableall'interfaccia.


5
++ Condizioni di gara. Puoi dirlo di nuovo.
Mike Dunlavey,

Si. Ricorda che il calcolo parallelo in generale è difficile, anche con l'assistenza linguistica. (Le macro Lisp comuni sono difficili, nonostante il supporto linguistico, perché ciò che fanno è molto potente. Stesso principio.)
David Thornley,

Che mi dici di Erlang?
Malfist,

@Malfist, non so abbastanza su Erlang per rispondere. Forse dovresti aprire una domanda se vuoi davvero saperlo?

2
Erlang è una programmazione funzionale progettata per rendere il multithreading semplice e sicuro. Non condividi variabili, passi messaggi. Leggi la sua pagina di Wikipedia.
Malfist,

13

Ada è progettato in modo tale da catturare il più possibile in fase di compilazione anziché in fase di esecuzione. Ciò significa che spesso ci vuole circa 10 volte più tempo per compilare un programma in Ada rispetto all'equivalente in Java direbbe, ma quando lo compila puoi essere molto più sicuro che intere classi di bug non si manifesteranno quando il programma è correre.


7
+1 per aver notato, correttamente, che Ada rileva nel compilatore elementi che altre lingue ignorano. -1 per l'affermazione che ciò significa che ci vuole 10 volte più tempo per compilare un programma Ada. Ada premia il programmatore che DISEGNA !!! il suo codice, che PENSA !!! su ciò che sta facendo prima di iniziare a scrivere follemente. La mia esperienza personale, durante la programmazione della produzione in Ada per il lavoro di difesa, è stata che non ci è voluto molto più tempo per compilare il codice Ada di quanto ci volesse C / C ++ o FORTRAN, ma il codice Ada ha avuto significativamente meno problemi in seguito. Pratt & Whitney notarono qualcosa di simile.
John R. Strohm,

1
@john r strohm, da quello che ho capito, non sta parlando del tempo impiegato dal compilatore per compilare il codice, piuttosto il tempo per renderlo compilabile.
Malfist,

oh, ho deciso di compilare il codice. Ricordo un sacco di commenti sessisti fatti da programmatori che imparavano la lingua sulla sua selezione. Di solito sulla falsariga di Well, se dai il nome a una lingua di una donna ...
Michael Shaw,

@Ptolemy, se le barche possono essere chiamate come donne (tranne apparentemente per i vettori statunitensi), anche i linguaggi di programmazione possono farlo. Piuttosto hanno chiamato "Ada" di "USS Ronald Reagan" :)

1
Una volta superata la curva di apprendimento, in realtà sono diventato abbastanza veloce nello scrivere il codice Ada - dopo aver trascorso parecchio tempo a pensare al design. Ada NON è sicuramente la lingua di un hacker. Oggi lavoro in Java e alcune delle cose che vedo nei miei progetti attuali mi fanno perdere un po 'Ada (non avrei mai pensato di dirlo).
James Adam,

7

Innanzitutto una definizione: un bug difficile da trovare, a quanto ho capito, è un bug che può essere riprodotto ma la causa è difficile da trovare.

Probabilmente l'aspetto più importante è ciò che definirei ristrettezza , ovvero quanto lontano può sfuggire un bug, quanto è grande l'ambito che un bug può potenzialmente influenzare. In linguaggi come C, un bug, ad esempio un indice di array negativo o un puntatore non inizializzato, può influenzare letteralmente tutto ovunque in tutto il programma, quindi nel peggiore dei casi, devi controllare tutto ovunque per trovare la fonte del tuo problema.

Le buone lingue a questo proposito supportano i modificatori di accesso e li applicano in un modo che rende difficile o impossibile bypassarli. Le buone lingue ti incoraggiano a limitare l'ambito delle tue variabili, invece di rendere troppo facile avere variabili globali (ad esempio "tutto ciò che non è dichiarato esplicitamente è una variabile globale con un tipo e un valore predefiniti").

Il secondo aspetto importante è la concorrenza . Le condizioni di gara sono generalmente difficili da riprodurre e quindi difficili da trovare. Le buone lingue offrono meccanismi di sincronizzazione facili da usare e le loro librerie standard sono thread-safe dove necessario.

Questo completa già la mia lista; altre cose come la digitazione forte aiutano a catturare i bug in fase di compilazione, ma quei bug probabilmente non sarebbero difficili da trovare in seguito.

Considerando tutto ciò, direi che Java e C #, e molte altre lingue nel mondo JVM e .net, sono adatti per evitare bug difficili da trovare.


Il blocco manuale potrebbe sembrare un "meccanismo di sincronizzazione facile da usare" ma in realtà è solo "semplice da usare ma ci si trova in momenti divertenti mentre si insegue lo stallo". E, bene, puoi fare concorrenza basata sull'attore in parecchie lingue.
Frank Shearar,

Frank: almeno il blocco è abbastanza semplice in modo che tutti possano farlo senza spendere giorni, capire quale API usare ecc.
user281377

Poi c'è il punto di vista che una soluzione di concorrenza "abbastanza semplice da consentire a tutti di farlo" è come regalare seghe da tavolo ai bambini in età prescolare.
David Thornley,

@ David: vedo il tuo punto, ma in molti casi una soluzione semplice è davvero tutto ciò che serve. Ad esempio, considera un servlet utilizzato solo da una manciata di utenti, che deve utilizzare una risorsa condivisa (ad esempio la connessione al database). Senza sincronizzazione, le condizioni di gara si verificano di tanto in tanto; ma non è esattamente scienza missilistica mettere tutte le operazioni di accesso al database in un blocco sincronizzato (connessione) {}.
user281377

+1 Per questa definizione "quanto è grande l'ambito che un bug può potenzialmente influenzare". Ho avuto alcuni bug molto cattivi con linguaggi dinamici in cui un oggetto con un tipo di dati errato è andato molto lontano nel codice (grazie alla digitazione anatra) prima di manifestarsi come un bug.
Giorgio

7

Poiché Excel è il DSL più utilizzato, vado con Excel. (escluso VBA ovviamente)

Si adatta al conto:

  • È sempre facile riprodurlo (ecco un foglio di calcolo - non funziona)
  • È abbastanza facile trovare il bug, dato che è totalmente "funzionale" - inizia con la cella che è sbagliata e rintraccia tutte le sue dipendenze.

Questo potrebbe essere vero fino a quando non si sommano bug facili da trovare.
mouviciel,

+1 Un po 'sfacciato poiché Excel è un dato, non una lingua. Mi ha fatto una bella risata :)
recursion.ninja

2
@awashburn - oh, non lo so. Penso che si qualifichi come una lingua. Ogni cella è una "variabile". Ogni variabile è impostata in modo dichiarativo come letterale (come 123o ABC) o funzione ( =SUM(A2:A5)). Excel quindi valuta tutte le variabili, determinando quale ordine per risolvere le dipendenze, ecc. Non sono certamente solo dati.
Scott Whitlock,

2
Ritiro la mia dichiarazione, si scopre che Excel è Turing completo ... Ho imparato qualcosa di totalmente inquietante ...
recursion.ninja

1
"... il vero master [Lisp] si rende conto che tutti i dati sono codice." Forse questo vale anche per Excel, abbastanza stranamente. blogs.msdn.com/b/sriram/archive/2006/01/15/lisp-is-sin.aspx
James Mishra

7

Questa è una domanda difficile perché la maggior parte dei bug non è colpa della lingua stessa - piuttosto è colpa degli sviluppatori che commettono errori nel modo in cui usano la lingua.

Credo che ci siano diversi aspetti delle caratteristiche del linguaggio che influenzano la probabilità di bug:

  • Interattività : linguaggi dinamici con REPL incoraggiano l'interazione / sperimentazione con programmi in esecuzione e cicli di codice / test molto più piccoli. Se ritieni che l'iterazione sia un buon modo per scoprire soluzioni semplici e pulite e rilevare / eliminare i bug, questo favorirebbe le lingue interattive.

  • Espressività : se il codice è più breve e presenta una minore complessità / incidenza della caldaia, è più facile visualizzare errori / errori logici.

  • Tipo di sicurezza : più tempo è necessario per la compilazione, più errori verranno rilevati dal compilatore, quindi in generale la sicurezza del tipo è una buona cosa. Tuttavia, questi di solito non sono difficili da trovare bug - anche in un linguaggio completamente dinamico il tipo errato in una struttura di dati di solito causerà un errore di runtime molto evidente e TDD rileva quasi sempre questo tipo di bug.

  • Immutabilità : molti bug gravi sono dovuti a complesse interazioni di stato mutevole. Le lingue che enfatizzano l'immutabilità (Haskell, Clojure, Erlang) hanno un enorme vantaggio evitando la mutabilità

  • Programmazione funzionale - gli approcci funzionali alla scrittura del codice tendono ad essere più "dimostrabilmente corretti" del codice orientato agli oggetti con sequenze complesse di effetti / interazioni. La mia esperienza è che FP aiuta a evitare bug insidiosi - credo che ci sia qualche ricerca accademica da qualche parte che attualmente non riesco a trovare a sostegno di questo.

  • Supporto per la concorrenza: i problemi di concorrenza sono particolarmente difficili da rilevare ed eseguire il debug, motivo per cui questo è così importante. Tutto ciò che richiede il blocco manuale è destinato a fallire (e questo include praticamente tutti approccio alla concorrenza orientato agli oggetti). Il miglior linguaggio che conosco a questo proposito è Clojure: ha un approccio unico alla gestione della concorrenza che combina la memoria transazionale del software con strutture di dati immutabili per ottenere un framework di concorrenza nuovo, affidabile e compostabile. Vedi http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey per ulteriori approfondimenti


Le persone come me che sono appassionate sostenitrici della programmazione funzionale apprezzano questa risposta. L'espressività è tua amica.
Sridhar Sarnobat,

1
La migliore risposta finora! Penso che questo sia supportato dalle seguenti due analisi della frequenza dei bug: deliberate-software.com/safety-rank-part-2 e macbeth.cs.ucdavis.edu/lang_study.pdf . Entrambi mostrano che più il linguaggio è puro, più funzionale, più espressivo e più sicuro è, meno bug ha. Allo stesso modo, entrambi mostrano che Clojure e Ruby sfuggono alla regola di sicurezza, probabilmente indicando che l'interattività ha un impatto uguale come hai sottolineato.
Didier A.

@DidierA. sfortunatamente il link ucdavis è interrotto (senza archivio wbm) - Penso che tu l'abbia ottenuto dalla pagina di Prem Devanbu che ora punta a un link aggiornato: uno studio su larga scala di linguaggi di programmazione e qualità del codice in Github
icc97,

5

Meno potente è una lingua, meno opzioni ti dà per sparare al tuo piede.

Linguaggi di alto livello come Java e C # produrranno meno bug rispetto a linguaggi di basso livello come C ++.

Detto questo, credo che Java sia più sicuro di C #. Java è artificialmente limitato in modo che un programmatore medio senza conoscenze avanzate possa dominarlo e produrre programmi stabili.


4
+1 per "Meno una lingua è potente, meno opzioni ti vengono in mente di sparare al tuo piede".
Michael K,

Il grafico di missingfaktor sembra dire che C # e C ++ sono sullo stesso "piano" per quanto riguarda la possibilità di sparare a se stessi e va sopra Java. Non che io sia d'accordo con quella parte del grafico, però. Sei costretto a passare attraverso i cerchi per fare alcune riprese in C #.
Jesse C. Slicer,

6
Sicurezza e potenza non sono inversamente proporzionali (che è quello che sembra pensare ;-) Haskell, ad esempio, è ESTREMAMENTE potente e ha ancora pochi modi per spararti a piedi.
missingfaktor il

3
Se la lingua è debole, hai solo bisogno di un piede più grande.

7
@missingfaktor, è perché è un effetto collaterale spararsi al piede.

3

Quale lingua, secondo te, consente al programmatore medio di produrre funzionalità con il minor numero di bug difficili da trovare?

Secondo me, Delphi. Essendo basato su Pascal, il linguaggio è abbastanza semplice e intuitivo per il programmatore medio (o anche programmatori inesperti) di imparare facilmente, e il suo ricco supporto di strumenti e librerie rende la maggior parte dei bug facili da trovare.

  • Digitazione forte e un compilatore rigoroso che rileva molti errori comuni.
  • Sintassi intuitiva che non incoraggia errori comuni. ("L'ultimo bug del mondo",if (alert = RED) {LaunchNukes;} , ad esempio, non verrà compilato).
  • Un modello a oggetti ben progettato che elimina molti degli errori OOP C ++ comuni.
  • Verifica dei limiti e controllo della portata integrati nella lingua, riducendo drasticamente le possibilità di problemi di sicurezza.
  • Probabilmente il compilatore più veloce conosciuto dall'uomo, che aumenta la tua produttività e rende più difficile perdere il filo del pensiero mentre aspetti una costruzione.
  • Il debugger Il debugger di Visual Studio vuole essere come quando cresce.
  • Tracciamento delle perdite incorporato direttamente nel gestore della memoria, rendendo banale la ricerca e la correzione delle perdite di memoria.
  • Una libreria standard ampia e matura che fornisce modi precompilati e pre-testati per eseguire attività comuni senza dover costruire implementazioni personalizzate, possibilmente difettose.
  • Fornito con strumenti utili, come un potente sistema di registrazione e un profiler, per facilitare la localizzazione dei problemi.
  • Forte supporto della comunità per problemi comuni che non si trovano nella libreria standard, inclusa una potente libreria di concorrenza di terze parti .

Sono un fantino di Delfi da molto tempo fa, ma è andato via dalle sue radici Pascal quando mi ha permesso di scrivere qualsiasi cosa su qualsiasi altra cosa, a la C / C ++:var I: Integer; Pointer(I)^ := $00;
Jesse C. Slicer,

1
@Jesse: forse, ma lo vedo come una concessione necessaria al pragmatismo. Kernighan ha fatto molti buoni punti quando ha scritto Why Pascal non è il mio linguaggio di programmazione preferito. I dattiloscritti sono necessari per fare molte cose importanti di basso livello. Ma uno dei punti di forza di Delphi è il modo in cui le sue librerie incapsulano dettagli di basso livello e rendono superflua la maggior parte del puntatore non sicuro e degli elementi tipografici.
Mason Wheeler,

Non sono in disaccordo sul fatto che potrebbe essere necessario, ma affermare che la tipizzazione forte è in qualche modo negato da questo. L'originale Pascal non permetteva tali shenanigans e quindi era fortemente tipizzato. Ma non andrei così lontano a chiamare Delphi debolmente tipizzato - è una specie di "medio-ben tipizzato".
Jesse C. Slicer,

1
@Jesse: la versione originale di Pascal di Wirth non consentiva i record delle varianti? IIRC alla fine sono diventati così comunemente usati per sovvertire la tipizzazione forte che Borland e altri hanno deciso di inserire solo i caratteri di battitura per renderlo più semplice perché tutti lo facevano comunque.
Mason Wheeler,

en.wikipedia.org/wiki/Pascal_(programming_language)#Divisions e en.wikipedia.org/wiki/Pascal_(programming_language)#Criticism e pascal-central.com/ppl/chapter3.html sembrano indicare che faceva parte di il primo standard nel 1983. Vedo alcuni riferimenti di Wirth che sembrano risalire al 1974, quindi direi di sì. Penso che la parte problematica sia stata quella di permetterle di essere sovvertito come tale (cioè i campi varianti che occupano la stessa memoria, come i sindacati in C). Se fossero semplicemente usati come meccanismo di scoping e il layout di memoria fosse per il superset, sarebbe più forte.
Jesse C. Slicer,

2

Una cosa da tenere in considerazione è il tempo di inversione.

Negli ultimi cinque anni ho sviluppato principalmente applicazioni web in Java (JSF, Seam, ecc.). Di recente ho ottenuto un nuovo lavoro e stiamo usando Perl (con Catalyst e Moose).

Sono molto più produttivo in Perl, rispetto a Java.

Non è necessario compilare e distribuire (a caldo), è uno dei motivi. Trovo anche che scrivere casi d'uso sia più semplice, poiché può essere fatto in modo più iterativo. E i framework in Java sembrano essere inutili complessi, almeno per i progetti in cui sono stato coinvolto.

Immagino che il numero di bug nel mio codice Perl sia più o meno lo stesso del numero di bug nel mio codice Java, potrebbe anche essere superiore. Ma trovo più facile e veloce trovare e correggere questi bug.


1

Forse sondare il numero di strumenti disponibili per l'analisi del codice statico e dinamico per ogni linguaggio di programmazione potrebbe dare un'idea. Più strumenti per una lingua, è più probabile che la lingua sia molto popolare tra gli utenti o molto popolare nel generare bug difficili da trovare. Ma non riesco a convincere Google a indicarmi qualsiasi studio fatto su questo argomento. Va inoltre notato che alcuni linguaggi come C possono essere utilizzati per aggirare i bug hardware sottostanti e per aggirare l'usura dell'hardware nel tempo.


1
"aggirare l'usura dell'hardware mentre invecchia" ...?
j_random_hacker

Ho letto che alcuni sistemi operativi Unix eseguiti su macchine mission critical verificano lo stato di salute della CPU, della RAM e di altro hardware. serverfault.com/questions/56192/… ne discute a fondo. Se alcune linee in un modulo RAM diventano difettose nel tempo, tali moduli difettosi non verranno utilizzati dal sistema operativo e non verranno riportati nella memoria fisica totale disponibile. Tali cose potrebbero essere fatte anche su altro hardware.
vpit3833,

È un bocconcino interessante, ma non vedo quanto sia rilevante qui. Inoltre, niente nel tuo link menziona questi sistemi operativi Unix autoriparanti: parla solo di come stressare l'hardware di un PC.
j_random_hacker il

1
Ho detto che significa che i programmi da soli potrebbero non essere le fonti di bug, ma potrebbero essere anche l'hardware o altri fattori esterni.
vpit3833,

1

Invece di parlare di lingue, parlare di funzionalità linguistiche

  • java ti costringe a pensare alle eccezioni (genera ...) e devi pubblicare o gestire queste eccezioni. Ciò mi impedisce davvero di dimenticare errori di configurazione o sto usando più eccezioni derivate da SystemException che non necessitano di questa gestione?
  • che dire di "design by contract" (http://en.wikipedia.org/wiki/Design_by_contract) che mi costringe a pensare a pre e postcondizioni. Ho letto che ora è possibile con c # -4.0.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.