La tipizzazione statica vale i compromessi?


108

Ho iniziato a programmare in Python principalmente dove non esiste la sicurezza dei tipi, quindi sono passato a C # e Java dove c'è. Ho scoperto che avrei potuto lavorare un po 'più rapidamente e con meno mal di testa in Python, ma poi di nuovo, le mie app C # e Java hanno un livello di complessità molto più alto, quindi suppongo che non abbia mai dato a Python un vero stress test.

I campi Java e C # lo fanno sembrare senza la sicurezza del tipo in atto, la maggior parte delle persone si imbatterebbe in tutti i tipi di orribili bug lasciati a destra e sarebbe più un problema che un valore.

Questo non è un confronto linguistico, quindi per favore non affrontare problemi come compilati o interpretati. La sicurezza del tipo vale la pena per la velocità di sviluppo e flessibilità? PERCHÉ?

alle persone che volevano un esempio dell'opinione che la digitazione dinamica sia più veloce:

"Utilizza un linguaggio tipizzato in modo dinamico durante lo sviluppo. Offre feedback, tempi di risposta e velocità di sviluppo più rapidi." - http://blog.jayway.com/2010/04/14/static-typing-is-the-root-of-all-evil/



8
@Prof Plum: posso richiedere una prova del successo nella velocità di sviluppo e flessibilità? Dal momento che stiamo parlando di un particolare aspetto del tipo di sicurezza , usando Javao C#sarebbe inconcludente, il loro modo di fornirlo NON è l'unico ...
Matthieu M.

32
Con la diligenza in un linguaggio rigoroso, è possibile ridurre al minimo i "mal di testa" e quindi si potrebbe anche vedere un aumento della velocità a causa del completamento automatico dell'IDE, della generazione del codice e del suggerimento del codice.
Nicole,

9
@Prof Plum: Capisco, non mi aspetto che tu (o nessuno davvero) abbia testato completamente ogni lingua mai creata ^^ Il problema è che la maggior parte delle persone che ho visto lamentarsi di un aspetto specifico dei linguaggi di programmazione (Statico La digitazione viene spesso) in genere si lamentano di un'implementazione particolare e non riescono a realizzarla.
Matthieu M.

5
@Prof Plum, tutto ciò che quel post sul blog ha davvero da dire sulla velocità è l'affermazione calva "Chiunque, che ha lavorato seriamente con un linguaggio moderno tipicamente dinamicamente come Ruby o Smalltalk, sappia che sono più produttivi". Nessun esempio reale di come, in termini pratici, lo sviluppo sia più rapido.
Carson63000,

Risposte:


162

È una specie di mito che i programmatori non debbano preoccuparsi dei tipi in linguaggi tipizzati dinamicamente.

Nelle lingue tipizzate dinamicamente:

  • Devi ancora sapere se stai lavorando con un array, un numero intero, una stringa, una tabella hash, un riferimento di funzione, un dizionario, un oggetto o altro.

  • Se è un oggetto, devi sapere a quale classe appartiene.

  • Assegnare uno di questi tipi a una variabile o parametro di funzione che si prevede sia un altro tipo è quasi sempre un errore.

  • Ad un livello inferiore, cose come il numero di bit o con segno rispetto a non firmato frequentemente devono comunque essere prese in considerazione se si sta popolando un pacchetto TCP, ad esempio.

  • È possibile riscontrare problemi in cui si ottiene uno zero in cui si desidera davvero una stringa vuota. In altre parole, stai ancora eseguendo il debug di errori di mancata corrispondenza del tipo. L'unica vera differenza è che il compilatore non sta rilevando gli errori.

  • Direi che non stai nemmeno salvando molta digitazione , perché tendi a voler documentare nei commenti quale tipo sono i parametri della tua funzione invece di documentarla nel tuo codice. Questo è il motivo per cui i blocchi di commenti in stile doxygen sono molto più popolari nella pratica in tutto il codice tipizzato dinamicamente, dove nei linguaggi tipicamente statici li vedi principalmente solo per le biblioteche.

Questo non vuol dire che la programmazione in linguaggi tipizzati dinamicamente non sia più piacevole perché il compilatore non è sempre sulla schiena e i programmatori esperti non hanno difficoltà a trovare e correggere il tipo di bug che la tipizzazione statica catturerebbe comunque , ma questo è un problema completamente separato da un presunto aumento dell'efficienza o riduzione del bug rate, per cui la tipizzazione dinamica è nella migliore delle ipotesi anche con la tipizzazione statica.


10
Dovrei contestare il fatto che programmatori esperti non realizzino / introducano questo tipo di bug. I buoni sviluppatori che sono umili e riconoscono la possibilità che commettano errori (e gli sviluppatori esperti non sono sempre così) hanno meno probabilità di creare questi bug.
Jay Jay Jay

12
Non potrei essere più d'accordo con "Direi che non stai nemmeno risparmiando molta battitura". Finisci per documentare i tipi nei commenti e controllarli nei tuoi test, che, semmai, richiedono più digitazione e manutenzione (dopo tutto, devi ricordare di aggiornare detti commenti ogni volta che i tuoi tipi cambiano e molto spesso non lo farai ).
Severyn Kozak,

Trascorriamo molto più tempo nel nostro negozio di Python a documentare i tipi di quanti ne risparmiamo su un linguaggio dettagliato tipicamente statico come C # o Java. Vale anche la pena notare che la nuova generazione di linguaggi come Go e Rust usa l'inferenza del tipo, quindi stai digitando "x: = new (Object)" invece di "Object x = new Object ()".
weberc2,

Sono d'accordo con te quando dici che il linguaggio dinamico è più piacevole, ma non so perché. Hai una spiegazione per questo?
Rodrigo Ruiz,

Sì, invece di fornire il tipo di variabili, puoi usare valori predefiniti o unit test (test inline) in Python. Anche in Python a volte puoi avere strani errori con errori ortografici [meno probabile che si verifichi se usi il completamento automatico che spesso può essere usato anche se non sempre nei vigili dei linguaggi dinamici] e devi capire se self.bread = 5 sta introducendo pane o ridefinendolo.
aoeu256,

