Tipos específicos ainda são necessários?

20

Uma coisa que me ocorreu no outro dia, são tipos específicos ainda necessários ou um legado que está nos impedindo. O que quero dizer é: realmente precisamos de short, int, long, bigint etc etc.

Eu entendo o raciocínio, variáveis ​​/ objetos são mantidos na memória, a memória precisa ser alocada e, portanto, precisamos saber quão grande pode ser uma variável. Mas, realmente, uma linguagem de programação moderna não deveria ser capaz de lidar com "tipos adaptativos", isto é, se algo é alocado apenas no intervalo curto, ele usa menos bytes e se algo é alocado repentinamente em um número muito grande, a memória é alocada de acordo com essa instância em particular.

Flutuação, reais e duplos são um pouco mais complicados, pois o tipo depende da precisão de que você precisa. No entanto, as strings devem ser capazes de ocupar menos memória em muitos casos (em .Net), onde ascii é usado principalmente, mas as strings buth sempre ocupam o dobro da memória devido à codificação unicode.

Um argumento para tipos específicos pode ser que isso faz parte da especificação, ou seja, por exemplo, uma variável não deve ser maior que um determinado valor, portanto, definimos isso como abreviação. Mas por que não ter restrições de tipo? Seria muito mais flexível e poderoso poder definir intervalos e valores permitidos em variáveis ​​(e propriedades).

Percebo o imenso problema em renovar a arquitetura de tipos, uma vez que é tão fortemente integrada ao hardware subjacente e coisas como serialização podem se tornar realmente complicadas. Mas, do ponto de vista da programação, deve ser ótimo, não?

Homde
fonte
6
PHP, Ruby, Perl e outros não exigem que você indique os tipos de variáveis. O ambiente descobre isso para você.
FrustratedWithFormsDesigner
7
As seqüências de caracteres Unicode não precisam ocupar memória adicional quando são usadas apenas para ASCII (UTF-8).
2
Mas há uma diferença entre os tipos de variantes e adaptativos IMO. As variantes não são digitadas, mas são digitadas quando atribuídas, enquanto os tipos adaptativos seriam digitados, mas de maneira mais vaga. (e eu gosto do conceito de restrições de tipo)
Homde
Isso me lembra este projeto: tom.lokhorst.eu/media/…
LennyProgrammers
4
E Ada? type hour is range 0 .. 23;
Mouviciel 2/02

Respostas:

12

Eu acredito totalmente que esse seja o caso. As restrições semânticas valem mais do que as restrições de implementação. Preocupar-se com o tamanho de algo é como se preocupar com a velocidade de algo quando a programação orientada a objetos estava surgindo.

Não substituiu a programação crítica de desempenho. Ele apenas tornou a programação crítica sem desempenho mais produtiva.

Mark Canlas
fonte
1
Confira os contratos de código no .NET 4.0.
Steven Jeuris
+1 Quando se trata de armazenamento / transmissão de dados (por exemplo, rede), as restrições são fundamentais para maximizar a eficiência do protocolo / implementação. Além disso, há muito a ganhar se as coleções digitadas estiverem disponíveis. Fora isso, é seguro supor que a eficiência pode ficar em segundo plano (especialmente se diminuir a possibilidade de erros semânticos).
Evan Solha
9

Tipos adaptativos significa lógica para fazer a adaptação, trabalho em tempo de execução para executar essa lógica (o modelo e o tempo de compilação exigiriam um tipo específico, a inferência de tipo é um caso especial em que você obtém o melhor dos dois mundos). Esse trabalho extra pode ser bom em ambientes onde o desempenho não é crítico e o sistema mantém um tamanho razoável. Em outros ambientes, não é o caso (os sistemas embarcados são um, onde você precisa usar tipos inteiros de 32 / 64bits para desempenho da CPU e tipos inteiros de 8 / 16bits para otimização de backup de memória estática).

Até mesmo linguagens de uso geral que oferecem suporte à ligação tardia (resolução de tipos em tempo de execução, como o VB6) tendem a promover a digitação forte agora (VB.NET), devido ao impacto no desempenho que costumava surgir quando a ligação tardia era abusada e porque muitas vezes termine com um código feio quando os tipos não forem explícitos ( Referência / Refatoração profissional no Visual Basic - Danijel Arsenovski ).

