Idiomas dinamicamente digitados são uni-tipados
Comparando sistemas de tipos , não há vantagem na digitação dinâmica. A digitação dinâmica é um caso especial de digitação estática - é uma linguagem com estaticamente onde cada variável tem o mesmo tipo. Você pode conseguir o mesmo em Java (menos concisão), tornando todas as variáveis do tipo Object
e tendo os valores de "objeto" do tipo Map<String, Object>
:
void makeItBark(Object dog) {
Map<String, Object> dogMap = (Map<String, Object>) dog;
Runnable bark = (Runnable) dogMap.get("bark");
bark.run();
}
Portanto, mesmo sem reflexão, você pode obter o mesmo efeito em praticamente qualquer linguagem de tipo estaticamente, deixando de lado a conveniência sintática. Você não está recebendo nenhum poder expressivo adicional; ao contrário, você tem menos poder expressivo porque em uma linguagem de tipagem dinâmica, você está negado a capacidade de restringir variáveis para determinados tipos.
Fazendo um latido de pato em um idioma estaticamente tipado
Além disso, uma boa linguagem de tipo estaticamente permitirá que você escreva um código que funcione com qualquer tipo que tenha uma bark
operação. No Haskell, esta é uma classe de tipo:
class Barkable a where
bark :: a -> unit
Isso expressa a restrição de que, para que algum tipo a
seja considerado Barkable, deve existir uma bark
função que aceite um valor desse tipo e não retorne nada.
Você pode escrever funções genéricas em termos de Barkable
restrição:
makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)
Isso diz que makeItBark
funcionará para qualquer tipo que satisfaça Barkable
os requisitos. Isso pode parecer semelhante ao interface
Java ou C #, mas tem uma grande vantagem - os tipos não precisam especificar antecipadamente quais classes de tipos eles satisfazem. Posso dizer que o tipo Duck
é Barkable
a qualquer momento, mesmo que Duck
seja um tipo de terceiros que não escrevi. De fato, não importa que o escritor de Duck
não tenha escrito uma bark
função - eu posso fornecê-la posteriormente quando digo a linguagem que Duck
satisfaz Barkable
:
instance Barkable Duck where
bark d = quack (punch (d))
makeItBark (aDuck)
Isso diz que Duck
s pode latir, e sua função de latido é implementada perfurando o pato antes de torná-lo charlatão. Com isso fora do caminho, podemos chamar os makeItBark
patos.
Standard ML
e OCaml
são ainda mais flexíveis, pois você pode satisfazer a mesma classe de tipo de mais de uma maneira. Nessas línguas, posso dizer que números inteiros podem ser ordenados usando a ordenação convencional e, em seguida, dar a volta e dizer que também podem ser ordenados por divisibilidade (por exemplo, 10 > 5
porque 10 é divisível por 5). No Haskell, você só pode instanciar uma classe de tipo uma vez. (Isso permite que Haskell saiba automaticamente que não há problema em chamar bark
um pato; no SML ou no OCaml, você precisa ser explícito sobre qual bark
função deseja, porque pode haver mais de um.)
Concisão
Claro, existem diferenças sintáticas. O código Python que você apresentou é muito mais conciso do que o equivalente em Java que escrevi. Na prática, essa concisão é uma grande parte do fascínio das linguagens de tipo dinâmico. Mas a inferência de tipo permite que você escreva um código tão conciso nas linguagens de tipo estaticamente, dispensando a necessidade de escrever explicitamente os tipos de todas as variáveis. Uma linguagem de tipo estático também pode fornecer suporte nativo para digitação dinâmica, removendo a verbosidade de todas as manipulações de elenco e mapa (por exemplo, C # 's dynamic
).
Programas corretos, mas incorretos
Para ser justo, a digitação estática exclui necessariamente alguns programas tecnicamente corretos, embora o verificador de tipos não possa verificá-lo. Por exemplo:
if this_variable_is_always_true:
return "some string"
else:
return 6
A maioria das linguagens de tipo estaticamente rejeitaria essa if
declaração, mesmo que o ramo else nunca ocorra. Na prática, parece que ninguém faz uso desse tipo de código - algo muito inteligente para o verificador de tipos provavelmente fará com que futuros mantenedores do seu código amaldiçoem você e seus parentes mais próximos. Caso em questão, alguém traduziu com êxito 4 projetos Python de código aberto para Haskell, o que significa que eles não estavam fazendo nada que uma boa linguagem de tipo estatístico não pudesse compilar. Além disso, o compilador encontrou alguns bugs relacionados a tipos que os testes de unidade não estavam detectando.
O argumento mais forte que eu já vi para a digitação dinâmica são as macros do Lisp, pois elas permitem que você estenda arbitrariamente a sintaxe da linguagem. No entanto, o Typed Racket é um dialeto estatístico do Lisp que possui macros, portanto parece que a digitação estática e as macros não são mutuamente exclusivas, embora talvez seja mais difícil de implementar simultaneamente.
Maçãs e laranjas
Por fim, não esqueça que há diferenças maiores nos idiomas do que apenas no sistema de tipos. Antes do Java 8, fazer qualquer tipo de programação funcional em Java era praticamente impossível; um lambda simples exigiria 4 linhas de código de classe anônimo padrão. Java também não tem suporte para literais de coleção (por exemplo [1, 2, 3]
). Também pode haver diferenças na qualidade e disponibilidade de ferramentas (IDEs, depuradores), bibliotecas e suporte da comunidade. Quando alguém afirma ser mais produtivo em Python ou Ruby do que Java, essa disparidade de recurso precisa ser levada em consideração. Há uma diferença entre comparar idiomas com todas as baterias incluídas , núcleos de idiomas e sistemas de tipos .
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof"))
. Esse argumento não é nem uma classe , é uma tupla anônima chamada. A digitação de pato ("se quack como um ...") permite fazer interfaces ad hoc com restrições praticamente nulas e sem sobrecarga sintática. Você pode fazer isso em uma linguagem como Java, mas acaba com muita reflexão confusa. Se uma função em Java requer um ArrayList e você deseja atribuir outro tipo de coleção, você é SOL. Em python, isso nem pode surgir.