123

Man mano che i tipi diventano più forti, possono aiutarti di più, se li usi correttamente invece di combatterli. Progettare i tipi in modo da riflettere lo spazio del problema e gli errori logici hanno maggiori probabilità di diventare disallineamenti del tipo di compilazione invece di arresti anomali del runtime o risultati senza senso.


37
+1! "È più probabile che gli errori logici diventino disallineamenti di tipo in fase di compilazione invece di arresti anomali del runtime o risultati senza senso": Risposta davvero buona! Quando dedico più tempo alla progettazione dei miei tipi, il codice segue in modo più naturale ed è spesso corretto non appena viene compilato. Progettare un tipo significa comprendere un dominio e le sue operazioni.
Giorgio,

78

Disclaimer: sono un amante del tipo;)

È difficile rispondere alla tua domanda: quali sono questi compromessi ?

Prenderò un esempio estremo: Haskell , è scritto staticamente. Forse una delle lingue più fortemente tipizzate che esistano, in effetti.

Tuttavia, Haskell supporta la programmazione generica , nel senso che si scrivono metodi che funzionano con qualsiasi tipo conforme a un determinato concetto (o interfaccia).

Inoltre, Haskell utilizza Type Inference , in modo da non dover mai dichiarare il tipo delle variabili. Vengono calcolati staticamente durante la compilazione, proprio come un interprete Python li calcolerebbe eseguendo il programma.

Ho scoperto che la maggior parte delle persone che fanno del male alla tipizzazione statica, in realtà si lamentano di qualcos'altro (verbosità, dolore nel cambiare un tipo in favore di un altro), ma Haskell non mostra nessuno di questi problemi, mentre viene tipizzato staticamente ...


Esempio di brevità:

-- type
factorial :: Integer -> Integer

-- using recursion
factorial 0 = 1
factorial n = n * factorial (n - 1)

Oltre al supporto integrato, è difficile essere più brevi.

Esempio di programmazione generica:

> reverse "hell­o" -- Strings are list of Char in Haskell
=> "olleh"
> reverse [1, 2, 3, 4, 5]
=> [5,4,3,2,1]

Esempio di inferenza di tipo:

> :t rever­se "hell­o"
:: [Char]

che può essere calcolato semplicemente:

  • "hello"è un elenco di Char(espresso come [Char])
  • reverseapplicato a un tipo [A]restituisce un tipo[A]

Provalo nel tuo browser


4
Per fare da avvocato del diavolo, un compromesso a favore dei linguaggi dinamici (almeno durante la prototipazione) è che, nella misura in cui le dichiarazioni di tipo possono servire allo stesso scopo di alcuni test unitari, possono anche consolidare le interfacce allo stesso modo dei test unitari ( sebbene certamente con meno spese generali). Inoltre, i linguaggi digitati staticamente senza coercizione, sebbene più sicuri, richiedono la cast esplicita dei tipi (in particolare quando un tipo non è abbastanza generico), che può sminuire la terseness.
TR

7
Non conosco Haskell ma +1 per "in realtà si lamentavano di qualcos'altro (verbosità, dolore di cambiare un tipo a favore di un altro)"
Nicole

1
@Aidan: Haskell è un linguaggio in evoluzione. Haskell 98 è stato un miglioramento rispetto a Haskell 1.4; Haskell 2010 è stato un miglioramento rispetto a quello. Nel frattempo, vale la pena notare che per gran parte della sua vita, la ragion d'essere di Haskell è stata quella di aiutare a esplorare i sistemi di tipi; Le classi di tipi multiparametro sono un esempio di dove è riuscito a chiarire un'estensione di sistema di tipo utile. (D'altro canto, le dipendenze funzionali sembrano essere una sorta di vicolo cieco.)
Geekosaur,

4
@Matthieu: WRT "Forse uno dei linguaggi più fortemente tipizzati che esistono, in effetti.", Vedrò il tuo Haskell e ti farò crescere Agda e Coq . (
Garantirò

1
@Matthieu: gli assistenti di prova sono un'applicazione diretta della corrispondenza Curry-Howard, quindi sono fondamentalmente linguaggi di programmazione (anche se con librerie standard piuttosto limitate). Sono in prima linea nella ricerca dei tipi dipendenti perché sono necessari tipi dipendenti per fare buon uso della corrispondenza "tipi sono proposizioni".
Geekosaur,

37

Mi piacciono le lingue sia tipicamente che dinamicamente. I due maggiori vantaggi della sicurezza del tipo per me sono:

1) Spesso puoi praticamente dedurre ciò che una funzione fa semplicemente dalla sua firma di tipo (questo è particolarmente vero in linguaggi funzionali come Haskell).

2) Quando esegui un refactor significativo, il compilatore ti dice automaticamente tutto ciò che devi fare per far funzionare tutto. Quando refactoring qualcosa in C ++, la mia procedura è spesso semplicemente a) cambiare la parte che so che voglio cambiare, quindi b) correggere ogni errore di compilazione.


Esattamente lo stesso con me, e ogni volta che voglio refactificare qualcosa (uso principalmente golang / dattiloscritto / java), sì, quei 2 passaggi sono quelli di cui tutti avrebbero bisogno. cambiare una parte, quindi correggere tutti gli errori di compilazione :) risposta perfetta.
Nishchal Gautam,

29

Personalmente, trovo che la sicurezza del tipo mi aiuti a sviluppare più velocemente nel mio lavoro attuale. Il compilatore fa molto del controllo della sanità mentale per me quasi mentre scrivo, permettendomi di concentrarmi maggiormente sulla logica aziendale che sto implementando.

In conclusione, anche se perdo un po 'di flessibilità, guadagno un po' di tempo che altrimenti verrebbe speso a rintracciare i problemi di tipo.


13

Ci sono molte opinioni forti intorno al dibattito, ma ovviamente questa non è in realtà una questione di opinione, è una questione di fatti . Quindi dovremmo guardare alla ricerca empirica . E le prove da ciò sono chiare:

