A digitação estática vale as compensações?


108

Comecei a codificar em Python principalmente onde não há segurança de tipo, depois mudei para C # e Java onde houver. Descobri que poderia trabalhar um pouco mais rapidamente e com menos dores de cabeça em Python, mas, novamente, meus aplicativos C # e Java estão em um nível de complexidade muito mais alto, por isso nunca suponho que nunca fiz ao Python um verdadeiro teste de estresse.

Os campos Java e C # fazem parecer que, sem o tipo de segurança, a maioria das pessoas enfrentaria todos os tipos de bugs terríveis deixados à direita e isso seria mais complicado do que vale a pena.

Esta não é uma comparação de idiomas; portanto, não trate de questões como compiladas ou interpretadas. A segurança do tipo vale a pena na velocidade de desenvolvimento e flexibilidade? PORQUE?

para as pessoas que queriam um exemplo da opinião de que a digitação dinâmica é mais rápida:

"Use uma linguagem de tipo dinâmico durante o desenvolvimento. Ele fornece feedback mais rápido, tempo de resposta e velocidade de desenvolvimento". - http://blog.jayway.com/2010/04/14/static-typing-is-the-root-of-all-evil/



8
@Prof Plum: posso exigir uma prova de que há um impacto na velocidade de desenvolvimento e flexibilidade? Uma vez que estamos a falar de um aspecto particular Tipo de segurança , usando Javaou C#seria inconclusivo, sua maneira de fornecer não é o único ...
Matthieu M.

32
Com diligência em um idioma estrito, você pode minimizar as "dores de cabeça" e até ver um aumento de velocidade devido ao preenchimento automático do IDE, geração de código e dicas de código.
18711 Nicole

9
@Prof Plum: Entendo, não espero que você (ou alguém realmente) tenha testado completamente todas as linguagens já criadas ^^ O problema é que a maioria das pessoas que vi reclamando de algum aspecto específico das linguagens de programação (Static A digitação geralmente vem) geralmente reclamam de uma implementação específica e não conseguem realizá-la.
Matthieu M.

5
@Prof Plum, tudo o que o blog realmente tem a dizer sobre velocidade é a afirmação careca "Qualquer um que tenha trabalhado seriamente com uma linguagem moderna de tipo dinâmico, como Ruby ou Smalltalk, sabe que é mais produtivo". Não há exemplos reais de como, em termos práticos, torna o desenvolvimento mais rápido.
Carson63000

Respostas:


162

É um mito que os programadores não precisam se preocupar com tipos em linguagens dinamicamente tipadas.

Em idiomas tipificados dinamicamente:

  • Você ainda precisa saber se está trabalhando com uma matriz, um número inteiro, uma sequência de caracteres, uma tabela de hash, uma referência de função, um dicionário, um objeto ou qualquer outra coisa.

  • Se é um objeto, você precisa saber a que classe pertence.

  • A atribuição de um desses tipos a um parâmetro de variável ou função que se espera que seja outro tipo é quase sempre um erro.

  • Em um nível inferior, coisas como número de bits ou assinados versus não assinados com frequência ainda devem ser contabilizados se você estiver preenchendo um pacote TCP, por exemplo.

  • Você pode encontrar problemas onde obtém um zero, onde realmente queria uma string vazia. Em outras palavras, você ainda está depurando erros de incompatibilidade de tipo. A única diferença real é que o compilador não está capturando os erros.

  • Eu diria que você não está salvando muita digitação -, porque você tende a querer documentar nos comentários o tipo de seus parâmetros de função, em vez de documentá-lo em seu código. É por isso que os blocos de comentários no estilo doxygen são muito mais populares na prática em todo o código digitado dinamicamente, onde nas linguagens estaticamente tipificadas você geralmente os vê apenas para bibliotecas.

Isso não quer dizer que a programação em linguagens de tipagem dinâmica não sentir mais agradável porque o compilador nem sempre está em sua volta, e programadores experientes não tendem a ter dificuldade em encontrar e corrigir o tipo de erros que tipagem estática iria pegar de qualquer maneira , mas esse é um problema completamente separado de um suposto aumento na eficiência ou redução na taxa de erros, para o qual a digitação dinâmica é, na melhor das hipóteses, mesmo com a digitação estática.


10
Eu teria que contestar um pouco sobre programadores experientes que não produzem / apresentam esse tipo de erro. Bons desenvolvedores que são humildes e reconhecem a possibilidade de cometer erros (e desenvolvedores experientes nem sempre são assim) têm menos probabilidade de criar esses bugs.
Jay Jay Jay

12
Não poderia concordar mais com "Eu diria que você não está economizando muita digitação". Você acaba documentando tipos em comentários e verificando-os em seus testes, o que, se houver, requer mais digitação e manutenção (afinal, você deve se lembrar de atualizar esses comentários sempre que seus tipos forem alterados e, na maioria das vezes, você não )
Severyn Kozak

Passamos muito mais tempo em nossa loja Python, documentando tipos do que economizamos em uma linguagem detalhada de tipo estatístico, como C # ou Java. Também é importante notar que a nova geração de idiomas como Go e Rust usa inferência de tipo, então você está digitando "x: = new (Object)" em vez de "Object x = new Object ()".
Weberc2

Concordo com você quando diz que a linguagem dinâmica se sente mais agradável, mas não sei por que. Você tem uma explicação para isso?
Rodrigo Ruiz

Sim, em vez de fornecer o tipo de variável, você pode usar valores padrão ou testes de unidade (documentos em linha) no Python. Também em Python, às vezes, você pode ter erros estranhos com erros ortográficos [menos propensos a acontecer se você usar o preenchimento automático, que geralmente pode ser usado, embora nem sempre seja o tempo todo nas linguagens dinâmicas] e ter que descobrir se self.bread = 5 está introduzindo pão ou redefinindo-o.
aoeu256 01/07