Matthieu
fonte
Por favor, defina "digitação automática".
@delnan: substituído auto-digitação com wich ligação tardia é o que eu quis dizer :)
Matthieu
Existem muitas linguagens de uso geral que resolvem tipos em tempo de execução, o Common Lisp para citar apenas uma. (Para fins de desempenho, você pode declarar tipos em Lisp comum, para que você possa fazê-lo apenas em seções de desempenho crítico.)
David Thornley
@ David Thornley: "reforçar" digitação forte pode ter sido muito forte, "promover" seria mais apropriado, atualizei minha resposta de acordo. Um idioma que permite escolher entre os dois tipos de encadernação, dependendo da situação, é certamente melhor do que ser forçado de uma maneira ou de outra. Especialmente quando não estiver fazendo programação de baixo nível e focando na lógica.
Matthieu
4

Simplicidade, memória e velocidade Quando declaro uma variável, a memória dessa variável é alocada em um bloco. Para dar suporte a uma variável que cresce dinamicamente, eu precisaria adicionar o conceito de memória não contígua a essa variável (isso ou reservar o maior bloco que a variável pode representar). A memória não contígua reduziria o desempenho na atribuição / recuperação. Alocar o maior possível seria um desperdício no cenário em que eu só preciso de um byte, mas o sistema reserva um longo.

Pense nas vantagens e desvantagens entre uma matriz e um vetor (ou lista vinculada). Com uma matriz, procurar uma posição específica é uma simples questão de obter a posição inicial e mudar o ponteiro da memória x espaços para localizar essa nova posição na memória. Pense em um int como um pouco [32], lendo um int envolve percorrer esse array para obter todos os valores de bits.

Para criar um tipo de número dinâmico, você deve alterá-lo de uma matriz de bits para um vetor de bits. Ler seu número dinâmico envolve ir à tona, obter esse bit, perguntar onde o próximo bit está na memória, mudar para esse local, obter esse bit etc. Para cada bit no número dinâmico, você está executando três operações lidas ( atual), leia (endereço do próximo), mova (próximo). Imagine ler os valores de um milhão de números. Isso é um milhão de operações extras. Pode parecer insignificante. Mas pense nos sistemas (como finanças) em que cada milissegundo é importante.

Foi decidido que colocar o ônus no desenvolvedor para verificar o tamanho e validar é uma pequena desvantagem em comparação com o desempenho do sistema.

Michael Brown
fonte
1
A outra alternativa é implementar números semelhantes às listas de matrizes em que a matriz é realocada quando o número ultrapassa o tamanho atual. Além disso, você deve considerar o caso em que o usuário QUER que o excesso exceda o loop.
Michael Brown
Isso é verdade, mas de certa forma uma simplificação. Você poderia criar uma estrutura de matriz mais eficiente, embora não tão rápido quanto o estaticamente digitado pudesse ser "rápido o suficiente" para a maioria dos casos. por exemplo, você pode salvar informações sobre blocos de tipos diferentes, se a matriz não estiver completamente irregular, não ocupando muito mais memória ou desempenho. Ou a matriz pode sacrificar alguma memória para ter um tipo de índice. A matriz pode até se auto-otimizar com base em seu conteúdo. Você ainda pode ter a opção de digitar o tamanho da memória através de uma restrição de tipo, se precisar de desempenho.
Homde 2/02
Para ser justo, não é tão brutal quanto você imagina. Cf minha próxima resposta.
Paul Nathan
3

Tipos específicos são necessários para projetos e idiomas centrados em hardware. Um exemplo são os protocolos de rede on-the-wire.

Mas vamos criar - por diversão - um tipo de varint em uma linguagem como C ++. Construa-o a partir de uma newmatriz de ints.

Não é difícil implementar a adição: apenas x ou os bytes juntos e verifique os bits altos: se houver uma operação de transporte, newem um novo byte superior e repasse o bit. A subtração segue trivialmente na representação do complemento de 2. (Isso também é conhecido como adicionador de transporte de ondulação).

A multiplicação segue da mesma forma; use adição / deslocamento iterativo. Como sempre, a verdadeira reviravolta na sua cauda é a divisão [*].

O que você perdeu quando isso acontece?

  • Tempo determinístico. Você tem um syscall ( new) que pode ser acionado em pontos que não são necessariamente controláveis.

  • Espaço determinístico.

  • A matemática do semi-software é lenta.

Se você precisa usar uma linguagem da camada de hardware e também precisa operar em um nível alto (lento) e não deseja incorporar um mecanismo de script, isso varintfaz muito sentido. Provavelmente está escrito em algum lugar.