, la tipizzazione statica vale i compromessi - e non solo per un po ', ma in effetti sostanzialmente . In effetti, prove concrete dimostrano che la tipizzazione statica può ridurre il numero di bug nel codice di almeno il 15% (e questa è una stima bassa, la percentuale effettiva è quasi certamente maggiore). Questo è un numero incredibilmente alto : penso che anche la maggior parte dei sostenitori della tipizzazione statica non avrebbe pensato che avesse fatto una differenza così drastica.

Considera questo: se qualcuno ti dicesse che esiste un modo semplice per ridurre i bug nel tuo progetto del 15% dall'oggi al domani, dovrebbe essere un gioco da ragazzi. 1 È quasi il proverbiale proiettile d'argento.

Le prove sono presentate nel documento Per scrivere o non scrivere: quantificare i bug rilevabili in JavaScript di Zheng Gao, Christian Bird e Earl T. Barr. Incoraggio tutti a leggerlo, è un documento ben scritto che presenta ricerche esemplari.

È difficile riassumere sinteticamente quanto rigorosamente gli autori hanno eseguito la loro analisi, ma ecco uno schema (molto approssimativo):

TypeScript e Flow sono due linguaggi di programmazione basati su JavaScript che, pur rimanendo altrimenti compatibili, aggiungono suggerimenti sul tipo e controllo statico del tipo. Ciò consente di aumentare il codice esistente di tipi e quindi di controllarlo.

I ricercatori hanno raccolto progetti Open Source scritti in JavaScript da GitHub, hanno esaminato le segnalazioni di bug risolte e hanno tentato di ridurre ciascuno dei bug segnalati a un pezzo di codice che sarebbe stato catturato dal controllo statico del tipo di TypeScript o Flow. Ciò ha permesso loro di stimare un limite inferiore della percentuale di bug che potrebbe essere risolto utilizzando la tipizzazione statica.

I ricercatori hanno preso rigorose precauzioni per garantire che la loro analisi non considerasse un bug non correlato al tipo come correlato ai tipi. 2

Rispetto agli studi precedenti, questo nuovo studio ha particolari punti di forza:

  • Esiste un confronto diretto tra tipizzazione statica e dinamica, con pochi (se presenti) fattori confondenti, poiché l'unica differenza tra JavaScript e TypeScript / Flow è la tipizzazione.
  • Eseguono la replica su più dimensioni controllando sia TypeScript che Flow (ovvero sistemi di tipo diverso) e facendo in modo che persone diverse riproducano l'annotazione di tipo (manuale) per correggere i bug. E lo fanno attraverso un gran numero di basi di codice di diversi progetti.
  • L'articolo misura l'impatto diretto della tipizzazione statica sui bug risolvibili (piuttosto che su una qualità più vaga).
  • Gli autori definiscono un modello rigoroso di cosa misurare e come, in anticipo. Inoltre, la loro descrizione è incredibilmente chiara e semplifica l'analisi dei difetti (è sempre utile quando un documento di ricerca si apre agli attacchi: se nessun attacco riesce a intaccare le sue argomentazioni, risulta ancora più forte). 3
  • Eseguono un'adeguata analisi della potenza in modo che la loro dimensione del campione sia sufficiente e la loro successiva analisi statistica sia ermetica.
  • Sono eccessivamente conservatori per escludere spiegazioni confondenti e misurare solo una singola parte mobile. Inoltre, limitano la loro analisi ai bug immediatamente risolvibili includendo i tipi ed escludono tutto ciò che potrebbe richiedere un refactoring più avanzato per adattarsi alla digitazione. Quindi, in realtà, l'effetto è plausibilmente molto più grande, ma certamente non inferiore a quello che hanno riportato.
  • E infine, non trovano un leggero effetto ma una differenza sbalorditiva . Nonostante la loro procedura eccessivamente conservativa, anche nella parte bassa dell'intervallo di confidenza al 95% scoprono che ci sono almeno il 10% di bug che svanirebbero semplicemente con controlli di tipo aggiunti minimi.

A meno che non vi sia un difetto fondamentale nel documento che nessuno ha ancora scoperto, il documento mostra in modo conclusivo un grande vantaggio della digitazione statica, quasi a costo zero. 4


Da un punto di vista storico, la ricerca sulle discipline tipografiche nella programmazione ha avuto un inizio difficile perché, per molto tempo, le prove non erano affatto chiare. La ragione di ciò è che fare esperimenti sistematici per esaminare l'effetto della tipizzazione statica vs dinamica non è facile: un esperimento sistematico deve isolare l'effetto che stiamo studiando. E sfortunatamente non possiamo isolare l'effetto della disciplina di battitura, poiché è legata ai linguaggi di programmazione.

In realtà c'erano linguaggi di programmazione che permettevano la digitazione sia statica che dinamica in dialetti diversi (ad es. VB con Option Strict Ono Off, o Lisp tipicamente statico). Tuttavia, questi non erano adatti per un confronto diretto, soprattutto perché non esistevano basi di codice sufficientemente grandi che consentano il confronto diretto. Nella migliore delle ipotesi, potremmo confrontarli in "impostazioni di laboratorio", in cui i soggetti del test risolvono in modo casuale un'attività nella variante della lingua tipizzata staticamente o dinamicamente.

Sfortunatamente questi incarichi di programmazione artificiale non modellano bene l'uso nel mondo reale. In particolare, molti di essi hanno una portata ridotta e risolvono un problema ben definito che può essere riassunto in mezza pagina di testo.

Fortunatamente è passato, perché TypeScript, Flow e JavaScript sono in effetti gli stessi linguaggi, tranne per la tipizzazione statica, e perché esiste un ampio set di dati del codice e bug presenti nel mondo reale.


1 Ispirato da una citazione dalla carta originale.

2 Non ne sono del tutto contento: uno dei principali punti di forza dei linguaggi tipizzati staticamente è che i problemi apparentemente non correlati al tipo possono essere formulati in modi che possono essere controllati staticamente. Ciò trasforma molti errori logici in errori di tipo, che aumentano drasticamente la velocità di bug che possono essere rilevati dalla tipizzazione statica. In effetti, il documento classifica approssimativamente i bug non correlati al tipo e io sostengo che una grande percentuale di questi potrebbe in effetti essere catturata dalla tipizzazione statica.