123

À medida que os tipos ficam mais fortes, eles podem ajudá-lo mais - se você os usar corretamente, em vez de combatê-los. Projete seus tipos para refletir o espaço do problema e os erros lógicos provavelmente se tornarão incompatibilidades de tipo em tempo de compilação, em vez de travamentos de tempo de execução ou resultados sem sentido.


37
+1! "É mais provável que erros de lógica se tornem incompatibilidades do tipo em tempo de compilação, em vez de travamentos de tempo de execução ou resultados sem sentido": resposta realmente boa! Quando levo mais tempo para projetar meus tipos, o código segue mais naturalmente e geralmente está correto assim que é compilado. Projetar um tipo significa entender um domínio e suas operações.
Giorgio

78

Disclaimer: Eu sou um amante de tipos;)

É difícil responder à sua pergunta: o que são essas compensações ?

Vou dar um exemplo extremo: Haskell , é estaticamente digitado. Talvez uma das linguagens mais fortemente tipadas que existem, de fato.

No entanto, Haskell oferece suporte à programação genérica , no sentido de que você escreve métodos que funcionam com qualquer tipo que esteja em conformidade com um determinado conceito (ou interface).

Além disso, Haskell usa Inferência de Tipo , para que você nunca precise declarar o tipo de suas variáveis. Eles são computados estaticamente durante a compilação, da mesma forma que um interpretador Python os computaria executando o programa.

Descobri que a maioria das pessoas que gostava de digitar estática estava realmente reclamando de outra coisa (verbosidade, dor de trocar um tipo em favor de outro), mas Haskell não exibe nenhum desses problemas enquanto está sendo tipicamente estatizado ...


Exemplo de brevidade:

-- type
factorial :: Integer -> Integer

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

Além do suporte embutido, é difícil ficar mais breve.

Exemplo de programação genérica:

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

Exemplo de Inferência de Tipo:

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

que pode ser calculado simplesmente:

  • "hello"é uma lista de Char(expressa como [Char])
  • reverseaplicado a um tipo [A]retorna um tipo[A]

Experimente no seu navegador


4
Para bancar o advogado do diabo, uma troca a favor de linguagens dinâmicas (pelo menos durante a prototipagem) é que, na medida em que as declarações de tipo podem servir ao mesmo objetivo que alguns testes de unidade, elas também podem solidificar interfaces da mesma maneira que os testes de unidade ( embora certamente com menos sobrecarga). Além disso, linguagens de tipo estaticamente sem coerção, embora mais seguras, exigem a conversão explícita de tipos (principalmente quando um tipo não é genérico o suficiente), o que pode prejudicar a dispersão.
TR

7
Eu não sei Haskell, mas +1 para "foram realmente reclamando sobre outra coisa (verbosidade, dor de mudar de um tipo em favor de outro)"
Nicole

11
@Aidan: Haskell é uma linguagem em evolução. Haskell 98 foi uma melhoria em relação ao Haskell 1.4; Haskell 2010 foi uma melhoria em relação a isso. Enquanto isso, vale a pena notar que, durante a maior parte de sua vida, a razão de ser de Haskell foi ajudar a explorar sistemas de tipos; as classes de tipo multiparâmetros são um exemplo de onde ele conseguiu elucidar uma extensão útil do sistema de tipos. (Por outro lado, as dependências funcionais estão olhando para ser uma espécie de beco sem saída.)
geekosaur

4
@ Matthieu: WRT "Talvez uma das linguagens mais fortemente tipadas que existem, na verdade.", Verei seu Haskell e criará você Agda e Coq . (Vou conceder que é provavelmente o mais fortemente tipado praticamente útil de linguagem.)
geekosaur

11
@ Matthieu: Os assistentes de prova são uma aplicação direta da correspondência Curry-Howard; portanto, eles estão no coração das linguagens de programação (embora com bibliotecas padrão bastante limitadas). Eles estão na vanguarda da pesquisa de tipos dependentes porque você precisa de tipos dependentes para fazer bom uso da correspondência "tipos são proposições".
Geekosaur

37

Gosto de linguagens de tipo estático e de tipo dinâmico. As duas maiores vantagens da segurança de tipo para mim são:

1) Geralmente, você pode deduzir o que uma função faz puramente a partir de sua assinatura de tipo (isso é particularmente verdade em linguagens funcionais como Haskell).

2) Quando você faz refatoração significativa, o compilador informa automaticamente tudo o que você precisa fazer para manter tudo funcionando. Quando refato algo em C ++, meu procedimento geralmente é simplesmente a) alterar a parte que sei que quero alterar e b) corrigir todos os erros de compilação.


Exatamente o mesmo comigo, e sempre que eu quero refatorar alguma coisa (eu uso principalmente golang / typescript / java), sim, essas duas etapas são as que alguém precisaria. mude uma parte e corrija todos os erros de compilação :) resposta perfeita.
Nishchal Gautam 12/09/19

29

Pessoalmente, acho que esse tipo de segurança me ajuda a desenvolver mais rapidamente em meu trabalho atual. O compilador faz muita verificação de sanidade para mim quase enquanto digito, permitindo que eu me concentre mais na lógica de negócios que estou implementando.

O ponto principal para mim é que, embora eu perca alguma flexibilidade, ganho algum tempo que seria gasto rastreando problemas de tipo.


13

Há muitas opiniões fortes em torno do debate, mas obviamente isso não é realmente uma questão de opinião, é uma questão de fatos . Portanto , devemos olhar para a pesquisa empírica . E a evidência disso é clara:

Sim , a digitação estática vale a pena - e não apenas um pouco, mas de fato substancialmente . De fato, evidências sólidas mostram que a digitação estática pode reduzir o número de bugs no código em pelo menos 15% (e essa é uma estimativa baixa, a porcentagem real é quase certamente maior). Esse é um número surpreendentemente alto : acho que mesmo a maioria dos defensores da tipagem estática não pensaria que isso fazia uma diferença tão drástica.

Considere o seguinte: se alguém lhe dissesse que havia uma maneira simples de reduzir os bugs em seu projeto em 15% da noite para o dia, isso seria um acéfalo. 1 É quase a bala de prata proverbial.

As evidências são apresentadas no artigo Para digitar ou não digitar: quantificando erros detectáveis ​​em JavaScript por Zheng Gao, Christian Bird e Earl T. Barr. Encorajo todos a lerem, é um artigo bem escrito que apresenta pesquisa exemplar.

É difícil resumir sucintamente o quão rigorosamente os autores realizaram sua análise, mas aqui está um esboço (muito aproximado):

TypeScript e Flow são duas linguagens de programação baseadas em JavaScript que, embora permaneçam compatíveis, adicionam dicas de tipo e verificação de tipo estático. Isso permite aumentar o código existente por tipos e depois digitá-lo.

Os pesquisadores coletaram projetos de código aberto escritos em JavaScript no GitHub, analisaram os relatórios de bugs resolvidos e tentaram reduzir cada um dos bugs relatados para um pedaço de código que seria capturado pelo verificador de tipo estático do TypeScript ou Flow. Isso permitiu que eles estimassem que um limite inferior da porcentagem de bugs poderia ser corrigido usando a digitação estática.

Os pesquisadores tomaram precauções rigorosas para garantir que suas análises não considerassem um bug não relacionado ao tipo como sendo relacionado aos tipos. 2

Comparado a estudos anteriores, este novo estudo tem pontos fortes:

  • Há uma comparação direta entre a digitação estática e a digitação dinâmica, com poucos (se houver) fatores de confusão, pois a única diferença entre JavaScript e TypeScript / Flow é a digitação.
  • Eles executam a replicação em várias dimensões, verificando o TypeScript e o Flow (ou seja, sistemas de tipos diferentes) e fazendo com que pessoas diferentes reproduzam a anotação de tipo (manual) para corrigir os erros. E eles fazem isso em um grande número de bases de código de diferentes projetos.
  • O artigo mede o impacto direto da digitação estática em bugs corrigíveis (em vez de uma qualidade mais vaga).
  • Os autores definem um modelo rigoroso do que medir e como, antecipadamente. Além disso, sua descrição é incrivelmente clara e facilita a análise de falhas (sempre é bom quando um trabalho de pesquisa se abre para ataques: se nenhum ataque consegue minar seus argumentos, ele fica ainda mais forte). 3
  • Eles realizam uma análise de potência adequada para que o tamanho da amostra seja suficiente e a análise estatística subsequente seja hermética.
  • Eles são excessivamente conservadores para excluir explicações confusas e medem apenas uma única parte móvel. Além disso, eles restringem sua análise a erros corrigidos imediatamente pela inclusão de tipos e excluem qualquer coisa que possa exigir refatoração mais avançada para acomodar a digitação. Então, na realidade, o efeito é plausivelmente muito maior, mas certamente não menor do que o que eles relataram.
  • E, finalmente, eles não encontram um efeito leve, mas uma diferença impressionante . Apesar de seu procedimento excessivamente conservador, mesmo no nível mais baixo do intervalo de confiança de 95%, eles descobrem que há pelo menos 10% de erros que simplesmente desapareceriam com o mínimo de verificação de tipo adicionado.

A menos que exista uma falha fundamental no artigo que ninguém ainda tenha descoberto, o artigo mostra conclusivamente um grande benefício da digitação estática, quase sem nenhum custo. 4


Em uma nota histórica, a pesquisa sobre a digitação de disciplinas em programação teve um início difícil, porque, por um longo tempo, as evidências não eram claras. A razão para isso é que não é fácil realizar experimentos sistemáticos para examinar o efeito da tipagem estática versus dinâmica: um experimento sistemático deve isolar o efeito que estamos investigando. E, infelizmente, não podemos isolar o efeito da disciplina de digitação, pois ela está ligada às linguagens de programação.

Na verdade, havia linguagens de programação que permitiam digitar estática e dinâmica em diferentes dialetos (por exemplo, VB com Option Strict Onou Off, ou Lisp tipicamente estatístico). No entanto, eles não eram adequados para uma comparação direta, o mais importante porque não havia bases de código suficientemente grandes e existentes que permitissem comparação direta. Na melhor das hipóteses, poderíamos compará-los em "ambientes de laboratório", onde os participantes do teste resolvem aleatoriamente uma tarefa na variante do idioma com estatica ou dinâmica.

Infelizmente, essas atribuições de programação artificial não modelam bem o uso no mundo real. Em particular, muitos deles têm escopo pequeno e resolvem um problema bem definido que pode ser resumido em meia página de texto.

Felizmente isso aconteceu no passado, porque TypeScript, Flow e JavaScript são realmente as mesmas linguagens, exceto para a digitação estática, e porque há um extenso conjunto de dados do mundo real de código e bugs para amostragem.


1 Inspirado por uma citação do papel original.

2 Não estou inteiramente satisfeito com isso: um dos principais pontos fortes das linguagens estaticamente tipadas é que problemas ostensivamente não relacionados ao tipo podem ser expressos de maneiras que podem ser verificadas estaticamente. Isso transforma muitos erros lógicos em erros de tipo, o que aumenta drasticamente a taxa de erros que podem ser detectados pela digitação estática. De fato, o artigo classifica aproximadamente bugs não relacionados a tipos e eu afirmo que uma grande porcentagem deles pode de fato ser capturada pela digitação estática.