[*] Cf algoritmos matemáticos de hardware para maneiras mais rápidas de fazer isso - geralmente o truque são operações paralelas.

Paul Nathan
fonte
2

Essa é uma boa pergunta. Explica por que uma linguagem como Python não precisa de "short, int, long, bigint etc.": números inteiros são, assim, números inteiros (existe um único tipo inteiro no Python 3) e não têm tamanho limite (além do tamanho de a memória do computador, é claro).

Quanto ao Unicode, a codificação UTF-8 (que faz parte do Unicode) usa apenas um único caractere para caracteres ASCII, portanto não é tão ruim assim.

De um modo mais geral, as linguagens dinâmicas parecem seguir a direção mencionada. No entanto, por razões de eficiência, tipos mais restritos são úteis em alguns casos (como programas que precisam ser executados rapidamente). Não vejo muitas mudanças no futuro próximo, pois os processadores organizam os dados em bytes (ou 2, 4, 8, etc. bytes).

Eric O Lebigot
fonte
1

Com base na teoria da linguagem, você está certo. Os tipos devem se basear em um conjunto de estados legais, nas transformações disponíveis para esses estados e nas operações executáveis ​​nesses estados.

Isso é aproximadamente o que a programação OOP em sua forma típica fornece, no entanto. De fato, em Java, você está efetivamente falando sobre as classes BigIntegere BigDecimal, que alocam espaço com base em quanto é necessário para armazenar o objeto. (Como observou o FrustratedWithFormsDesigner, muitas linguagens do tipo script estão ainda mais nesse caminho e nem exigem uma declaração do tipo e armazenam o que você fornecer.)

No entanto, o desempenho ainda é relevante e, como é caro alternar tipos em tempo de execução e como os compiladores não podem garantir o tamanho máximo de uma variável no tempo de compilação, ainda temos variáveis ​​de tamanho estatístico para tipos simples em muitos idiomas.

jprete
fonte
Percebo que algum tipo de digitação dinâmica / adaptativa parece onerosa e com menos desempenho do que o que temos agora, e usando os compiladores atuais certamente seriam. Mas temos 100% de certeza de que, se você criar um idioma e um compilador desde o início, não poderá fazê-lo, se não for tão rápido quanto o estaticamente digitado, pelo menos de maneira viável para valer a pena.
Homde
1
@MKO: Por que você não tenta e vê?
Anon.
1
Sim, você pode torná-lo viável rapidamente (mas provavelmente nunca tão rápido quanto um sistema estático para números). Mas a parte "vale a pena" é mais complicada. A maioria das pessoas trabalha com dados cujo intervalo se encaixa confortavelmente em um intou a double, e se não, eles estão cientes disso, então o dimensionamento dinâmico de valor é um recurso pelo qual eles não precisam pagar.
jprete
Como todos os programadores, claro, eu sonho de algum dia fazer a minha própria língua;)
Homde
@ jprete: eu discordo; a maioria das pessoas não tem conhecimento de possíveis grandes resultados intermediários. Essa linguagem pode e foi criada com rapidez suficiente para a maioria dos propósitos.
David Thornley
1

Depende do idioma. Para linguagens de nível superior, como Python, Ruby, Erlang e outras, você só tem o conceito de números integrais e decimais.

No entanto, para uma determinada classe de idiomas com esses tipos, é muito importante. Quando você está escrevendo um código para ler e gravar formatos binários como PNG, JPeg, etc., precisa saber exatamente quantas informações estão sendo lidas por vez. O mesmo acontece com a gravação de kernels do sistema operacional e drivers de dispositivo. Nem todo mundo faz isso e, nas linguagens de nível superior, usam as bibliotecas C para realizar o trabalho pesado detalhado.

Em short, ainda há um lugar para os tipos mais específicos, mas muitos problemas de desenvolvimento não exigem essa precisão.

Berin Loritsch
fonte
0

Recentemente, criei um editor de lógica ladder e o tempo de execução e decidi ser muito limitado com os tipos:

  • boleano
  • Número
  • Corda
  • Data hora

Eu acredito que tornou mais intuitivo para o usuário. Esta é uma mudança radical da maioria dos PLCs, com todos os tipos "normais" de tipos que você veria em um idioma como C.

Scott Whitlock
fonte
0