3 Invito chiunque, in particolare i sostenitori della tipizzazione dinamica, a cercare di trovare difetti non risolti nell'analisi. Non credo che ce ne siano molti (se ce ne sono), e sono fiducioso che nessun potenziale difetto altererebbe materialmente il risultato.

4 Sospetto che il costo effettivo della tipizzazione statica in progetti reali su larga scala sia inesistente, dal momento che diventa una parte naturale dell'architettura e potrebbe persino semplificare la pianificazione. La correzione degli errori di tipo statico richiede tempo, ma molto meno degli errori rilevati in seguito. Questo è stato ampiamente studiato empiricamente ed è noto da decenni (vedi ad esempio Codice completo ).


3
So che questa è una risposta tardiva a questa domanda, ma credo che le nuove prove (che spiego qui) cambino l'intero dibattito statico-dinamico.
Konrad Rudolph,

2
È certamente interessante, ma mi chiedo quanto sia legato al particolare sistema di tipi di JavaScript. Il sistema di tipo Python (specialmente python3) è molto più rigoroso con conversioni implicite molto meno.
Peter Green,

@PeterGreen Sì, questo è senza dubbio vero. Forse siamo fortunati e i suggerimenti sul tipo di Python porteranno a un'analisi simile lungo la strada (anche se ne dubito, poiché lo scopo espresso in PEP484 e PEP526 è di non implementare la tipizzazione statica).
Konrad Rudolph,

1
Leggendo l'abstract posso già dire che la metodologia è fondamentalmente imperfetta. Non puoi usare una base di codice scritta usando una disciplina e poi semplicemente aggiungere tipi per giustificare argomenti in una disciplina totalmente diversa. Il codice scritto come disciplina statica sembra fondamentalmente molto diverso dalla disciplina dinamica, non dovresti scrivere Java in Python, proprio come non dovresti scrivere Python in Java. Anche dattiloscritto e javascript sono un linguaggio fondamentalmente diverso, nonostante le somiglianze superficiali.
Lie

2
@LieRyan Se qualcosa che rende l'analisi eccessivamente conservativa, come notato nella mia descrizione e altrove. Non invalida affatto l'analisi. La tua stima dell'1% è, onestamente, ridicola. È completamente spento, il tuo intuito ti delude. Allo stesso modo, la tua caratterizzazione dei problemi con la tipizzazione statica è tipica di un professionista della tipizzazione dinamica che aveva poca esperienza effettiva con la tipizzazione statica moderna (cioè non solo Java).
Konrad Rudolph,

12

La sicurezza dei tipi vale il colpo per la velocità di sviluppo e flessibilità?

Quindi davvero questo si riduce a quello che stai facendo. Se stai programmando, diciamo, i sistemi di backup per aeroplani, la sicurezza dei tipi è probabilmente la strada da percorrere.

Linguaggio dinamico vs programmazione statica del linguaggio sono in realtà due animali diversi. Entrambi richiedono un approccio sostanzialmente diverso l'uno dall'altro. Puoi principalmente eseguire il porting di un metodo di approccio tra statico e dinamico, ma perderai i vantaggi dell'altro.

È davvero una mentalità. Uno è migliore dell'altro? Dipende molto da chi sei e da come pensi. La maggior parte delle persone con cui lavoro non toccherebbe mai un linguaggio dinamico se non fosse necessario, perché sentono che c'è troppo spazio per gli errori. Hanno torto a pensarlo? No, certo che no, ma significa che si sono resi conto che il loro approccio all'applicazione del loro stile di codifica non funzionerà in un ambiente dinamico. Altre persone con cui vado a gruppi di utenti sono esattamente l'opposto. Trovano la tipizzazione statica troppo ingombrante, perché limita il loro approccio alla risoluzione di alcuni tipi di problemi.

Sinceramente posso dire che salto molto tra JavaScript e C #. Ora, conoscere e lavorare in entrambe le lingue influenza in qualche modo l'altro, ma in verità il codice che scrivo in ogni aspetto è completamente diverso dall'altro. Richiedono un approccio diverso, perché sostanzialmente diversi. Quello che ho scoperto è che se ti ritrovi a pensare "Amico, è molto più difficile farlo in linguaggio X", il tuo approccio probabilmente è un po 'fuori. Ecco un esempio, le persone parlano del modo "Pythonic" di fare le cose. Ciò significa che esiste un modo in cui il linguaggio Python funziona per facilitare un problema. Farlo in un altro modo è generalmente più difficile e più ingombrante. Devi superare la gobba di sapere come funziona una lingua per funzionare davvero per te. E'


Sono stato per un po 'l'impressione che i linguaggi di programmazione dovrebbero nascondere solo le caratteristiche del tuo codice a cui non devi mai pensare. Questo vale per portare il codice macchina fino a qualcosa di più alto livello come Java perché quel livello inferiore di implementazione è qualcosa che praticamente non devi mai affrontare. Questo non è il caso dei tipi di oggetti. A mio avviso, la digitazione dinamica rende la programmazione più difficile perché introduce un'intera classe di errori che devi rilevare.
MCllorf

7

Di recente è stata posta una domanda simile: linguaggi dinamici e tipicamente statici per i siti Web

Per ribadire il nocciolo della mia risposta:

Man mano che i sistemi diventano più grandi, i linguaggi tipizzati staticamente garantiscono robustezza a livello di componente e quindi flessibilità a livello di sistema.

Sì, Java è tipizzato rigorosamente e sì, Java fa schifo (senza offesa. È terribile. Ottima piattaforma ed ecosistema, ma uno dei peggiori linguaggi di sempre (attualmente utilizzato)).
Ma deducendo da ciò, che la digitazione severa fa schifo è solo un errore. È come indicare PHP e dedurre che la digitazione dinamica fa schifo (di nuovo, senza offesa. Sta lentamente migliorando, te lo dò).