3 Convido qualquer pessoa, especialmente os defensores da tipagem dinâmica, a tentar encontrar falhas não corrigidas na análise. Não acho que haja muitos (se houver), e estou confiante de que nenhuma falha em potencial alteraria materialmente o resultado.

4 Suspeito que o custo real da digitação estática em projetos reais de grande escala seja inexistente, pois ele se torna uma parte natural da arquitetura e pode até simplificar o planejamento. A correção de erros do tipo estático leva tempo, mas muito menos que os erros descobertos posteriormente. Isso foi extensivamente estudado empiricamente e é conhecido há décadas (ver, por exemplo, Código Completo ).


3
Sei que essa é uma resposta tardia a essa pergunta, mas acredito que a nova evidência (que explico aqui) altera o debate estático versus dinâmico completo.
Konrad Rudolph

2
Certamente é interessante, mas eu me pergunto o quanto isso se relaciona com o sistema de tipos particular do javascript. O sistema do tipo Python (especialmente python3) é muito mais rígido, com muito menos conversões implícitas.
Peter Green

@ PeterGreen Sim, isso sem dúvida é verdade. Talvez tenhamos sorte e as dicas de tipo do Python levem a uma análise semelhante no futuro (embora eu duvide, pois o objetivo expresso em PEP484 e PEP526 é não implementar a digitação estática).
Konrad Rudolph

11
Apenas lendo o resumo, já posso dizer que a metodologia é fundamentalmente falha. Você não pode usar uma base de código escrita usando uma disciplina e simplesmente adicionar tipos para justificar argumentos em uma disciplina totalmente diferente. O código escrito como uma disciplina estática parece fundamentalmente muito diferente da disciplina dinâmica; você não deve escrever Java em Python, assim como não deve escrever Python em Java. Mesmo datilografado e javascript são linguagem fundamentalmente diferente, apesar das semelhanças superficiais.
Lie Ryan

2
@LieRyan Se algo que torna a análise excessivamente conservadora, como observado na minha descrição e em outros lugares. Isso não invalida a análise. Sua estimativa de 1% é, honestamente, risível. Está completamente errado, sua intuição está decepcionando você. Da mesma forma, sua caracterização de problemas com a digitação estática é típica de um praticante de digitação dinâmica que tinha pouca experiência real com a digitação estática moderna (ou seja, não apenas Java).
Konrad Rudolph

12

A segurança do tipo vale a pena para acelerar o desenvolvimento e a flexibilidade?

Então, na verdade, isso se resume ao que você está fazendo. Se você está programando, digamos, os sistemas de backup para aviões, o tipo de segurança é provavelmente o caminho a percorrer.

Linguagem dinâmica versus programação de linguagem estática são realmente dois animais diferentes. Ambos exigem uma abordagem fundamentalmente diferente um do outro. Na maioria das vezes, você pode portar um método de abordagem entre estático e dinâmico, mas perderá as vantagens do outro.

É realmente uma mentalidade. Um é melhor que o outro? Isso realmente depende de quem você é e como pensa. A maioria das pessoas com quem trabalho nunca tocaria em um idioma dinâmico se não precisasse, porque sente que há muito espaço para erro. Eles estão errados ao pensar isso? Não, é claro que não, mas significa que eles perceberam que sua abordagem de aplicar seu estilo de codificação não funcionará em um ambiente dinâmico. Outras pessoas com quem vou a grupos de usuários são exatamente o oposto. Eles acham a digitação estática muito complicada, porque limita sua abordagem à solução de certos tipos de problemas.

Posso dizer honestamente, eu pulo muito entre JavaScript e C #. Agora, conhecer e trabalhar em ambos os idiomas influencia o outro até certo ponto, mas, na verdade, o código que escrevo em cada um parece totalmente diferente do outro. Eles exigem uma abordagem diferente, porque são fundamentalmente diferentes. O que eu descobri é que, se você está pensando: "Cara, isso é muito mais difícil de fazer isso na linguagem X", sua abordagem provavelmente está um pouco errada. Aqui está um exemplo, as pessoas falam sobre a maneira "Pythonic" de fazer as coisas. O que isso significa é que existe uma maneira pela qual a linguagem Python funciona para facilitar um problema. Fazer isso de outra maneira é geralmente mais difícil e mais complicado. Você precisa superar o problema de saber como um idioma funciona para realmente funcionar para você. Isto'


Há algum tempo, tenho a impressão de que as linguagens de programação devem ocultar apenas os recursos do seu código nos quais você nem precisa pensar. Isso vale para obter código de máquina até algo de nível superior como Java, porque esse nível mais baixo de implementação é algo com o qual você basicamente nunca precisa lidar. Este não é o caso para tipos de objetos. Na minha opinião, a digitação dinâmica apenas torna a programação mais difícil, porque apresenta toda uma classe de erros que você precisa detectar.
MCllorf

7

Houve uma pergunta semelhante que acabou de ser feita recentemente: Linguagem dinâmica versus linguagem de tipo estático para websites

Para reafirmar o núcleo da minha resposta:

À medida que os sistemas crescem, as linguagens estaticamente garantidas garantem robustez no nível do componente e, portanto, flexibilidade no nível do sistema.

Sim, o Java é estritamente digitado e sim, o Java é péssimo (sem ofensa. É horrível. Ótima plataforma e ecossistema, mas uma das piores linguagens de todos os tempos (atualmente sendo usada)).
Mas deduzindo disso, que digitar estritamente é uma merda é apenas uma falácia. É como apontar para PHP e inferir que a digitação dinâmica é uma porcaria (mais uma vez, sem ofensas. Está melhorando lentamente, digo isso).

