Sei que essa é uma questão muito ampla, ambígua e possivelmente filosófica. Até certo ponto, a palavra-chave mais importante da pergunta - sistema de tipos "forte" - em si está mal definida . Então, deixe-me tentar explicar o que quero dizer.
Contexto geral da pergunta
Criamos um aplicativo da web em escala muito grande no Ruby on Rails e geralmente ficamos felizes com nossa pilha. Quando queremos, podemos enviar coisas muito rapidamente - algo que funciona para 90% dos casos "comerciais", sem nos preocupar muito com 10% dos casos extremos. Por outro lado, com a ajuda de análises de código e cobertura de testes, podemos ser lentos e deliberados e garantir que cobrimos todas as bases - novamente, apenas em situações que merecem um exame e segurança mais próximos.
No entanto, à medida que a equipe cresce, comecei a me sentir desconfortável com a falta de uma "rede de segurança" inserida em nossa pilha.
Recentemente, começamos a desenvolver algum desenvolvimento nativo do Android em Java. E eu (agradavelmente) me lembrei da segurança fornecida por uma linguagem compilada / estática / fortemente tipada.
- Variáveis com erros de ortografia, tipos de dados incorretos, invocações de funções incorretas e uma série de erros triviais são detectados pelo próprio IDE. Tudo porque o IDE pode se conectar ao compilador e verificar certos aspectos da "correção" do programa.
- Precisa alterar uma assinatura de função? Fácil. O compilador + IDE pode ajudá-lo a identificar TODOS os sites de chamada.
- Precisa garantir que certas exceções sejam sempre tratadas? Exceções marcadas para o seu resgate.
Agora, embora esses recursos de segurança tenham suas vantagens, também estou ciente de suas desvantagens. Mais ainda, no mundo do Java "pesado". Portanto, em vez de Java, comecei a analisar as inúmeras linguagens modernas "fortemente tipadas" nas quais as pessoas começaram a trabalhar atualmente. Por exemplo: Scala, Rust, Haskell, etc. O que mais me interessa é o poder de seus sistemas de tipos e verificações estáticas / em tempo de compilação.
Agora, a pergunta
Como uso esses poderosos sistemas de tipos e recursos estáticos / em tempo de compilação em aplicativos maiores?
Por exemplo, como eu iria além do tipo de introdução padrão "olá mundo" a esses recursos poderosos? Um que usa um sistema de tipo avançado para modelar um problema no domínio comercial? O sistema de tipos ajuda ou atrapalha quando você está na zona de 30.000 LOC +? O que acontece com a rede de segurança fornecida por esses sistemas de tipo (e verificações em tempo de compilação) quando seu sistema interage com o mundo externo pouco digitado, por exemplo. via APIs JSON ou XML, vários armazenamentos de dados, entrada do usuário etc.
Respostas:
Vou dar uma resposta breve devido à minha falta de tempo no momento, mas atualmente estou trabalhando em dois grandes projetos (> 100.000 LOC em Haskell) - flowbox.io e luna-lang.org. Usamos Haskell para todas as partes, incluindo o back-end, o compilador de nossa linguagem de programação e até a GUI baseada em webGL. Devo admitir que o sistema de tipos robusto e o mecanismo semelhante ao "tipo dependente" podem guiá-lo e salvá-lo do fardo e dos problemas conhecidos de outras línguas. Usamos os tipos muito extensivamente e tudo o que pode ser verificado em tempo de compilação é feito. De fato, durante os últimos 3 anos de desenvolvimento, nunca encontramos nenhum erro de tempo de execução ou excesso de pilha (e isso é algo realmente incrível). Os únicos erros são erros lógicos óbvios cometidos pelos programadores. Muitas pessoas dizem que, se algo compilar em Haskell, simplesmente funcionará e você deve ter certeza de que não vai explodir em seu rosto algum dia.
Respondendo à primeira parte da pergunta: Você pode aprender sobre esses recursos poderosos do sistema de tipos lendo alguns ótimos blogs, como:
De fato, existem muitos outros blogs legais por aí (como o planeta Haskell ). De qualquer forma, o melhor método para realmente entender os sistemas de tipos avançados é desenvolver uma biblioteca de código aberto útil. Nós (na Flowbox & New Byte Order) estamos lançando muitas bibliotecas (você pode encontrá-las no Hackage); portanto, se você não tem uma idéia do que desenvolver, sempre pode se envolver em nossos projetos - envie-me um e-mail sempre que desejar want (correio disponível em luna-lang.org ).
fonte
Bem, digitação fraca versus forte é vagamente definida. Além disso, como o mais próximo de um uso geral de 'tipagem forte' é referir coisas que dificultam a conversão de tipos, isso não deixa mais nada para descrever sistemas de tipos ainda mais fortes. É como dizer que se você pode carregar menos de 30 libras, você é fraco e todos que conseguem levantar mais estão na mesma categoria de 'forte' - uma distinção enganosa.
Então, eu prefiro a definição:
O que quero dizer com fazer coisas para você? Bem, vamos examinar a criação de uma API de conversão de imagem na estrutura Servant (em Haskell, mas você realmente não precisa saber disso para acompanhar, verá ...)
Isso significa que queremos alguns módulos, incluindo o pacote Servant e o plug-in JuicyPixels para o Servant, e que o principal ponto de entrada do programa é executar a função 'conversion' na porta 8001 como servidor usando o back-end Warp. Ignore o bit do idioma.
Isso significa que a função de conversão é um servidor em que a API deve corresponder ao tipo 'ConversionApi' e as solicitações são tratadas pela função
handler
Isso está especificando o
ConvesionApi
tipo. Ele diz que devemos aceitar os tipos de conteúdo recebidos especificados pela lista '[BMP, GIF, JPEG 50, PNG, TIFF, RADIANCE] e manipulá-los como uma DynamicImage, e que devemos retornar uma DynamicImage convertida no mesmo intervalo de conteúdo tipos. Não se preocupe exatamente com o que:> significa, pense nisso como mágica feliz por enquanto.Portanto, dada a minha definição preferida, um sistema de tipo fraco agora pode garantir coisas como:
Todos os objetivos elevados, mas não o suficiente para se qualificar como um sistema fortemente tipado, dada a definição acima. E agora temos que chegar à parte mais difícil de escrever código que segue essa especificação. Em um sistema de tipos realmente forte , escrevemos:
E então terminamos. É isso aí , não há mais código para escrever . Este é um servidor da web totalmente operacional (módulo que ocorreu algum erro de digitação). O tipo disse ao compilador tudo o que é necessário para criar nosso servidor Web a partir dos tipos e pacotes (módulos tecnicamente) que definimos e importamos.
Então, como você aprende a fazer isso na maior escala de aplicativos? Bem, realmente não é muito diferente de usá-los em aplicativos de menor escala. Tipos absolutos não se importam com a quantidade de código gravado relacionado a eles.
A inspeção do tipo de tempo de execução é algo que você provavelmente desejará evitar, porque isso reduz uma enorme quantidade de benefícios e permite que os tipos tornem seu projeto mais complicado de trabalhar, em vez de simplificar as coisas.
Como tal, é principalmente uma questão de prática modelar coisas com tipos. As duas principais maneiras de modelar as coisas (ou construir as coisas em geral) são de baixo para cima e de cima para baixo. A parte superior para baixo começa com o nível mais alto de operações e, à medida que você constrói o modelo, você tem partes nas quais está adiando a modelagem para mais tarde. A modelagem de baixo para cima significa que você começa com as operações básicas, assim como inicia com as funções básicas, depois cria modelos cada vez maiores até capturar completamente a operação do projeto. De baixo para cima é mais concreto e provavelmente mais rápido de construir, mas de cima para baixo pode informar melhor seus modelos de nível inferior sobre como eles realmente precisam se comportar.
Os tipos são como os programas se relacionam com a matemática, literalmente, então não há realmente um limite superior sobre o quão complicado eles podem ficar, ou um ponto em que você pode 'terminar' aprendendo sobre eles. Praticamente todos os recursos fora dos cursos universitários de nível superior são todos dedicados à forma como os tipos funcionam em algum idioma específico. Portanto, você precisa decidir isso também.
Da melhor forma que posso oferecer, os tipos podem ser estratificados da seguinte forma:
Geralmente, quanto mais abaixo você está nessa lista, mais os tipos podem fazer por você, mas, no fundo, você está subindo na estratosfera e o ar está ficando um pouco mais fino - o ecossistema de pacotes é muito menor e você ' você terá que escrever mais coisas contra ter encontrado uma biblioteca relevante. A barreira de entrada também aumenta à medida que você desce, pois você precisa realmente entender o sistema de tipos o suficiente para escrever programas em grande escala.
fonte
Proxy :: Proxy True
funciona, mas é melhor escrever comoProxy :: Proxy 'True
.'[xs]
sintaxe? Isso claramente não é umChar
, mas eu não vejo como querTypeOperators
ouDataKinds
permite uma sintaxe alternativa. É algum tipo de quasiquotagem?Comecei a trabalhar na equipe principal de uma grande plataforma escrita em Scala. Você pode ver aplicativos de código aberto bem-sucedidos, como Scalatra, Play ou Slick, para ver como eles lidam com algumas de suas perguntas mais detalhadas sobre interações com formatos de dados dinâmicos.
Uma das grandes vantagens que encontramos da forte digitação do Scala é a educação do usuário. A equipe principal pode tomar decisões e aplicá-las no sistema de tipos, portanto, quando outras equipes que estão muito menos familiarizadas com os princípios de design precisam interagir com o sistema, o compilador as corrige e a equipe principal não está constantemente corrigindo as coisas. solicitações de recebimento. Essa é uma enorme vantagem em um sistema grande.
Obviamente, nem todos os princípios de design podem ser aplicados em um sistema de tipos, mas quanto mais forte o seu sistema de tipos, mais princípios de design você pode aplicar no compilador.
Também podemos facilitar as coisas para os usuários. Frequentemente, eles estão apenas trabalhando com coleções regulares ou classes de caso, e estamos convertendo-o para JSON ou o que for automaticamente conforme necessário para o transporte de rede.
A digitação forte também ajuda a fazer diferenciações entre itens como entradas não higienizadas e higienizadas, o que pode ajudar na segurança.
A digitação forte também ajuda seus testes a se concentrarem mais no seu comportamento real , em vez de precisar de vários testes que apenas testam seus tipos. Isso torna os testes muito mais agradáveis, mais focados e, portanto, mais eficazes.
A principal desvantagem é a falta de familiaridade com a linguagem e o paradigma da linguagem, e isso é corrigível com o tempo. Além disso, achamos que vale a pena o esforço.
fonte
Embora não seja uma resposta direta (desde que eu não tenho trabalhado em +30.000 bases de código LOC em Haskell ainda :( ..), eu te imploro para verificar https://www.fpcomplete.com/business/resources/case-studies / que apresenta muitos estudos de caso de haskell em configurações reais do setor.
Outro bom artigo é o IMVU, que descreve a experiência de mudar para haskell - http://engineering.imvu.com/2014/03/24/what-its-like-to-use-haskell/ .
Da experiência pessoal em aplicações maiores, o sistema de tipos muito mais ajuda, especialmente se você tentar codificar tanto quanto você pode em tipos. O verdadeiro poder é realmente óbvio quando se trata de refatorar as coisas - significando manutenção e isso se torna uma tarefa muito menos preocupante.
Vou despejar alguns links para recursos que eu recomendo, pois você está fazendo muitas perguntas ao mesmo tempo:
Como observação final, o trato com o mundo exterior é feito de várias maneiras. Existem bibliotecas para garantir que as coisas do seu lado sejam seguras, como Aeson para JSON, Esqueleto para SQL e muito mais.
fonte
O que eu vi:
Trabalhei em alguns aplicativos Web Ruby grandes (Rails), em um aplicativo Web Haskell grande e em vários aplicativos menores. Com essa experiência, devo dizer que a vida trabalhando nos aplicativos Haskell é muito mais fácil do que no Rails em aspectos como manutenção e menor curva de aprendizado. Sou da opinião de que esses benefícios se devem ao sistema de tipos de Haskell e ao estilo de programação funcional. No entanto, diferentemente de muitos, acredito que a parte "estática" do sistema de tipos é apenas uma enorme conveniência, pois ainda há benefícios a serem obtidos ao usar contratos dinâmicos.
O que eu acredito
Existe um bom pacote chamado Contracts Ruby, que fornece alguns dos principais recursos que eu acho que ajudam os projetos Haskell a obter melhores características de manutenção. Contratos O Ruby executa suas verificações em tempo de execução, por isso é melhor quando emparelhado com alta convergência de teste, mas ainda fornece a mesma documentação em linha e expressão de intenção e significado como usar anotações de tipo em idiomas como Haskell.
Resposta à pergunta
Para responder às perguntas colocadas acima, há muitos lugares onde você pode se familiarizar com Haskell e outros idiomas com sistemas de tipos avançados. No entanto, e para ser perfeitamente honesto, embora essas fontes de documentação sejam excelentes por si mesmas, todas parecem um pouco abaixo do esperado quando comparadas à infinidade de documentação e conselhos práticos encontrados em Ruby, Python, Java e outras linguagens. De qualquer forma, o Mundo Real Haskell está envelhecendo, mas ainda é um bom recurso.
Teoria da categoria
Se você escolher Haskell, encontrará uma grande quantidade de literatura discutindo a teoria das categorias. A teoria da categoria IMHO é útil, mas não necessária. Dada a sua prevalência na comunidade Haskell, é fácil confundir os prós e contras dos tipos com os sentimentos sobre a praticidade da teoria das categorias. É útil lembrar que são duas coisas diferentes, ou seja, as implementações guiadas pela teoria das categorias podem ser feitas em linguagens dinamicamente tipadas, assim como em estáticas (módulo os benefícios que o sistema de tipos oferece). Os sistemas de tipos avançados em geral não estão vinculados à teoria das categorias e a teoria das categorias não está vinculada aos sistemas de tipos.
Mais sobre tipos
À medida que você aprende mais sobre programação com tipos e as técnicas nele contidas (o que acontece muito rapidamente porque é divertido), você deseja expressar mais com o sistema de tipos. Nesse caso, eu examinaria alguns dos recursos a seguir e me juntaria a mim para conscientizar os fornecedores de ferramentas de que queremos que ferramentas de qualidade industrial com esses recursos sejam empacotadas apenas em algo que exponha uma interface fácil de usar (como o Contracts Ruby):
Introdução à programação no ATS
Tipos de refinamento LiquidHaskell via SMT e abstração de predicado
Desenvolvimento orientado a tipos com Idris
fonte
Primeiro, sinto que há uma confusão nas respostas entre tipagem fraca versus forte e estática versus dinâmica. Vincular o OP fornecido claramente faz a distinção:
Por exemplo, C, C ++ e Java são digitados estaticamente, pois as variáveis são digitadas em tempo de compilação. No entanto, C e C ++ podem ser considerados fracamente digitados porque a linguagem permite ignorar restrições usando
void *
ponteiros e elencos. Mais sobre este tópico.Nessa distinção, a digitação forte só pode ser melhor. Quanto mais cedo a falha, melhor.
No entanto, ao escrever programas grandes, não acho que o sistema de tipos tenha um papel importante. O kernel do Linux possui dez milhões de LOC escritos em C e assembly e é considerado um programa muito estável, a quilômetros de distância das minhas 200 linhas Java que provavelmente estão cheias de falhas de segurança. Da mesma forma, embora as "linguagens de script" tipadas dinamicamente tenham uma má reputação quando se trata de escrever programas grandes, ocasionalmente há provas de que não são merecidas (como Python Django, mais de 70k de LOC)
Na minha opinião, é tudo sobre padrão de qualidade. A responsabilidade pela escalabilidade de aplicativos grandes só deve ser assumida pelos programadores e arquitetos e por sua vontade de tornar o aplicativo limpo, testado, bem documentado etc.
fonte
da resposta anterior https://www.fpcomplete.com/business/resources/case-studies/
é como qualquer outra língua para criar força
Usando tipos de dados abstratos, ou mais geralmente polimorfismo
Isso ajuda o tempo todo. o sistema de tipos ajuda a escrever o código, pois indica a forma do resultado que você deseja obter. Na verdade, a Agda escreve um código para você.
PS: não cometa o erro de ter um sistema de tipos e de escrever os tipos você mesmo, o que é idiota: o computador pode fazer isso por você.
É ótimo, pois conhecer a estrutura dos valores por meio de tipos significa que o computador pode deduzir uma maneira de escrever (des) serializador para você.
Se você realmente deseja saber sobre tipos e abstração, a melhor introdução é Sobre compreensão de tipos, abstração de dados e polimorfismo
É um artigo, não um estudo de caso com fotos bonitas, mas é esclarecedor
fonte
Como programador .net que trabalha muito em aplicativos da Web, vejo tanto o lado C # digitado quanto o Javascript não digitado.
Não tenho certeza de ter visto alguma literatura sobre as perguntas que você faz. Com uma linguagem digitada, você toma todas essas coisas como garantidas. Com um não digitado, você vê a definição dos tipos como sobrecarga desnecessária.
Pessoalmente, não acho que você possa negar que uma linguagem fortemente tipada ofereça os benefícios que você descreve a um custo muito baixo (comparado a escrever testes de unidade equivalentes). A interação com sistemas de tipo fraco geralmente envolve tipos genéricos, como matrizes ou dicionários de objetos, como DataReader, uso criativo de strings ou a nova classe dinâmica. Basicamente, tudo funciona, você apenas recebe um erro de tempo de execução em vez de tempo de compilação.
Se você deseja escrever um programa muito curto, talvez definindo uma função em algumas linhas para trabalhar com um aplicativo maior, simplesmente não tem espaço para declarar classes. Certamente este é o nicho de linguagens não tipadas como JS ocupa?
fonte