Todas as linguagens de programação têm suas falhas de design simplesmente porque nenhuma linguagem pode ser perfeita, assim como a maioria (todas?) De outras coisas. Além disso, qual falha de design em uma linguagem de programação o incomodou mais ao longo de sua história como programador?
Observe que se um idioma é "ruim", apenas porque não foi projetado para uma coisa específica, não é uma falha de design, mas um recurso do design; portanto, não liste esses aborrecimentos de idiomas. Se uma linguagem é inadequada para o que foi projetada, é claro que isso é uma falha no design. Coisas específicas de implementação e coisas secretas também não contam.
Respostas:
Um dos meus grandes aborrecimentos é o modo como os
switch
casos nas linguagens derivadas de C passam a passar para o próximo caso, se você esquecer de usá-lobreak
. Entendo que isso é útil em código de nível muito baixo (por exemplo , Dispositivo de Duff ), mas geralmente é inadequado para código de nível de aplicativo e é uma fonte comum de erros de codificação.Lembro-me de 1995, quando lia pela primeira vez sobre os detalhes do Java, quando cheguei à parte sobre a
switch
afirmação e fiquei muito desapontado por eles terem mantido o comportamento de falha padrão. Isso apenas se tornaswitch
glorificadogoto
com outro nome.fonte
switch
não precisa trabalhar dessa maneira. Por exemplo, a instrução case / when de Ada (equivalente a switch / case) não possui comportamento de falha.switch
- como declarações em idiomas não relacionados a C não tem que funcionar dessa maneira. Mas se você usar controle de fluxo de estilo C ({
...}
,for (i = 0; i < N; ++i)
,return
, etc.), inferência linguagem vai fazer as pessoas esperarswitch
para o trabalho como C, e dando-lhe Ada / Pascal / pessoas BASIC-like semântica teria confundido. O C # exigebreak
emswitch
declarações pelo mesmo motivo, embora o torne menos propenso a erros ao proibir o avanço silencioso. (Mas eu gostaria que você pudesse escreverfall;
, em vez do feiogoto case
.)switch
permite o avanço. É que a maioria dos usos da inovação não é intencional.Eu realmente nunca gostei do uso de
=
tarefas e==
testes de igualdade em linguagens derivadas de C. O potencial de confusão e erros é muito alto. E nem me inicie===
em Javascript.Melhor teria sido
:=
a atribuição e o=
teste de igualdade. A semântica poderia ter sido exatamente a mesma de hoje, em que atribuição é uma expressão que também produz um valor.fonte
x<-5
). Programadores C não toleraria esse tipo de espaço em branco necessário :):=
e==
porque seria muito fácil esquecer:
e não ser notificado como já é o caso (embora revertido) quando você esquecer um=
hoje. Sou grato por avisos do compilador sobre isso ...=
e==
não está na leitura, porque são símbolos distintos. É por escrito e certificando-se de que você acertou.A escolha do
+
Javascript para adição e concatenação de strings foi um erro terrível. Como os valores não são digitados, isso leva a regras bizantinas que determinam se+
adicionar ou concatenar, dependendo do conteúdo exato de cada operando.Teria sido fácil no começo introduzir um operador completamente novo, como
$
concatenação de strings.fonte
+
faz muito sentido como um operador de concatenação de strings em uma linguagem fortemente tipada. O problema é que o Javascript o usa, mas não é fortemente digitado.+
não faz sentido para concatenação, porque concatenação definitivamente não é comutativa. Para mim, é um abuso.*
operador?Acho que o Javascript é o padrão global para um problema grave e, muitas vezes, fonte de erros, se você não usa JSLint ou algo parecido.
fonte
var
sempre que você declarar uma variável e estiver pronto para prosseguir. E não me diga que é muita digitação, porque o Java força você a declarar todos os tipos duas vezes e ninguém reclama que é uma escolha de design de merda.std::map<KEY, VALUE>::const_iterator
variáveis em C ++ o suficiente paraauto
serem adicionadas a esse idioma."use strict"
diretiva, adicionada em es5, altera as referências não declaradas em erro.O pré-processador em C e C ++ é um grande problema, cria abstrações que vazam como peneiras, incentiva o código espaguete por meio de ninhos de
#ifdef
declarações de ratos e requerALL_CAPS
nomes terrivelmente ilegíveis para contornar suas limitações. A raiz desses problemas é que ele opera no nível textual, e não no nível sintático ou semântico. Deveria ter sido substituído por recursos de linguagem real para seus vários casos de uso. Aqui estão alguns exemplos, embora alguns deles sejam resolvidos em C ++, C99 ou extensões padrão não oficiais, mas de fato :#include
deveria ter sido substituído por um sistema de módulo real.Funções embutidas e modelos / genéricos podem substituir a maioria dos casos de uso de chamada de função.
Algum tipo de recurso de tempo de manifesto / compilação constante pode ser usado para declarar essas constantes. As extensões de enum de D funcionam muito bem aqui.
Macros reais no nível da árvore de sintaxe podem resolver muitos casos de uso diversos.
Mixins de strings podem ser usados para o caso de uso de injeção de código.
static if
ouversion
instruções podem ser usadas para compilação condicional.fonte
#include
questão, mas o sistema de módulos foi inventado ... depois! E C e C ++ apontar para um máximo de compatibilidade: /#include
hackers baseados em cpp .Pode-se listar centenas de erros em centenas de idiomas, mas o IMO não é um exercício útil do ponto de vista do design de idiomas.
Por quê?
Porque algo que seria um erro em um idioma não seria um erro em outro idioma. Por exemplo:
Não são lições a serem aprendidas, mas as lições são cortados raramente clara, e para entendê-los você tem que entender os trade-offs técnicos ... eo contexto histórico. (Por exemplo, a complicada implementação Java de genéricos é uma conseqüência de um requisito comercial primordial para manter a compatibilidade com versões anteriores).
Na IMO, se você é sério sobre o design de um novo idioma, precisa realmente usar uma ampla variedade de idiomas existentes (e estudar idiomas históricos) ... e se decidir quais são os erros. E você precisa ter em mente que cada uma dessas linguagens foi projetada em um contexto histórico específico, para preencher uma necessidade específica.
Se houver lições gerais a serem aprendidas, elas estão no nível "meta":
fonte
C e C ++ : todos esses tipos inteiros que não significam nada.
Especialmente
char
. É texto ou é um número inteiro minúsculo? Se for texto, é um caractere "ANSI" ou uma unidade de código UTF-8? Se for um número inteiro, está assinado ou não?int
foi planejado para ser o número inteiro "nativo", mas em sistemas de 64 bits, não é.long
pode ou não ser maior queint
. Pode ou não ser do tamanho de um ponteiro. É praticamente uma decisão arbitrária por parte dos escritores do compilador, seja de 32 ou 64 bits.Definitivamente uma linguagem dos anos 70. Antes do Unicode. Antes de computadores de 64 bits.
fonte
null
.Seu inventor, Tony Hoare, chama isso de "erro de bilhão de dólares" .
Foi introduzido no ALGOL nos anos 60 e existe na maioria das linguagens de programação mais usadas atualmente.
A melhor alternativa, usada em idiomas como OCaml e Haskell, é o talvez . A idéia geral é que as referências a objetos não possam ser nulas / vazias / inexistentes, a menos que haja uma indicação explícita de que podem ser.
(Embora Tony seja incrível em sua modéstia, acho que quase qualquer um teria cometido o mesmo erro, e ele foi o primeiro.)
fonte
maybe
como um opt-in nulo, enquanto a exceção nula é um opt-out. É claro que há algumas coisas a serem ditas sobre a diferença entre erros de tempo de execução e erros em tempo de compilação também, mas apenas o fato de nulo ser essencialmente um comportamento de injeção é digno de nota por si só.Tenho a sensação de que as pessoas que criaram o PHP não usavam um teclado normal, nem sequer usam um teclado colemak, porque deveriam ter percebido o que estavam fazendo.
Eu sou desenvolvedor de PHP. PHP não é divertido de digitar.
Who::in::their::right::mind::would::do::this()
? O::
operador exige manter pressionada a tecla Shift e depois pressionar duas teclas. Que desperdício de energia.Embora-> isto-> não seja> muito-> melhor. Isso também requer três pressionamentos de tecla, com a mudança entre os dois símbolos.
$last = $we.$have.$the.$dumb.'$'.$character
. O cifrão é usado uma quantidade enorme de vezes e requer o alongamento do prêmio até a parte superior do teclado, além de pressionar a tecla Shift.Por que eles não conseguiram projetar o PHP para usar chaves muito mais rápidas para digitar? Por
we.do.this()
que o vars não pôde ou teve início com uma chave que requer apenas um único pressionamento de tecla - ou não (JavaScript) e apenas pré-define todos os vars (como eu tenho que fazer pelo E_STRICT)!Não sou datilógrafo lento - mas essa é apenas uma escolha de design ruim.
fonte
I::have::nothing::against::this->at.all()
O uso de formulários inspirados na área de trabalho no asp.net .
Sempre pareceu uma farsa e atrapalhou ou como a web realmente funciona. Felizmente, o asp.net-mvc não sofre da mesma maneira, apesar de agradecer a Ruby etc. por essa inspiração.
fonte
Para mim, é a absoluta falta de convenções de nomes e pedidos de argumentos do PHP em sua biblioteca padrão.
Embora a necessidade do JASS de anular as referências após o lançamento / remoção do objeto referenciado (ou a referência vaze e vários bytes de memória sejam perdidos) seja mais grave, mas como o JASS é uma linguagem de propósito único, isso não é crítico.
fonte
A maior falha de design que eu enfrento é que o python não foi projetado como o python 3.x para começar.
fonte
Matriz decaimento em C e, consequentemente, C ++.
fonte
delete
e separadosdelete[]
.Tipos primitivos em Java.
Eles quebram o princípio de que tudo é descendente
java.lang.Object
, o que, do ponto de vista teórico, leva a uma complexidade adicional da especificação da linguagem e, de uma perspectiva prática, eles tornam o uso de coleções extremamente tedioso.A autoboxing ajudou a aliviar os inconvenientes práticos, mas ao custo de tornar a especificação ainda mais complicada e de introduzir uma casca de banana grande e gorda: agora você pode obter uma exceção de ponteiro nulo do que parece ser uma operação aritmética simples.
fonte
Eu conheço Perl melhor, então vou escolher.
Perl tentou muitas idéias. Alguns eram bons. Alguns eram ruins. Alguns eram originais e não eram amplamente copiados por um bom motivo.
Uma é a idéia de contexto - toda chamada de função ocorre em lista ou contexto escalar e pode fazer coisas totalmente diferentes em cada contexto. Como apontei em http://use.perl.org/~btilly/journal/36756, isso complica todas as APIs e freqüentemente leva a problemas sutis de design no código Perl.
O próximo é a ideia de vincular completamente a sintaxe e os tipos de dados. Isso levou à invenção do empate para permitir que objetos se disfarçam como outros tipos de dados. (Você também pode obter o mesmo efeito usando sobrecarga, mas o empate é a abordagem mais comum no Perl.)
Outro erro comum, cometido por muitos idiomas, é começar oferecendo escopo dinâmico em vez de lexical. É difícil reverter essa decisão de projeto posteriormente e leva a verrugas duradouras. A descrição clássica dessas verrugas no Perl é http://perl.plover.com/FAQs/Namespaces.html . Observe que isso foi escrito antes do Perl adicionar
our
variáveis estatic
variáveis.As pessoas discordam legitimamente sobre digitação estática e dinâmica. Eu pessoalmente gosto de digitação dinâmica. No entanto, é importante ter estrutura suficiente para permitir que erros de digitação sejam detectados. O Perl 5 faz um bom trabalho com rigor. Mas Perl 1-4 entendeu errado. Vários outros idiomas têm verificadores de fiapos que fazem a mesma coisa que estritos. Desde que você seja bom em impor a verificação de cotão, isso é aceitável.
Se você estiver procurando por mais idéias ruins (muitas delas), aprenda PHP e estude sua história. Meu erro favorito do passado (corrigido há muito tempo porque causava tantas falhas de segurança) era o padrão para permitir que qualquer pessoa definisse qualquer variável passando parâmetros de formulário. Mas isso está longe de ser o único erro.
fonte
Ambiguidade JavaScripts para blocos de código e literais de objetos.
poderia ser um bloco de código, onde
a
é um rótulo eb
é uma expressão; ou pode definir um objeto, com um atributoa
que tenha o valorb
fonte
a
.Voltarei ao FORTRAN e à insensibilidade a espaço em branco.
Ele permeava a especificação. O
END
cartão tinha que ser definido como um cartão com 'E', 'N' e 'D' nessa ordem nas colunas 7-72, e sem outros itens em branco, em vez de um cartão com "END" no lugar apropriado. colunas e nada mais.Isso levou a uma fácil confusão sintática.
DO 100 I = 1, 10
foi uma instrução de controle de loop, enquantoDO 100 I = 1. 10
foi uma instrução que atribuiu o valor 1.1 a uma variável chamadaDO10I
. (O fato de as variáveis poderem ser criadas sem declaração, o tipo delas dependendo da primeira letra contribuiu para isso.) Diferente de outros idiomas, não havia como usar espaços para separar tokens para permitir a desambiguação.Também permitiu que outras pessoas escrevessem códigos realmente confusos. Há razões pelas quais esse recurso do FORTRAN nunca foi duplicado novamente.
fonte
(test ? a : b)
, e) insistem ao usar**
, f) não pode lidar com distinção entre maiúsculas e minúsculas. A maior parte disso foi por causa de punções nos anos 50.Um dos maiores problemas com o BASIC foi a falta de um método bem definido para estender a linguagem além dos ambientes iniciais, levando a várias implementações completamente incompatíveis (e a uma tentativa pós-fato quase irrelevante de qualquer padronização).
Quase qualquer idioma será usado para fins gerais por algum programador louco. É melhor planejar esse uso de uso geral no início, caso essa ideia louca decole.
fonte
Eu acredito em DSLs (linguagens específicas de domínio) e uma coisa que valorizo em uma linguagem é se ela me permite definir uma DSL em cima dela.
No Lisp, existem macros - a maioria das pessoas considera isso uma coisa boa, assim como eu.
Em C e C ++, existem macros - as pessoas reclamam, mas consegui usá-las para definir DSLs.
Em Java, eles foram deixados de fora (e, portanto, em C #), e a falta deles foi declarada uma virtude. Claro que permite que você tenha bom senso, mas para mim isso é apenas uma obra de arte . Para fazer minha DSL, tenho que expandir manualmente. É uma dor e me faz parecer um péssimo programador, mesmo que me permita fazer muito mais com muito menos código.
fonte
cdr
da lista e formaria um fechamento lambda (ou seja, uma continuação) e passaria como um argumento para ocar
da lista. Isso foi feito recursivamente, é claro, e "faria a coisa certa" para condicionais, loops e chamadas de função. Então a função "escolha" acabou de se transformar em um loop normal. Não é bonito, mas era robusto. O problema é que torna super fácil criar loops excessivamente aninhados.Declarações , em todos os idiomas que as contêm. Eles não fazem nada que você não pode fazer com expressões e o impedem de fazer muitas coisas. A existência de um
?:
operador ternário é apenas um exemplo de ter que tentar contorná-los. Em JavaScript, eles são particularmente irritantes:fonte
unit
tipo (aka()
) em vez de declarações têm consideração especial para garantir que eles não lançem avisos ou se comportem de maneira estranha.Para mim, é o problema de design que afeta todos os idiomas derivados de C; ou seja, o " dangling else ". Esse problema gramatical deveria ter sido resolvido em C ++, mas foi realizado em Java e C #.
fonte
Acho que todas as respostas até agora apontam para uma falha única de muitos idiomas principais:
Não há como alterar o idioma principal sem afetar a compatibilidade com versões anteriores.
Se isso for resolvido, praticamente todas as outras queixas poderão ser resolvidas.
EDITAR.
isso pode ser resolvido nas bibliotecas com espaços para nome diferentes, e você pode conceber fazer algo semelhante para a maior parte do núcleo de uma linguagem, embora isso possa significar que você precisa oferecer suporte a vários compiladores / intérpretes.
Por fim, acho que não sei como resolvê-lo de uma maneira totalmente satisfatória, mas isso não significa que não exista uma solução ou que mais não possa ser feito
fonte
Estouro aritmético de número inteiro silencioso do Java
fonte
Java e C # têm problemas irritantes com seus sistemas de tipos devido ao desejo de manter a compatibilidade com versões anteriores ao adicionar genéricos. Java não gosta de misturar genéricos e matrizes; O C # não permitirá algumas assinaturas úteis porque você não pode usar tipos de valor como limites.
Como exemplo deste último, considere que
ao lado ou substituindo naEnum
classe permitiria ao invés do tautológicotl; dr: pense no polimorfismo paramétrico quando você começar a projetar seu sistema de tipos, não depois de publicar a versão 1.
fonte
MyMethod<T>(T value) where T : struct, IComparable, IFormattable, IConvertible
Mas você ainda precisa testar uma enumeração e isso é um hack. Eu acho que a maior falta nos genéricos de C # não é compatível com tipos mais altos, o que realmente abriria a linguagem para alguns conceitos interessantes.Sinto que estou me abrindo para ser inflamado, mas realmente odeio a capacidade de passar tipos de dados antigos simples por referência em C ++. Eu só odeio poder passar tipos complexos por referência. Se eu estou olhando para uma função:
Do ponto de chamada, não há como dizer que
bar
, que pode ser definido em um arquivo completamente diferente, é:Alguns podem argumentar que fazer algo assim pode ser apenas um projeto de software ruim e não culpar a linguagem, mas não gosto que a linguagem permita que você faça isso em primeiro lugar. Usando um ponteiro e chamando
é muito mais legível.
fonte
ALTERAR
Quando aprendi COBOL, a instrução ALTER ainda fazia parte do padrão. Em poucas palavras, essa instrução permitiria modificar chamadas de procedimento durante o tempo de execução.
O perigo era que você poderia colocar essa declaração em uma seção obscura do código que raramente era acessada e que tinha o potencial de alterar completamente o fluxo do restante do seu programa. Com várias instruções ALTER, você pode tornar quase impossível saber o que seu programa estava fazendo a qualquer momento.
Meu instrutor da universidade, enfaticamente, afirmou que, se alguma vez visse essa afirmação em algum de nossos programas, ele automaticamente nos reprovaria.
fonte
v() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }
você dizv() { result = long(operation); v = () => result; result; }
O pior pecado de uma linguagem de programação não está sendo bem definido. Um caso que me lembro é o C ++, que, em suas origens:
Pelo que me lembro, demorou cerca de uma década para definir C ++ bem o suficiente para torná-lo tão profissionalmente confiável quanto C. É algo que nunca deve acontecer novamente.
Outra coisa que considero um pecado (deveria ser uma resposta diferente?) É ter mais de uma "melhor" maneira de realizar uma tarefa comum. É o caso de (novamente) C ++, Perl e Ruby.
fonte
Classes em C ++ são algum tipo de padrão de design forçado na linguagem.
Praticamente não há diferença no tempo de execução entre uma estrutura e uma classe, e é tão confuso entender qual é a verdadeira vantagem de programação de "ocultar informações" que eu quero colocar lá.
Ficarei com o voto negativo por isso, mas enfim, os compiladores C ++ são tão difíceis de escrever que essa linguagem parece um monstro.
fonte
Embora todos os idiomas apresentem suas falhas, nenhum é incômodo quando você os conhece. Exceto para este par:
Sintaxe complexa acoplada a APIs prolixo
Isto é particularmente verdade em uma linguagem como Objective-C. Não apenas a sintaxe é extremamente complexa, mas a API usa nomes de funções como:
Sou a favor de ser explícito e não ambíguo, mas isso é ridículo. Cada vez que me sento com o xcode, me sinto como um n00b, e isso é realmente frustrante.
fonte
tableView:cellForRowAtIndexPath:
, o que é muito descritivo na minha opinião.fonte
(u)int_least8_t
). A assinatura faz todo sentido para números inteiros pequenos, mas não faz sentido para caracteres.char*
... como um C-String, eles ficam realmente confusos.sbyte
,byte
echar
tipos.