Pessoalmente, faço a maior parte do meu desenvolvimento no haXe , que possui um sistema de tipo estático. Não é apenas significativamente mais expressivo do que o Java e exige muito menos esforço devido à inferência de tipo, mas também é opcional. Se alguma vez entrar no seu caminho, você simplesmente ignora.

A segurança de tipo é um recurso (isso é algo que muitas linguagens supostamente de alto nível não dão certo) para ajudá-lo a evitar um tiro no pé .
E sobre qualquer linguagem de tipo dinamicamente bem-sucedida seria simplesmente melhor, se você tivesse a opção de verificar seu tipo de código à vontade.
Por exemplo, eu certamente gostei de experimentar o Ruby, mas isso ocorreu principalmente porque o Ruby é totalmente orientado a objetos, o que é totalmente ortogonal à presença de um sistema do tipo tempo de compilação.

Penso que a afirmação de que os sistemas de tipo estático são obstrutivos baseia-se apenas na falta de conhecimento de bons sistemas de tipo estático. Há várias linguagens que fazem o certo, sendo uma delas e sem dúvida nem a melhor a esse respeito.

Exemplo de código 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());

Isso produzirá um erro de tempo de compilação:

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'

Você não pode realmente afirmar que eu tive que me esforçar muito na segurança de tipos.

Dizer que você não precisa de segurança de tipo, porque você tem testes é ainda mais idiota. Escrever testes é chato e repetitivo. E eu realmente não quero escrever um teste, apenas para descobrir, que uma instância de Car não é um grasnado e um Duck precisa de alguém para grasnar.

No final do dia, você encontrará, não importa o quanto a segurança do tipo sobrecarga lhe custou, ela será amortizada (mesmo em Java - embora talvez não tão cedo).


No python, os doctests são apenas copiados e colados a partir do repl / shell, como uma fonte de documentação e verificação posterior. docs.python.org/3/library/doctest.html
aoeu256

5

Por qualquer motivo, não cometo mais erros relacionados ao tipo de objeto. Em linguagens como C #, é mais provável que eu cometa erros relacionados a conversões de tempo de execução do que cometa um erro de segurança detectável pelo compilador, o que, admito, geralmente é causado pela necessidade ocasional de contornar a estática de uma estatística. idioma digitado. Quando escrevo ruby, o código tende a sugerir fortemente o tipo de um objeto e a disponibilidade de um REPL significa que eu já verifiquei experimentalmente que o método / atributos desejados existem, ou terei um teste de unidade que faça isso. basicamente a mesma coisa, então eu também raramente encontro problemas de segurança de tipo em ruby.

Mas isso não quer dizer que os sistemas estaticamente tipificados não podem ser melhores do que são.

Nas linguagens de tipo estaticamente, o sistema de tipos também importa muito. Por exemplo, com algo como a mônada Some em linguagens funcionais (tipo <Alguns>: = yes x | no), você obtém verificações em tempo de compilação que essencialmente impedem a temida NullReferenceException comum na maioria dos sistemas de tipos; quando o código de correspondência de padrões é executado, você recebe erros de tempo de compilação informando que não conseguiu lidar com a condição nula (se você usar esse mecanismo para declarar o tipo). Você também reduz tipos semelhantes de erros quando usa coisas como o operador de pipeline |> em F #.

Na tradição Hindley-Milner da tipagem estática, você pode criar coisas que lhe dão muito mais do que uma garantia de que um tipo alega suporte à interface X e, uma vez que você tenha essas coisas, eu diria que o sistema estaticamente tipificado se torna muito mais valioso.

Quando isso não é uma opção, as extensões Design By Contract para C # podem adicionar outro conjunto de mecanismos que aumentam o valor do sistema de tipo estático, mas ainda exigem mais disciplina do que alguns desses paradigmas funcionais.


5

Depende.

Os modos de falha humana são frequentemente estatísticos. A verificação de tipo forte reduz a probabilidade de alguns tipos de falhas humanas (causando código de buggy). Mas só porque você pode falhar nem sempre significa que você vai (Murphy não suporta).

Se essa redução nas chances de falha em potencial vale o custo depende.

Se você estiver escrevendo um código para uma usina nuclear ou sistema ATC, qualquer redução no modo de falha humana pode ser extremamente importante. Se você cria uma prototipagem rápida de uma ideia de site sem especificação e com consequências de falha quase nulas, a redução nos modos ou probabilidades de falha pode ou não comprar nada para você, mas pode custar em tempo de desenvolvimento (mais pressionamentos de tecla etc.), e nas células do cérebro distraídas memorizando o (s) tipo (s) atual (is) necessário (s).


3
Seu cenário de prototipagem rápida parece errado no artigo de Paul Hudak sobre um estudo da Marinha dos EUA que exigia o desenvolvimento de uma simulação semelhante ao AEGIS em diferentes idiomas, um dos quais era Haskell. Ele atende a quase todos os seus critérios: tratava-se de prototipagem rápida, os requisitos mal definidos e o custo da falha era quase zero (esse é um experimento extremamente informal). Haskell foi o vencedor na categoria evey: tempo de desenvolvimento, excedendo os requisitos, exigindo menos LOC e produzindo o único exemplo de trabalho entre todos os concorrentes!
Andres F.

2
O artigo: Haskell vs ..., Uma Experiência em Produtividade de Prototipagem de Software - Paul Hudak e Mark P. Jones . Ele descreve os resultados de um experimento encomendado pelo ARPA e pela Marinha dos EUA.
Andres F.

