Le lingue tipizzate dinamicamente sono tipizzate uni
Confrontando i sistemi di tipi , non c'è alcun vantaggio nella digitazione dinamica. La tipizzazione dinamica è un caso speciale di tipizzazione statica : è un linguaggio di tipo statico in cui ogni variabile ha lo stesso tipo. È possibile ottenere la stessa cosa in Java (meno concisione) rendendo ogni variabile di tipo Object
e avendo valori di "oggetto" di tipo Map<String, Object>
:
void makeItBark(Object dog) {
Map<String, Object> dogMap = (Map<String, Object>) dog;
Runnable bark = (Runnable) dogMap.get("bark");
bark.run();
}
Quindi, anche senza riflessione, puoi ottenere lo stesso effetto in quasi ogni linguaggio tipicamente statico, a parte la convenienza sintattica. Non stai ottenendo alcun potere espressivo aggiuntivo; al contrario, hai meno potere espressivo perché in un linguaggio tipizzato in modo dinamico, ti viene negata la possibilità di limitare le variabili a determinati tipi.
Produrre una corteccia d'anatra in un linguaggio tipicamente statico
Inoltre, un buon linguaggio tipicamente statico ti permetterà di scrivere codice che funziona con qualsiasi tipo che abbia bark
un'operazione. In Haskell, questa è una classe di tipo:
class Barkable a where
bark :: a -> unit
Ciò esprime il vincolo secondo cui per un tipo a
da considerare Barkable, deve esistere una bark
funzione che assume un valore di quel tipo e non restituisce nulla.
È quindi possibile scrivere funzioni generiche in termini di Barkable
vincolo:
makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)
Questo dice che makeItBark
funzionerà per qualsiasi tipo di Barkable
requisito soddisfacente. Questo potrebbe sembrare simile a un interface
in Java o C # ma ha un grande vantaggio: i tipi non devono specificare in anticipo quali classi di tipi soddisfano. Posso dire che il tipo Duck
è Barkable
in qualsiasi momento, anche se Duck
è un tipo di terze parti che non ho scritto. In realtà, non importa che lo scrittore Duck
non abbia scritto una bark
funzione: posso fornirlo dopo il fatto quando dico la lingua che Duck
soddisfa Barkable
:
instance Barkable Duck where
bark d = quack (punch (d))
makeItBark (aDuck)
Questo dice che i Duck
s possono abbaiare e la loro funzione di corteccia è implementata dando un pugno all'anatra prima di farlo ciarlare. Detto questo, possiamo fare appello makeItBark
alle anatre.
Standard ML
e OCaml
sono ancora più flessibili in quanto puoi soddisfare la stessa classe di tipo in più di un modo. In queste lingue posso dire che gli interi possono essere ordinati usando l'ordinamento convenzionale e quindi girare e dire che sono ordinabili anche per divisibilità (ad esempio 10 > 5
perché 10 è divisibile per 5). In Haskell è possibile creare un'istanza di una classe di tipo una sola volta. (Ciò consente a Haskell di sapere automaticamente che è possibile chiamare bark
un'anatra; in SML o OCaml devi essere esplicito su quale bark
funzione vuoi, perché potrebbe essercene più di una.)
conciseness
Certo, ci sono differenze sintattiche. Il codice Python che hai presentato è molto più conciso dell'equivalente Java che ho scritto. In pratica, quella concisione è una grande parte del fascino dei linguaggi tipizzati dinamicamente. Ma l'inferenza del tipo ti consente di scrivere un codice altrettanto conciso nei linguaggi tipicamente statici, sollevandoti dal dover scrivere esplicitamente i tipi di ogni variabile. Un linguaggio tipicamente statico può anche fornire supporto nativo per la tipizzazione dinamica, eliminando la verbosità di tutte le manipolazioni di casting e mappe (ad es. C # dynamic
).
Programmi corretti ma mal digitati
Per essere onesti, la tipizzazione statica esclude necessariamente alcuni programmi che sono tecnicamente corretti anche se il controllo del tipo non può verificarlo. Per esempio:
if this_variable_is_always_true:
return "some string"
else:
return 6
La maggior parte delle lingue tipicamente statiche respingerebbe questa if
affermazione, anche se il ramo else non si verificherà mai. In pratica sembra che nessuno faccia uso di questo tipo di codice - qualcosa di troppo intelligente per la verifica dei tipi probabilmente farà in modo che i futuri manutentori del tuo codice malediscano te e il tuo parente prossimo. Caso in questione, qualcuno ha tradotto con successo 4 progetti Python open source in Haskell, il che significa che non stavano facendo nulla che un buon linguaggio tipicamente statico non potesse compilare. Inoltre, il compilatore ha rilevato un paio di bug relativi al tipo che i test unitari non stavano rilevando.
L'argomento più forte che ho visto per la digitazione dinamica sono le macro di Lisp, poiché consentono di estendere arbitrariamente la sintassi del linguaggio. Tuttavia, Typed Racket è un dialetto di Lisp tipicamente statico che ha macro, quindi sembra che la tipizzazione statica e le macro non si escludano a vicenda, anche se forse sono più difficili da implementare contemporaneamente.
Mele e arance
Infine, non dimenticare che ci sono differenze più grandi nelle lingue rispetto al loro sistema di tipi. Prima di Java 8, fare qualsiasi tipo di programmazione funzionale in Java era praticamente impossibile; un semplice lambda richiederebbe 4 righe di codice anonimo di classe di boilerplate. Java non supporta inoltre i letterali di raccolta (ad es [1, 2, 3]
.). Possono esserci anche differenze nella qualità e nella disponibilità di strumenti (IDE, debugger), librerie e supporto della comunità. Quando qualcuno ha affermato di essere più produttivo in Python o Ruby rispetto a Java, è necessario tenere conto di tale disparità. C'è una differenza tra il confronto delle lingue con tutte le batterie incluse , i core della lingua e i sistemi di tipi .
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof"))
. Quell'argomento non è nemmeno una classe , è una tupla anonima. La digitazione Duck ("se si blocca come un ...") ti consente di realizzare interfacce ad hoc con restrizioni essenzialmente zero e nessun sovraccarico sintattico. Puoi farlo in un linguaggio come Java, ma finisci con un sacco di riflessioni disordinate. Se una funzione in Java richiede un ArrayList e vuoi assegnargli un altro tipo di raccolta, sei SOL. In pitone che non può nemmeno venire fuori.