Personalmente, faccio gran parte del mio sviluppo in haXe , che ha un sistema di tipo statico. Non solo è significativamente più espressivo di quello di Java e richiede molto meno sforzo a causa dell'inferenza del tipo, ma è anche facoltativo. Se mai dovesse ostacolarti, basta aggirarlo.

La sicurezza dei tipi è una caratteristica (questo è qualcosa che molte lingue apparentemente di alto livello non vanno bene) per aiutarti a evitare di spararti ai piedi .
E su qualsiasi linguaggio correttamente digitato in modo dinamico sarebbe semplicemente meglio, se avessi l'opzione di controllare il tuo tipo di codice a piacimento.
Ad esempio, mi è sicuramente piaciuto sperimentare Ruby, ma principalmente perché Ruby è completamente orientato agli oggetti, che è del tutto ortogonale alla presenza di un sistema di tipi di tempo di compilazione.

Penso che l'affermazione secondo cui i sistemi di tipo statico siano ostruttivi si basi semplicemente sulla mancanza di conoscenza di buoni sistemi di tipo statico. Ci sono un certo numero di lingue che lo fanno nel modo giusto, haXe essendo uno di questi, e probabilmente nemmeno il migliore in questo senso.

Esempio di codice haXe:

class Car {
    public function new();
    public function wroom() trace('wroooooooom!')
}
class Duck {
    public function new();
    public function quack(at) trace('quackquack, ' + at + '!')
}

function letQuack(o) o.quack();
letQuack(new Car());
letQuack(new Duck());

Questo produrrà un errore di tempo di compilazione:

Car should be { quack : Void -> Unknown<0> }
Car has no field quack
For function argument 'o'
Duck should be { quack : Void -> Unknown<0> }
Invalid type for field quack :
to : String -> Void should be Void -> Unknown<0>
For function argument 'o'

Non puoi davvero affermare che ho dovuto fare molti sforzi per la sicurezza dei tipi.

Dire che non hai bisogno della sicurezza dei tipi, perché hai dei test è ancora più idiota. Scrivere test è noioso e ripetitivo. E davvero non voglio scrivere un test, solo per scoprire che un'istanza di Car non cigolerà e un'anatra ha bisogno di qualcuno a cui crogiolarsi.

Alla fine della giornata, troverai, non importa quanto costi generali ti siano costati la sicurezza, alla fine viene ammortizzato (anche in Java - anche se forse non così presto).


In python i doctest vengono semplicemente copiati e incollati da repl / shell, come fonte di documentazione e controllo successivo. docs.python.org/3/library/doctest.html
aoeu256

5

Per qualsiasi motivo, non commetto più errori legati al tipo di oggetto che spesso. In linguaggi come C #, ho maggiori probabilità di commettere errori relativi ai cast di runtime di quanto sia probabile che commetta un errore di sicurezza di tipo rilevabile dal compilatore, che, concedo, è solitamente causato dall'occasionale necessità di aggirare la staticità di un statico linguaggio tipizzato. Quando scrivo ruby, il codice tende a suggerire piuttosto fortemente il tipo di un oggetto e la disponibilità di un REPL significa che ho già verificato sperimentalmente l'esistenza del metodo / degli attributi desiderati, o avrò un test unitario che lo fa praticamente la stessa cosa, quindi raramente mi imbatto in problemi di sicurezza del tipo nel rubino.

Ma ciò non significa che i sistemi tipizzati staticamente non possano essere migliori di loro.

Nelle lingue tipizzate staticamente, anche il sistema dei tipi conta molto. Ad esempio, con qualcosa come la monade Some in linguaggi funzionali (digitare <Some>: = yes x | no), si ottengono controlli in fase di compilazione che essenzialmente impediscono la temuta NullReferenceException comune nella maggior parte dei sistemi di tipo; quando viene eseguito il codice di corrispondenza dei modelli, vengono visualizzati errori in fase di compilazione che indicano che non è stato possibile gestire la condizione nulla (se si utilizza quel meccanismo per dichiarare il tipo). Riduci anche tipi simili di errori quando usi cose come l'operatore pipeline |> in F #.

Nella tradizione Hindley-Milner della tipizzazione statica, puoi costruire cose che ti danno molto più di una garanzia che un tipo afferma di supportare l'interfaccia X, e una volta che hai quelle cose, direi che il sistema tipizzato staticamente diventa molto più prezioso.

Quando questa non è un'opzione, le estensioni di Design by Contract a C # possono aggiungere un altro set di meccanismi che aumentano il valore del sistema di tipo statico, ma richiedono ancora più disciplina rispetto ad alcuni di quei paradigmi funzionali.


5

Dipende.

Le modalità di fallimento umano sono spesso statistiche. Il controllo di tipo forte riduce la probabilità di alcuni tipi di guasti umani (che causano codice errato). Ma solo perché puoi fallire non significa sempre che lo farai (Murphy non resistere).

Se valga la pena il costo dipende da questa riduzione delle potenziali probabilità di fallimento.

Se stai scrivendo un codice per una centrale nucleare o un sistema ATC, qualsiasi riduzione della modalità di guasto umano potrebbe essere estremamente importante. Se stai prototipando rapidamente un'idea di un sito Web che non ha specifiche e con conseguenze quasi pari a zero, la riduzione delle modalità di guasto o delle probabilità può o meno acquistare nulla, ma può costarti in termini di tempo di sviluppo (più sequenze di tasti, ecc.), e nelle cellule cerebrali distratte dalla memorizzazione dei tipi attuali richiesti.


3
Il tuo scenario di prototipazione rapida è suggerito come sbagliato nel documento di Paul Hudak su uno studio della marina statunitense che ha richiesto di sviluppare una simulazione simile a AEGIS in diverse lingue, uno dei quali era Haskell. Soddisfa quasi tutti i tuoi criteri: è stata la prototipazione rapida, i requisiti dove sono stati definiti male e il costo del fallimento è stato vicino allo zero (trattandosi di un esperimento estremamente informale). Haskell è risultato il vincitore nella categoria evey: tempo di sviluppo, superamento dei requisiti, richiesta di meno LOC e produzione del solo esempio funzionante tra tutti i concorrenti!
Andres F.