O vencedor não foi o Relational Lisp? Cara, eu gostaria que houvesse vídeos mostrando pessoas codificando coisas no Lisp com todas essas extensões poderosas e estranhas como o Shen (uma estrutura lógico-relacional que permite atribuir tipos dependentes ao código e misturar e combinar código do tipo com código não-tipo ), estruturas super-CLOS com envio predicado, etc ...
aoeu256 28/09

4

Existem muitos sistemas muito complicados escritos no Lisp, e eu não ouvi nenhum Lisper reclamando que eles queriam digitar estática. Quando trabalhei com ele, não me lembro de nenhum problema que me atrasasse muito que um sistema de tipo estático (e você pode especificar tipos estaticamente no Common Lisp) teria detectado.

Além disso, as principais linguagens estaticamente tipadas não parecem ser adequadas para detectar erros. Na concepção de um layout, o que é importante é que um certo número é uma medida vertical na página, não se é int, unsigned, float, ou double. O compilador, por outro lado, geralmente sinaliza as conversões de tipo que considera inseguras e, felizmente, deixe-me adicionar uma medida vertical e o número de caracteres em uma string. Essa fraqueza do sistema de tipos estáticos era a idéia original por trás da notação húngara de Simonyi, antes de ser bastardizada pela inutilidade feia.


4

Os tipos são restrições nas interfaces; portanto, são um subconjunto do que você pode querer testar com testes de unidade; portanto, muitas das compensações são semelhantes:

  • Os tipos estáticos fornecem feedback anterior sobre se o código atende ou não aos requisitos que podem ser expressos pelo sistema de tipos, em troca de atrasar o feedback ao criar algo minimamente funcional (como feedback do cliente ou testes de nível superior).
  • Saber que o código atende a certos requisitos pode facilitar a refatoração e a depuração, mas também adiciona uma sobrecarga às interfaces e requisitos de alteração.
  • Particularmente, se uma linguagem de tipo estaticamente não possui coerção, ela fornece segurança adicional contra códigos usados ​​em dados que causariam bugs (reduzindo a necessidade de condicionais e asserções), mas restrições excessivamente restritivas exigem que o usuário escreva mais código para massagear seus dados em um forma aceitável (como conversão explícita de tipo).
  • As anotações explícitas de tipo podem ajudar no entendimento da leitura de código ou podem desorganizar o código com informações redundantes ou desnecessárias.
  • Dependendo da implementação, isso pode prejudicar a concisão. Isso depende de coisas como se as anotações de tipo são necessárias ou inferidas, quão bem o sistema de tipos pode expressar tipos / interfaces genéricos, a sintaxe e se você pretende ou não testar restrições que podem ser expressas pelo sistema de tipos (ou seja, o o mesmo teste provavelmente é mais conciso como um recurso de idioma do que como um teste de unidade, mas você pode não ter a intenção de testá-lo).
  • Além disso (mas não relacionado ao TDD), os tipos estáticos podem ajudar na otimização do tempo de compilação, às custas de exigir que os tipos sejam verificados (e dedicar um tempo para verificá-los e realizar as otimizações), e uma melhor otimização pode ser feita se os dados estiverem restritos aos tipos que mapeiam bem para o hardware. Isso facilita o desenvolvimento do código com requisitos de desempenho, mas pode causar problemas para o código que não se encaixa bem nessas restrições (conforme o ponto 3).

Para resumir, eu argumentaria que linguagens dinâmicas são particularmente úteis para a criação de protótipos, enquanto que se você precisa ter certeza de que seu código está correto, deve favorecer um sistema de tipos forte.


3

Sim definitivamente. Uma coisa que você encontrará ao usar as linguagens fortemente tipadas e o Python (o Python é fortemente tipado) mais é que o código mais bem escrito nas linguagens dinâmicas tende a seguir muitas das mesmas convenções que o código fortemente tipado. A digitação dinâmica é muito útil para serialização e desserialização, mas para a maioria das outras coisas, ela realmente não contribui muito. E, a menos que a maior parte do seu código esteja relacionada à serialização, por que lançar a verificação de erros gratuita?


4
Linguagens fortemente tipadas, como Java e C #, lidam com a desserialização automaticamente através do uso do Reflection.
Matthieu M.

3

Morgan, tive uma ideia interessante para você tentar: digitação estática + dinâmica. Você mencionou Python, C # e Java. Você sabia que existem algumas boas portas do Python para .NET e Java? Nos dois casos, as portas permitem usar as bibliotecas dessas plataformas e / ou interoperar com o código existente. Isso oferece várias possibilidades:

  1. Mantenha o código legado em linguagem estática e inflexível. Use Python para novidades.
  2. Use o Python para criar protótipos de novas coisas sobre plataformas maduras. Codifique novamente os componentes que você deseja manter na linguagem mais madura.
  3. Use o idioma dinâmico para partes que você muda com frequência.
  4. Possivelmente use a linguagem dinâmica para brincar com idéias como modificar o código em execução.
  5. Faça tudo na linguagem dinâmica, exceto nas partes críticas em que você usa a linguagem fortemente tipada.

Eu usei essas abordagens desde o final dos anos 90 para contornar a dor do desenvolvimento em C / C ++. Eu precisava das bibliotecas nativas e, às vezes, de desempenho. No entanto, eu queria a melhor sintaxe, flexibilidade, segurança etc. Então, o truque foi combiná-los cuidadosamente para obter as vantagens e desvantagens. Muitas vezes, era melhor na prática do que jogar fora todo o idioma e o código legado para outro idioma / plataforma.