As linguagens de programação estão se movendo nessa direção. Pegue as cordas, por exemplo. Em idiomas antigos, é necessário declarar o tamanho da string, como PIC X(42)em COBOL, DIM A$(42)em algumas versões do BASIC ou [ VAR] CHAR(42)no SQL. Nas línguas modernas, você apenas tem um stringtipo alocado dinamicamente e não precisa pensar no tamanho.

Os números inteiros são diferentes, no entanto:

O que quero dizer é: realmente precisamos de short, int, long, bigint etc etc.

Dê uma olhada no Python. Ele costumava distinguir entre números inteiros de tamanho de máquina ( int) e de tamanho arbitrário ( long). No 3.x, o antigo se foi (o antigo longé o novo int) e ninguém sente falta.

Mas ainda existe um tipo especializado para sequências de números inteiros de 8 bits na forma de bytese bytearray. Por que não usar um tupleou listde números inteiros, respectivamente? É verdade bytesque existem métodos extras semelhantes a strings tuple, mas certamente a eficiência tem muito a ver com isso.

Flutuação, reais e duplos são um pouco mais complicados, pois o tipo depende da precisão de que você precisa.

Na verdade não. A abordagem "tudo é de precisão dupla" é muito comum.

dan04
fonte
1
Talvez os tipos base devam declarar a intenção básica do tipo, ou seja, int para números "comuns", dobrar para todos os "decimais" normais (as ints não deveriam ter decimais, por simplicidade?) "Dinheiro" para trabalhar com valores e bytes para trabalhar com dados binários. Uma restrição de tipo declarada por meio de um atributo pode permitir declarar o intervalo permitido, a precisão decimal, a nulidade e até os valores permitidos. Seria legal se você pudesse criar tipos personalizados e reutilizáveis que maneira
Homde
@konrad: IMHO, a razão pela qual números inteiros "não assinados" causam dores de cabeça em C é que eles são usados ​​algumas vezes para representar números e outras vezes para representar membros de um anel algébrico abstrato. Ter tipos "toque" e "número não assinado" separados pode garantir que o código semelhante unum64 += ring32a-ring32bsempre traga o comportamento correto, independentemente de o tipo inteiro padrão ser 16 bits ou 64 [observe que o uso de +=é essencial; uma expressão como unum64a = unum64b + (ring32a-ring32b);deve ser rejeitada como ambígua.]
supercat 14/02
0

Eu entendo o raciocínio, variáveis ​​/ objetos são mantidos na memória, a memória precisa ser alocada e, portanto, precisamos saber quão grande pode ser uma variável. Mas, realmente, uma linguagem de programação moderna não deveria ser capaz de lidar com "tipos adaptativos", isto é, se algo é alocado apenas no intervalo curto, ele usa menos bytes e se algo é alocado repentinamente em um número muito grande, a memória é alocada de acordo com essa instância em particular.

Flutuação, reais e duplos são um pouco mais complicados, pois o tipo depende da precisão de que você precisa. No entanto, as strings devem ser capazes de ocupar menos memória em muitos casos (em .Net), onde ascii é usado principalmente, mas as strings buth sempre ocupam o dobro da memória devido à codificação unicode.

Fortran teve algo semelhante (não sei se é exatamente isso que você quer dizer, já que estou vendo duas perguntas realmente). Por exemplo, no F90 para cima, você não precisa definir explicitamente o tamanho do tipo , por assim dizer. O que é bom, não apenas porque fornece um local central para definir seus tipos de dados, mas também uma maneira portátil de defini-los. REAL * 4 não é o mesmo em todas as implementações em todos os processadores (e por processador quero dizer CPU + compilador), não por um longo tiro.

selected_real_kind (p, r) retorna o valor de tipo de um tipo de dados real com precisão decimal maior que pelo menos p dígitos e intervalo de expoentes maior que pelo menos r.

Então você vai, por exemplo;

program real_kinds
integer,parameter :: p6 = selected_real_kind(6)
integer,parameter :: p10r100 = selected_real_kind(10,100) !p is precision, r is range
integer,parameter :: r400 = selected_real_kind(r=400)
real(kind=p6) :: x
real(kind=p10r100) :: y
real(kind=r400) :: z

print *, precision(x), range(x)
print *, precision(y), range(y)
print *, precision(z), range(z)
end program real_kinds

(Eu acho que é um exemplo bastante auto-explicativo).

Ainda não sei se entendi sua pergunta corretamente, e é isso que você mencionou.

Torre
fonte