2
L'articolo: Haskell vs ..., An Experiment in Software Prototyping Productivity - Paul Hudak e Mark P. Jones . Descrive i risultati di un esperimento ordinato da ARPA e dalla US Navy.
Andres F.

Il vincitore non era Relational Lisp? Amico, vorrei che ci fossero video che mostravano persone che codificavano cose in Lisp con tutte quelle strane estensioni potenti come Shen (un framework logico-relazionale che ti permette di dare tipi dipendenti al codice e mescolare e abbinare un codice di tipo con un codice non di tipo ), framework super-CLOS con invio predicato, ecc ...
aoeu256,

4

Ci sono stati molti sistemi molto complicati scritti in Lisp, e non ho sentito nessun Lisper lamentarsi del fatto che volessero scrivere in modo statico. Quando ho lavorato con esso, non ricordo alcun problema che mi ha rallentato molto che un sistema di tipo statico (e puoi specificare i tipi staticamente in Common Lisp) avrebbe riscontrato.

Inoltre, i principali linguaggi tipicamente statici non sembrano adatti a rilevare errori. Nella progettazione di un layout, ciò che è importante è che un certo numero è una misura verticale sulla pagina, non è che si tratti di int, unsigned, float, o double. D'altro canto, il compilatore contrassegnerà spesso le conversioni di tipo che ritiene non sicure e mi consente di aggiungere felicemente una misura verticale e il numero di caratteri in una stringa. Questa debolezza del sistema di tipo statico era l'idea originale alla base della notazione ungherese di Simonyi, prima che fosse trasformata in brutta inutilità.


4

I tipi sono vincoli sulle interfacce, quindi sono un sottoinsieme di ciò che potresti voler testare con i test unitari e quindi molti compromessi sono simili:

  • I tipi statici forniscono un feedback precedente sul fatto che il codice soddisfi o meno i requisiti che possono essere espressi dal sistema di tipi, in cambio del ritardo nel feedback dalla creazione di qualcosa di minimamente funzionale (come feedback dei clienti o test di livello superiore).
  • Sapere che il codice soddisfa determinati requisiti può facilitare il refactoring e il debug, ma aggiunge anche un sovraccarico alla modifica delle interfacce e alla modifica dei requisiti.
  • Soprattutto se un linguaggio tipicamente statico manca di coercizione, fornisce maggiore sicurezza contro il codice utilizzato su dati che potrebbero causare bug (riducendo la necessità di condizionali e asserzioni), ma vincoli eccessivamente restrittivi richiedono all'utente di scrivere più codice per massaggiare i propri dati in un forma accettabile (come il tipo esplicito casting).
  • Le annotazioni di tipo esplicito possono aiutare a comprendere durante la lettura del codice o possono ingombrare il codice con informazioni ridondanti o non necessarie.
  • A seconda dell'implementazione, può sminuire la terseness. Questo dipende da cose come se le annotazioni sui tipi sono richieste o dedotte, quanto bene il sistema dei tipi può esprimere tipi / interfacce generici, la sintassi e se si intende testare i vincoli che possono essere espressi dal sistema dei tipi (es. lo stesso test è probabilmente più conciso come funzionalità linguistica che come test unitario, ma potresti non aver intenzione di testarlo).
  • Inoltre (ma non correlato a TDD), i tipi statici possono aiutare l'ottimizzazione del tempo di compilazione, a scapito della necessità di controllare quei tipi (e impiegare il tempo per controllarli ed eseguire le ottimizzazioni), e una migliore ottimizzazione può essere fatta se i dati sono limitati ai tipi che si associa bene all'hardware. Ciò facilita lo sviluppo di codice con requisiti prestazionali, ma può causare problemi per il codice che non si adatta bene a questi vincoli (come da punto 3).

Per riassumere, direi che i linguaggi dinamici sono particolarmente utili per la prototipazione, mentre se devi essere sicuro che il tuo codice sia corretto, dovresti favorire un sistema di tipo forte.


3

Sì, sicuramente. Una cosa che troverai quando usi entrambi i linguaggi fortemente tipizzati e Python (Python è fortemente tipizzato) più è che la maggior parte del codice ben scritto in linguaggi dinamici tende comunque a seguire molte delle stesse convenzioni del codice fortemente tipizzato. La digitazione dinamica è molto utile per la serializzazione e la deserializzazione, ma per la maggior parte delle altre cose in realtà non contribuisce molto al vantaggio. E a meno che la maggior parte del codice non sia correlata alla serializzazione, perché eliminare il controllo degli errori gratuito?


4
I linguaggi fortemente tipizzati come Java e C # gestiscono automaticamente la deserializzazione mediante l'uso di Reflection.
Matthieu M.

3

Morgan, ho un'idea interessante da provare: statica + digitazione dinamica. Hai citato Python, C # e Java. Eri consapevole che ci sono alcune porte abbastanza buone di Python sia su .NET che su Java? In entrambi i casi, le porte consentono di utilizzare le librerie di tali piattaforme e / o interagire con il codice esistente. Questo ti offre diverse possibilità:

  1. Mantieni il codice legacy in un linguaggio statico e non flessibile. Usa Python per nuove cose.
  2. Usa Python per prototipare nuove cose su piattaforme mature. Ricodifica i componenti che desideri conservare nella lingua più matura.
  3. Usa il linguaggio dinamico per le parti che cambi spesso.
  4. Forse usa il linguaggio dinamico per giocare con idee come la modifica del codice in esecuzione.
  5. Fai tutto nel linguaggio dinamico tranne le parti critiche in cui usi un linguaggio fortemente tipizzato.

Ho usato questi approcci fin dalla fine degli anni '90 per evitare il dolore dello sviluppo in C / C ++. Avevo bisogno delle librerie native e talvolta delle prestazioni. Tuttavia, volevo la migliore sintassi, flessibilità, sicurezza, ecc. Quindi, il trucco era combinarli attentamente per ottenere i giusti compromessi. In pratica era spesso meglio che lanciare l'intera lingua e il codice legacy per un'altra lingua / piattaforma.