(Nota: Uma resposta já disse isso, mas também quero enfatizar novamente a digitação dinâmica! = Não / digitação fraca. Muitos sistemas de tipos dinâmicos usam digitação forte por dentro. A maneira como penso sobre o que faz uma dinâmica de tipo é: um tipo de variável é determinado no tempo de execução, não precisa de uma anotação de tipo e / ou pode ser alterado no tempo de execução.


2

Você não receberá uma resposta realmente objetiva, mas minha experiência é que a segurança de tipo é inestimável até que você domine o TDD. Depois de ter uma cobertura pesada de teste de unidade, onde os testes foram escritos antes do código, a verificação do compilador se torna um problema e, na verdade, começa a atrapalhar.


este é um controle de qualidade subjetivo, então estou bem com isso.
Morgan Herlocker 18/03/11

11
Alguém quer explicar os votos negativos?
Pd #

Não posso ajudá-lo com a explicação, mas dei um +1, acho que essa é uma contribuição útil. Um dos principais temores da digitação dinâmica é que você fará uma alteração em algum lugar e quebrará em algum outro lugar devido a suposições que seriam aplicadas pelo compilador em um idioma estaticamente tipado. Cobertura de teste de unidade pesada irá protegê-lo aqui.
Carson63000

5
Eu não diminuí a votação porque você fez um argumento válido, embora nenhuma ofensa tenha sido intencional, mas seu post parece um pouco de fanático por TDD, e é provavelmente por isso que as votações negativas.
Karl Bielefeldt

@ Karl, nenhuma ofensa, foi uma pergunta genuína. Eu posso ser unapologetically pró-TDD, eu admito
pdr

2

Eu vejo essa pergunta surgir muito, e acho que a qualidade do seu software (e a falta de bugs) tem mais a ver com seu processo de desenvolvimento, como seu sistema é arquitetado e o compromisso de você e seus colegas em codificar a qualidade.

Meu último trabalho foi principalmente o desenvolvimento de python. Trabalhei para uma grande empresa internacional de hospedagem na web e tínhamos equipes de desenvolvimento nos EUA, Canadá e Coréia do Sul. Estrutura da web python personalizada para aplicativo de cliente front-end que permitia aos usuários gerenciar seus nomes de domínio e contas de hospedagem na web. Back-end: todos python também. Serviço web Python para conversar com servidores individuais para fazer coisas como provisionar um novo site de hospedagem, criar um novo blog, criar entradas de DNS em nosso sistema de serviço de nomes; etc, etc. No meu trabalho atual, aplicativos clientes são tudo em java; nosso principal produto é uma mistura de java e flash. Estrutura da web em java personalizada para nossos aplicativos mais antigos, wicket para nossas novas ferramentas internas.

Tendo trabalhado em ambos, devo dizer que essa pergunta me incomoda toda vez que a vejo. Se você estiver usando uma linguagem de tipo dinâmico e realmente testar seu código, ficará bem. Se o sistema for bem projetado e você seguir os padrões, ficará bem. Nunca houve muitos bugs que surgiram devido à falta de tipos de verificação de um compilador. A maioria dos bugs eram erros lógicos, assim como meu trabalho em Java hoje.


2

A segurança do tipo vale a pena na velocidade de desenvolvimento e flexibilidade? PORQUE?

A digitação estática é um aumento líquido na velocidade e flexibilidade do desenvolvimento ao longo do ciclo de vida do software. Reduz o esforço total e a inconveniência, mas promove muito esforço e inconveniência antecipadamente, onde é mais perceptível. A barreira de entrada para ter código de trabalho é maior, mas depois que você ultrapassa essa barreira (satisfazendo o verificador de tipos), estender e manter esse código exige muito menos trabalho.

Sempre haverá algumas dores de cabeça no desenvolvimento de software devido a:

  • A complexidade inerente ao que você está tentando realizar

  • A falibilidade inerente aos seres humanos, especialmente considerando que cometemos mais erros quando tentamos fazer algo mais complexo

Mais cedo ou mais tarde, você precisa de um tempo para enfrentar esses desafios. Não há como evitar isso. A digitação estática simplesmente resolve esses desafios mais cedo ou mais tarde. Mais cedo é melhor que mais tarde, porque quanto mais tarde você descobrir um erro (não se trata de se , mas quando ), mais custa para corrigir esse erro.

Custa muito menos corrigir um erro relatado por um verificador de tipos do que custa depurar uma exceção relacionada ao tipo gerada em tempo de execução. Adiar a verificação de tipo para o tempo de execução está apenas varrendo o problema para baixo do tapete.


1

Esta é apenas a minha opinião, mas não, não acho que esse tipo de segurança valha a pena. Nem mesmo por um segundo.

Sou desenvolvedor há muito tempo. Começando com c ++, c # e depois movido para javascript (front-end e back-end via node.js). Desde que desenvolvi em javascript, minha produtividade disparou rapidamente, a ponto de realmente me agravar usando linguagens baseadas em tipos. Também sou contra a compilação, quero que tudo esteja em tempo de execução agora. Linguagens interpretadas é realmente onde encontrei meu amor pela programação.

Quanto aos tipos, simplesmente não vejo nenhum benefício. Agora vejo tipos da mesma maneira que vejo gerenciamento de memória. Completamente desnecessário. Os idiomas de amanhã devem proteger completamente o desenvolvedor de saber qualquer coisa sobre tipos. O computador deve entender os tipos e deixar o desenvolvedor fora dele.

Aqui está um exemplo. Eu estava usando o Swift (o novo idioma da Apple), esperando que ele cumprisse seu nome um dia atrás e tentasse: var n = 1/2 não funcionou. Eu estava tipo, o que está acontecendo aqui. e então, infelizmente, percebi que eu tinha que fazer var n: Float = 1/2. Isso me lembrou o quanto eu odeio sistemas de tipos e o quanto de um agravamento desnecessário eles são.

Eu iria até mais longe para dizer que nem quero tipos definidos pelo usuário (como Classes). Não quero tipos de jeito nenhum. Tudo o que eu quero é var e objetos. Onde qualquer objeto pode ser usado como qualquer objeto. E os objetos são dinâmicos e mudam constantemente. Onde se torna um problema de tempo de execução sobre o que funciona e o que não funciona.

Os desenvolvedores adoram dizer como as linguagens de tipo fraco não são boas para grandes projetos. Mas eu diria que é o contrário. Linguagens fortemente tipadas são horríveis para grandes projetos. E se você disser que o javascript não funciona em grandes projetos, peça à Uber uma empresa de mais de 40 bilhões de dólares que executa todo o seu back-end no node.js / javascript ou no Facebook que começou com PHP.

No que diz respeito aos idiomas de tipo estaticamente, não é bom para as iterações rápidas de hoje. Aqui está um exemplo simples: você tem 10 desenvolvedores trabalhando em um projeto .net com um servidor de integração contínua, um desenvolvedor envia um erro e toda a compilação está corrompida, mesmo que os 10 desenvolvedores estejam trabalhando em coisas diferentes, agora eles estão parados e aguardando para o desenvolvedor infrator corrigir seu erro. Falar sobre eficiente né? As linguagens estáticas do sistema / tipo são interdependentes dessa maneira e tornam seu código interdependente. No entanto, os arquivos de script nunca são interdependentes. Se houver um problema com um dos scripts, ele não interrompe a produção, todos os problemas que você vê são deixados para o tempo de execução. E o tempo de execução nunca para. Isso nunca quebra. Pode produzir uma saída errada, mas não


11
Muitos "eu", não muita substância do argumento. E, a propósito, se um erro "quebra" ou não, a compilação não tem nada a ver com estática versus dinâmica. Se você tiver testes de unidade e um falhar "a sua construção está quebrado" e esperemos que não implantar a produção até que seja corrigido
nafg

O que fez você pensar que eu implicava algo assim?
nafg

Sua produtividade em javascript não disparou porque faltavam tipos. Sua produtividade disparou porque C ++ e C # são linguagens pesadas. Os tipos Javascript + realmente aumentam ainda mais sua produtividade. Ninguém disse que o javascript é impossível para grandes projetos. Javascript em grandes projetos certamente é capaz. No entanto, não é o ideal. Os testes de unidade substituem a verificação de tipo, também os testes de unidade têm cobertura de tipo limitada, enquanto a verificação de tipo tem 100% de cobertura.
Brian Yeh

11
@BrianYeh c ++ e c # são linguagens pesadas porque estão centradas em tipos. Comecei a usar reactjs no meu trabalho e minha produtividade caiu mais uma vez devido ao uso incessante em tipos e componentes. se você gosta de tipos e testes de unidade, é bom para você. nem todos nós compartilhamos esse estilo de programação.

11
@foreyez reactjs não tem tipos. Você provavelmente está se referindo ao fluxo. Os testes de unidade substituem a verificação de tipo; portanto, se você não a tiver, precisará de mais testes de unidade. Testes e tipos de unidade são forças opostas. Sua produtividade diminuindo é uma ilusão. Qualquer erro de tipo detectado em um idioma seguro de tipo é um erro não detectado em um idioma digitado dinamicamente. Parece apenas mais rápido. O tipo de linguagem segura obriga a lidar com esses erros antecipadamente.
precisa saber é o seguinte

0

SIM.

Eu trabalhei em aplicativos PHP, onde os tipos não são tão "fortes" quanto em Java ou C #. Normalmente, eu terminava "simulando tipos" porque, para evitar conversões automáticas ruins ou validar dados.

Os idiomas de tipo dinâmico são bons para scripts do SO e aplicativos pequenos e rápidos., Não aplicativos complexos.

Resumo: Se eu tiver que escolher entre uma linguagem de programação "Tipo fraco" ou "Tipo dinâmico" ou uma Linguagem de programação "Tipo forte" para um aplicativo comercial complexo, escolho a Linguagem de programação "Tipo forte" .


0

Eu acho que vale a pena dar um passo atrás e considerar quando a digitação dinâmica causa problemas.

Um caso é o local em que uma ramificação de código não é testada, mas, francamente, o código que nunca é testado provavelmente será buggy, independentemente de a digitação dinâmica estar em uso ou não.

Outro problema mais sutil, porém, é a substituibilidade imperfeita.

Se um tipo estiver completamente errado, a menos que nunca seja usado um caminho de código específico que possa ser detectado rapidamente.

Por outro lado, se um tipo é um substituto imperfeito, o código pode funcionar principalmente, mas é interrompido de maneiras sutis que não serão detectadas até muito mais tarde.

Dois dos tipos mais comuns de programação são números e seqüências de caracteres. Em muitos idiomas dinâmicos, eles são substitutos imperfeitos um do outro. Por exemplo, javascript ou php, se você fornecer um número em que uma string é esperada ou vice-versa, seu programa é executado sem gerar um erro, mas pode se comportar de maneira bastante sutil.

O Python evitou esse problema específico, números e seqüências de caracteres não substituem um ao outro, e tentar usar um onde o outro é esperado normalmente levará a uma falha rápida.

No entanto, não evitou completamente o problema imperfeito de sustentabilidade. Diferentes tipos de número podem ser substitutos imperfeitos um do outro, assim como diferentes tipos de sequências.


O que estou chegando aqui é que não acho possível comparar os benefícios e custos da digitação estática e dinâmica de maneira genérica, porque acho que os benefícios e os custos dependem da variação específica da digitação estática ou dinâmica de uma linguagem. usos.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.