(Nota: una risposta l'ha già detto, ma voglio anche sottolineare nuovamente la tipizzazione dinamica! = Nessuna / digitazione debole. Molti sistemi di tipo dinamico usano una tipizzazione forte all'interno. Il modo in cui penso a ciò che rende una dinamica di tipo è che un tipo di variabile viene determinato in fase di esecuzione, non necessita di un'annotazione di tipo e / o potrebbe cambiare in fase di esecuzione.


2

Non otterrai una risposta veramente obiettiva a questo, ma la mia esperienza è che la sicurezza del tipo è preziosa fino a quando non padroni TDD. Una volta che hai una copertura pesante per i test unitari, in cui i test sono stati scritti prima del codice, il controllo del compilatore diventa una seccatura e inizia effettivamente a ostacolarti.


questo è un QA soggettivo, quindi sto bene.
Morgan Herlocker,

1
Qualcuno ha cura di spiegare i voti negativi?
pdr

Non posso aiutarti con la spiegazione, ma ti ho dato un +1, penso che questo sia un contributo utile. Una delle paure chiave con la digitazione dinamica è che farai una modifica da qualche parte e romperai qualcosa da qualche altra parte a causa di ipotesi che sarebbero state applicate dal compilatore in un linguaggio tipicamente statico. Una copertura pesante per i test unitari ti proteggerà qui.
Carson63000,

5
Non ho espresso il mio voto negativo in quanto hai formulato un punto valido, anche se non è stata intesa alcuna offesa, ma il tuo post si presenta come un piccolo fanboy del TDD, che è probabilmente il motivo per cui i voti negativi.
Karl Bielefeldt,

@Karl, senza offesa, è stata una vera domanda. Posso essere apologeticamente pro-TDD, lo ammetto
pdr il

2

Vedo questa domanda sorgere molto e penso che la qualità del tuo software (e la mancanza di bug) abbia più a che fare con il tuo processo di sviluppo, come il tuo sistema è progettato e l'impegno di te e dei tuoi colleghi per la qualità del codice.

Il mio ultimo lavoro è stato principalmente lo sviluppo di Python. Ho lavorato per una grande società internazionale di web hosting e avevamo team di sviluppo negli Stati Uniti, in Canada e in Corea del Sud. Framework Web python personalizzato per l'app per clienti front-end che ha consentito agli utenti di gestire i propri nomi di dominio e account di web hosting. Backend: anche tutti i pitoni. Servizio web Python per parlare con singoli server per fare cose come fare il provisioning di un nuovo sito di web hosting, creare un nuovo blog, creare voci dns nel nostro sistema di servizi di nomi; ecc. ecc. Nel mio attuale lavoro, le app client sono tutte in Java; il nostro prodotto principale è una miscela di java e flash. Framework web java personalizzato per le nostre vecchie app, wicket per i nostri nuovi strumenti interni.

Avendo lavorato in entrambi, devo dire che questa domanda mi dà fastidio ogni volta che la vedo. Se stai usando un linguaggio tipizzato in modo dinamico e testerai effettivamente il tuo codice, starai bene. Se il sistema è ben progettato e segui gli standard, starai bene. Non ci sono mai stati molti bug che sono emersi a causa della mancanza di un compilatore che controlla i tipi. La maggior parte dei bug erano errori logici, proprio come il mio lavoro di java oggi.


2

La sicurezza del tipo vale la pena per la velocità di sviluppo e flessibilità? PERCHÉ?

La tipizzazione statica è un netto aumento della velocità e della flessibilità dello sviluppo durante l'intero ciclo di vita del software. Riduce lo sforzo totale e l'inconveniente, ma sposta molto lo sforzo e l'inconveniente in anticipo, dove è più evidente. La barriera d'ingresso per avere un codice funzionante è più alta, ma una volta superata quella barriera (soddisfacendo il controllo del tipo), estendere e mantenere quel codice richiede molto meno lavoro.

Ci saranno sempre alcuni mal di testa nello sviluppo del software a causa di:

  • La complessità intrinseca di ciò che stai cercando di realizzare

  • L'intrinseca fallibilità degli umani, soprattutto considerando che commettiamo più errori quando proviamo a fare qualcosa di più complesso

Prima o poi, devi prendere del tempo per affrontare queste sfide. Non c'è niente da fare. La digitazione statica affronta semplicemente queste sfide prima piuttosto che dopo. Prima è meglio che in seguito, perché più tardi scopri un errore (non una domanda se , ma quando ), più costa correggerlo.

Costa molto meno correggere un errore segnalato da un verificatore del tipo di quanto costa per eseguire il debug di un'eccezione relativa al tipo sollevata in fase di esecuzione. Il rinvio del controllo del tipo al runtime sta semplicemente spazzando il problema sotto il tappeto.


1

Questa è solo la mia opinione, ma no, non credo che la sicurezza del tipo valga la pena. Neanche per un secondo.

Sono uno sviluppatore da molto tempo. Iniziando con c ++, c #, quindi spostato in javascript (frontend e backend tramite node.js). Da quando sto sviluppando in JavaScript la mia produttività è aumentata vertiginosamente, al punto che in realtà mi aggravano usando linguaggi basati sul tipo. Sono anche contrario alla compilazione, voglio che tutto sia in fase di esecuzione ora. Le lingue interpretate sono davvero quelle in cui ho trovato il mio amore per la programmazione.

Per quanto riguarda i tipi, non vedo alcun vantaggio. Ora vedo i tipi nello stesso modo in cui vedo la gestione della memoria. Completamente inutile Le lingue di domani dovrebbero proteggere completamente lo sviluppatore dal sapere qualcosa sui tipi. Il computer dovrebbe comprendere i tipi e lasciare fuori lo sviluppatore.

Ecco un esempio Stavo solo usando Swift (il nuovo linguaggio di Apple) sperando che fosse effettivamente all'altezza del suo nome un giorno fa e ho provato a farlo: var n = 1/2 non ha funzionato. Ero tipo, cosa sta succedendo qui. e poi tristemente capito che dovevo fare var n: Float = 1/2. Questo mi ha ricordato quanto odio i sistemi di tipo e la quantità di inutili aggravamenti che sono.

Vorrei anche fare un altro miglio per dire che non voglio nemmeno tipi definiti dall'utente (come Classi). Non voglio affatto tipi. Tutto quello che voglio è var e oggetti. Dove qualsiasi oggetto può essere utilizzato come qualsiasi oggetto. E gli oggetti sono dinamici e in continua evoluzione. Dove diventa un problema di runtime su ciò che funziona e cosa no.

Gli sviluppatori adorano dire come i linguaggi tipicamente vaghi non siano adatti ai grandi progetti. Ma direi che è il contrario. I linguaggi fortemente tipizzati sono orrendi per grandi progetti. E se dici che javascript non funziona per grandi progetti, chiedi a Uber un'azienda di oltre 40 miliardi di dollari che esegue tutto il suo backend su node.js / javascript o Facebook che è iniziato con PHP.

Per quanto riguarda le lingue tipicamente statiche non è buono per le iterazioni veloci di oggi. ecco un semplice esempio, hai 10 sviluppatori che lavorano su un progetto .net con un server di integrazione continua, uno sviluppatore invia un errore e l'intera build è rotta, anche se i 10 sviluppatori stanno lavorando su cose diverse, ora sono tutti fermi e in attesa per lo sviluppatore offensivo correggere il suo errore. Parli di efficiente eh? I linguaggi di sistema / statici di tipo sono interdipendenti in questo modo e rendono il codice interdipendente. Tuttavia, i file di script non sono mai interdipendenti. Se c'è un problema con uno degli script che non interrompe la produzione, tutti i problemi che vedresti vengono lasciati al runtime. E il runtime non si ferma mai. Non si rompe mai. Potrebbe produrre un output errato ma non


1
Molti "io", non molta sostanza di discussione. E comunque, se un errore "rompe" la build non ha nulla a che fare con statico o dinamico. Se hai dei test unitari e uno fallisce "la tua build è rotta" e si spera non si distribuirà alla produzione fino a quando non sarà corretta
nafg

Cosa ti ha fatto pensare che io abbia insinuato qualcosa del genere?
nafg

La tua produttività in javascript non è salita alle stelle perché a javascript mancavano i tipi. La tua produttività è aumentata alle stelle perché C ++ e C # sono linguaggi pesanti. I tipi Javascript + aumenteranno ulteriormente la tua produttività. Nessuno ha detto che JavaScript è impossibile per grandi progetti. Javascript su grandi progetti è certamente fattibile. Tuttavia non è l'ideale. I test unitari sostituiscono il controllo del tipo, anche i test unitari hanno una copertura del tipo limitata mentre il controllo del tipo ha una copertura del 100%.
Brian Yeh,

1
@BrianYeh c ++ e c # sono linguaggi pesanti perché sono incentrati sui tipi. Ho appena iniziato a utilizzare il reattore nel mio lavoro e la mia produttività è crollata ancora una volta a causa dell'uso incessante di tipi e componenti. se ti piacciono i tipi e i test unitari, va bene per te. non tutti condividiamo questo stile di programmazione.

1
@foreyez reactionjs non ha tipi. Probabilmente ti riferisci al flusso. I test unitari sostituiscono il controllo del tipo, quindi se non si dispone del controllo del tipo sono necessari ulteriori test unitari. Prove e tipi di unità sono forze opposte. La tua produttività in calo è un'illusione. Qualsiasi errore di tipo che si rileva in una lingua di tipo sicuro è un errore altrimenti non rilevato in una lingua tipizzata in modo dinamico. Sembra solo più veloce. Il tipo di linguaggio sicuro ti costringe a gestire questi errori in anticipo.
Brian Yeh,

0

SÌ.

Ho lavorato in applicazioni PHP, in cui i tipi non sono "forti" come in Java o C #. Di solito ho finito di "simulare i tipi", perché, al fine di evitare cattive conversioni automatiche o la convalida dei dati.

Le lingue di tipo dinamico sono utili per gli script del sistema operativo e le piccole app rapide, non le app complesse.

Riepilogo: se devo scegliere tra un linguaggio di programmazione "Tipo debole" o "Tipo dinamico" o un linguaggio di programmazione "Tipo forte" per un'applicazione aziendale complessa, scelgo il linguaggio di programmazione "Tipo forte" .


0

Penso che sia utile fare un passo indietro e considerare quando la digitazione dinamica causa problemi.

Un caso è quello in cui un ramo di codice non viene testato affatto, ma è probabile che il codice che non viene mai testato sia difettoso indipendentemente dal fatto che sia in uso o meno la tipizzazione dinamica.

Un altro problema più sottile, tuttavia, è la sostituibilità imperfetta.

Se un tipo è completamente sbagliato, a meno che non venga mai utilizzato un determinato percorso di codice, è probabile che venga rilevato rapidamente.

D'altra parte se un tipo è un sostituto imperfetto, allora il codice può funzionare per lo più ma rompersi in modi sottili che non saranno rilevati fino a molto tempo dopo.

Due dei tipi più comuni in programmazione sono numeri e stringhe. In molti linguaggi dinamici sono sostituti imperfetti l'uno dell'altro. Ad esempio javascript o php se si fornisce un numero in cui è prevista una stringa o viceversa, il programma viene eseguito senza generare un errore ma può comportarsi in modo errato in modi piuttosto subtule.

Python ha evitato che particolari problemi, numeri e stringhe non si sostituiscano in alcun modo l'uno con l'altro e il tentativo di usarne uno laddove si prevede che l'altro porti normalmente a un rapido fallimento.

Tuttavia, non ha evitato del tutto il problema della vulnerabilità imperfetta. Diversi tipi di numeri possono sostituirsi in modo imperfetto l'uno all'altro, così come diversi tipi di sequenze.


Quello che sto ottenendo qui è che non penso sia possibile confrontare i benefici e i costi della tipizzazione statica e dinamica in modo generico, perché penso che sia i benefici che i costi dipendono dalla particolare variazione della tipizzazione statica o dinamica di una lingua utilizza.

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.