Qual é a maior falha de design que você enfrentou em qualquer linguagem de programação? [fechadas]


29

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.


6
Observe que isso é construtivo para designers de linguagem (erros a serem evitados), se alguém quiser questionar quão construtiva é essa pergunta.
Anto


1
@greyfade: Não, na verdade, trata-se de falhas no idioma real, que parecem ser sobre coisas que diminuem a adoção de um idioma, que podem incluir uma biblioteca padrão ruim ou apenas um site ruim para o idioma. Algumas respostas listar por exemplo ruim de sintaxe, mas isso não é um específico falha de projeto
Anto

8
Maior falha em qualquer linguagem de programação? Humanos.
Joel Etherton

Se essas respostas são as maiores falhas, estou realmente impressionado com as linguagens de programação.
Tom Hawtin - defina

Respostas:


42

Um dos meus grandes aborrecimentos é o modo como os switchcasos nas linguagens derivadas de C passam a passar para o próximo caso, se você esquecer de usá-lo break. 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 switchafirmação e fiquei muito desapontado por eles terem mantido o comportamento de falha padrão. Isso apenas se torna switchglorificado gotocom outro nome.


3
@ Christopher Mahan: switchnão precisa trabalhar dessa maneira. Por exemplo, a instrução case / when de Ada (equivalente a switch / case) não possui comportamento de falha.
Greg Hewgill

2
@ Greg: 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 esperar switchpara o trabalho como C, e dando-lhe Ada / Pascal / pessoas BASIC-like semântica teria confundido. O C # exige breakem switchdeclarações pelo mesmo motivo, embora o torne menos propenso a erros ao proibir o avanço silencioso. (Mas eu gostaria que você pudesse escrever fall;, em vez do feio goto case.)
dan04

4
então não escreva switch, escreva if () else. o motivo pelo qual você está reclamando é o que torna a mudança melhor: não é uma condicional verdadeira / falsa, é uma condicional numeral e isso a torna diferente. Você também pode escrever sua própria função de chave.
jokoon

9
-1 Considero um benefício permitir a queda, não há outro perigo além da estupidez, mas concede uma função adicional.
Orbling

11
O problema não é que switch permite o avanço. É que a maioria dos usos da inovação não é intencional.
dan04

41

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.


3
@ Nemanja Trifunovic: Originalmente pensei nessa sugestão, mas ela tem uma ambiguidade infeliz em C com comparação menor que um número negativo (ou seja, x<-5). Programadores C não toleraria esse tipo de espaço em branco necessário :)
Greg Hewgill

7
@ Greg: Eu preferiria :=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 ...
Matthieu M.

13
Em quase todos os teclados que já usei, ": =" requer a alteração da tecla Shift enquanto a digita. No que estou usando agora, ':' está em maiúsculas e '=' está em minúsculas, e isso foi revertido. Eu digito muitas tarefas e não preciso desse tipo de dificuldade de digitação nelas.
David Thornley

14
@ David Thornley: O código é lido muitas vezes mais do que está escrito. Eu não compro argumentos sobre "digitar problemas".
Greg Hewgill 7/03/11

8
@ Greg Hewgill: Claro que é lido com mais frequência do que escrito. No entanto, o problema entre =e ==não está na leitura, porque são símbolos distintos. É por escrito e certificando-se de que você acertou.
21711 David Thornley

29

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.


13
@ Barry: Na verdade não. +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.
Mason Wheeler

13
@Mason: +não faz sentido para concatenação, porque concatenação definitivamente não é comutativa. Para mim, é um abuso.
Matthieu M.

12
@ Matthieu: Umm ... por que isso importa? A concatenação não é comutativa (como a adição é), mas a adição de duas strings não faz sentido, então ninguém pensa dessa maneira. Você está inventando um problema em que não existe.
Mason Wheeler

4
basta mudar para C ++ e adicionar qualquer tipo de sobrecarga esotérica para o operador de sua escolha
Newtopian

8
@ Matthieu: Comutatividade não é o problema aqui. Se JS tivesse uma classe Matrix, você consideraria um abuso ter um *operador?
dan04

24

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.


2
Padrão para global? Estou feliz que eu não uso JS, então ...
Anto 5/03

5
Na verdade, é uma linguagem muito agradável, com poucas opções de design ruins. Essa é a principal, se você não definir uma variável, ela será definida como global. O bom é que, usando o programa Jslint de Doug Crockford, você pode pegar esse erro e muito mais.
Zachary K

1
Use varsempre 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.
Davidk01 6/03/11

3
@ davidk01: As pessoas reclamam disso. Da mesma forma, as pessoas reclamaram de ter que declarar std::map<KEY, VALUE>::const_iteratorvariáveis ​​em C ++ o suficiente para autoserem adicionadas a esse idioma.
dan04

1
a "use strict"diretiva, adicionada em es5, altera as referências não declaradas em erro.
Sean McMillan

22

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 #ifdefdeclarações de ratos e requer ALL_CAPSnomes 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 ifou versioninstruções podem ser usadas para compilação condicional.


2
@dsimcha: Concordo com a #includequestão, mas o sistema de módulos foi inventado ... depois! E C e C ++ apontar para um máximo de compatibilidade: /
Matthieu M.

@ Matthieu: Sim, os sistemas de módulos foram inventados posteriormente, mas estamos falando de retrospectiva aqui de qualquer maneira.
dsimcha

3
Concordo que essa é uma grande dor em várias partes do corpo, mas o design de múltiplas passagens tem a vantagem da simplicidade: um compilador C não precisa saber muito sobre o contexto de operação antes de poder compilar com êxito um pedaço de código C . Isso só pode ser qualificado como erro de design se você puder mostrar que os custos do uso de um sistema de módulo hipotético na própria linguagem C (por exemplo, classes do tipo C ++) são sempre inferiores ou comparáveis ​​aos atuais #includehackers baseados em cpp .
Reinierpost

Concordo. No entanto, mas algumas pessoas pensam que o pré-processamento é uma coisa boa e reclamam que ele não é suportado (por exemplo) em Java.
Stephen C

5
@ Stephen: Eu concordo que o Java com um pré-processador pode ser melhor que o Java sem, mas apenas porque o Java não possui vários recursos "reais" necessários para substituir o pré-processador. Em linguagens como D, que incluem esses recursos, e Python, que ganha flexibilidade de outras maneiras por ser dinâmico, não perco nada.
dsimcha

21

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:

  • Tornar C um idioma gerenciado (ou seja, coletado de lixo) ou restringir os tipos primitivos limitaria sua utilidade como um idioma de baixo nível semi-portátil.
  • Adicionar o gerenciamento de memória no estilo C ao Java (por exemplo, para resolver problemas de desempenho) seria um problema.

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":

  • Você não pode criar uma linguagem de programação ideal para todos os fins.
  • Você não pode evitar cometer erros ... especialmente quando visto de trás.
  • Muitos erros são difíceis de corrigir ... para usuários do seu idioma.
  • Você deve levar em consideração o histórico e as habilidades do seu público-alvo; ou seja, programadores existentes.
  • Você não pode agradar a todos.

9
-1 - linguagens de programação seguem os objetivos do projeto. Um recurso de uma linguagem que trabalha contra esses objetivos é uma falha de design, a menos que seja um compromisso necessário para um de seus outros objetivos. Poucas línguas são criadas com a intenção de satisfazer a todos, mas todas as línguas devem tentar satisfazer as pessoas que se propõe a satisfazer em primeiro lugar. Esse tipo de correção política pós-moderna é bastante sufocante para a pesquisa e o desenvolvimento da linguagem de programação.
Rei Miyasaka

O que quero dizer é que é difícil aprender lições sem levar em conta os objetivos do projeto.
Stephen C

1
Parece-me que o OP já respondeu à sua resposta no segundo parágrafo da pergunta.
Aidan Cully

20

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 é.

longpode ou não ser maior que int. 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.


10
C não foi projetado para ser um idioma padrão para todos, portanto, não pode ser um erro de design. Ele foi projetado para modelar uma CPU como montadora portátil, evitando o código específico da montadora. No entanto, foi corrigido em Java.

7
Penso que o maior problema são as línguas mais novas que continuam a usar os mesmos termos sem sentido, apesar da história nos mostrar que é uma péssima ideia. Pelo menos o pessoal do C notou seu erro e criou tipos int padrão.
Mark H

5
Um char não é uma unidade utf-8. Um caractere utf-8 pode levar mais de 8 bits para armazenar. C não é uma linguagem da década de 1970, estou usando-a agora para um projeto (voluntariamente).
22411 daniwaterworth

4
C é pouco mais que uma abstração de alto nível do processador PDP-11. Por exemplo, pré e pós incremento foram diretamente suportados pelo PDP-11.
precisa saber é o seguinte

5
Esta é uma resposta terrivelmente equivocada. Primeiro, C e C ++ não são intercambiáveis. Segundo, a especificação da linguagem define claramente o que é um char - Um objeto declarado como tipo char é grande o suficiente para armazenar qualquer membro do conjunto de caracteres de execução básica. . Terceiro, C não é uma "linguagem dos anos 70", é uma linguagem que vive próxima ao hardware e provavelmente é a linguagem que permite que todas as abstrações de alto nível façam sentido para uma CPU. Você sai como uma pessoa que conhece apenas idiomas de alto nível e não aprecia a forma como as coisas realmente funcionam. -1
Ed S.

18

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.)


Discordo, mesmo com seu inventor !!! null é o valor vazio para o tipo de dados ponteiro / referência. Strings possui string vazia, sets possui conjunto vazio (Pascal set vazio = []), inteiros possuem 0. A maioria das linguagens de programação que usam nulo / zero / qualquer que seja, se uma variável é atribuída corretamente, um erro nulo pode ser evitado.
umlcat

4
@ user14579: Todo idioma que suporta qualquer tipo de conjunto, string ou matriz possui {}, mas ainda é semanticamente apropriado e não trava, a menos que você já tenha algo que possa causar um erro nos limites da matriz - mas esse é outro problema. Você pode processar uma sequência vazia para colocar em maiúscula todos os caracteres, o que resultará em uma sequência vazia. Você tenta a mesma coisa em uma sequência nula e, sem a devida consideração, ela falha. O problema é que essa consideração apropriada é tediosa, muitas vezes esquecida e dificulta a criação de funções de expressão única (por exemplo, lambdas).
Rei Miyasaka

1
Eu ganho dinheiro toda vez que digito nulo ... ah, alguém perde dinheiro toda vez que digito nulo. Exceto desta vez.
Kevpie 12/03/11

3
@umlcat - quando você tem idiomas com correspondência de padrões como Ocaml, Haskell e F #, usando o código Talvez x | Nenhum padrão impede que você esqueça o caso nulo em tempo de compilação. Nenhuma quantidade de truques em tempo de compilação pode detectar um erro nos idiomas em que nulo é o idioma estabelecido. Como você deve escolher explicitamente não lidar com o caso nulo em idiomas com a mônada Maybe e Some, eles têm uma séria vantagem sobre a abordagem "nula".
JasonTrue

1
@ Jason - eu gosto de pensar maybecomo 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ó.
Rei Miyasaka

14

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.


Perl compartilha essa dor.
Daenyth

2
O mesmo acontece com o C ++ e, por alguma razão bizarra e inexplicável, o PowerShell.
Rei Miyasaka

2
Use um editor mais poderoso e defina suas próprias seqüências de teclas para esses operadores.
kevin Cline

1
I::have::nothing::against::this->at.all()
Mateen Ulhaq

1
Talvez eles não usem teclados qwerty. $ e: não precisa da tecla Shift pressionada em toda a azerty, como teclados.
Arkh

13

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.


18
Isso não é coisa da biblioteca?
Nikie

@nikie que é um ponto bom;)
pomba

1
@nikie Na verdade, o código ASPX baseado em XML é uma linguagem, para que você possa alterar esse código para funcionar. : D
CodexArcanum

2
O verdadeiro problema com o ASP.NET, eu acho, é o quanto ele tenta esconder os detalhes da web do programador. Na verdade, existem algumas coisas realmente legais e úteis acontecendo no ASP.NET, mas você precisa lutar tanto e se aprofundar tanto para chegar lá.
CodexArcanum

1
Por outro lado, existem milhares e milhares de aplicativos de coleta de dados simples e bem-sucedidos que foram criados usando o aplicativo de desktop "clássico". A única coisa ruim era que, até o MVC, a única opção da Microsoft eram os formulários do Windows.
ElGringoGrande

13

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.


9
A falta de convenções no stdlib do PHP é discutível, não uma falha no design da linguagem .

3
@ delnan: A falta de convenções é resultado de como o PHP foi projetado e, portanto, tem muito a ver com o design da linguagem. Também não está claro para mim que exista uma distinção clara entre bibliotecas e linguagem. O Lisp, em particular, tem uma tradição orgulhosa de inicializar um idioma em cima de outro.
btilly

1
O mais notável do JASS era que ele tinha referências contando com alças, mas não as limpava a menos que fossem destruídas manualmente (e a interface gráfica criava funções que vazavam memória em todos os lugares)!
Craig Gidney

13

A maior falha de design que eu enfrento é que o python não foi projetado como o python 3.x para começar.


1
Bem, nem mesmo Guido pode obter tudo certo de uma vez ...

5
@ delnan, oh eu sei, e python <3 ainda é uma linguagem incrivelmente boa, mas é um pouco chato ter uma linguagem melhor na forma de python 3.x que não posso usar porque quebra todos os módulos que Eu preciso.
22411 dan_waterworth

Continue fazendo lobby para os módulos python 3.x! Enquanto isso, continuarei escrevendo no 2.5.4. Graças a SO, na verdade sou lembrado que o 3.x está vivo e bem.
Kevpie 12/03/11

@kevpie Primeiro, faça lobby para adicionar compilação condicional ao python para facilitar a transição para os mantenedores de bibliotecas. 2to3 não é uma solução sustentável a longo prazo.
Evan Plaice

12

Matriz decaimento em C e, consequentemente, C ++.


Também desejo suporte adequado ao array. Em C ++, você pode prevenir a cárie utilizando abscons sintaxe e modelos ... mas é mais de um hack: /
Matthieu M.

1
Observe que esse é o motivo pelo qual o C ++ precisa ter operadores deletee separados delete[].
dan04

Você sempre pode colocar uma matriz em uma estrutura e passar por valor, se quiser, mas isso geralmente é mais complicado do que o problema original. Em C ++, geralmente você pode evitar a necessidade de usar matrizes.
precisa

2
Pelo menos no caso C, o argumento contra o suporte adequado à matriz é "a verificação dos limites da matriz é cara", principalmente devido à maneira como a aritmética do ponteiro C funciona.
Stephen C

@ Stephen C: Que verificação de limites de matriz tem a ver com deterioração de matriz?
Nemanja Trifunovic

11

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.


Isso causaria um problema de desempenho terrível se você removesse os tipos de primitivos. E a autoboxing pode ser facilmente alterada, por isso não melhora nada.
deadalnix

Na época, essa foi uma decisão sensata dos designers de Java. Os ganhos de desempenho, dadas as máquinas / VMs disponíveis nos anos 90, superaram as vantagens conceituais de unificar tudo em torno do java.lang.Object.
Mikera # 8/11

10

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 ourvariáveis ​​e staticvariá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.


5
Sim, Perl tem muitos erros, porque as pessoas que o criaram estavam tentando novas idéias, e quando você faz isso, geralmente as interpreta mal. (Perl também tem algumas coisas muito bem, e é o padrão para Regexps que todo mundo parece ter copiado)
Zachary K

@ zachary-k: Absolutamente concordado. E tentei deixar isso claro antes de começar a analisar os problemas.
btilly

4
Os Lisps eram originalmente de escopo dinâmico e, com o tempo, passaram a ser lexicamente (pelo menos em Scheme e Common Lisp). Não é impossível mudar.
precisa

4
@ david-thornley: É impossível, a menos que você sacrifique a compatibilidade com versões anteriores em algum lugar. O esquema sempre teve um escopo lexical. O Lisp comum tinha um escopo lexical desde o momento em que foi padronizado, mas várias comunidades Lisp tiveram suas lutas adotando-o. E o Emacs Lisp ainda está usando o escopo dinâmico, mesmo que haja um desejo de alterá-lo por um longo tempo.
btilly

1
BTW, muitas das coisas pelas quais as pessoas não gostam do Perl não foram inventadas no Perl, mas tiradas de outros idiomas, principalmente o shell Bourne.
reinierpost

10

Ambiguidade JavaScripts para blocos de código e literais de objetos.

  {a:b}

poderia ser um bloco de código, onde aé um rótulo e bé uma expressão; ou pode definir um objeto, com um atributo aque tenha o valorb


Na verdade, eu gosto disso sobre JavaScript. A simplicidade da estrutura da linguagem é agradável e o objetivo deve ser evidente se o desenvolvedor souber o que está fazendo.
Xeoncross

2
Xeoncross: Eu geralmente não gosto de ambiguidades. Nesse caso, é óbvio para o desenvolvedor, mas eval () precisa de parênteses extras.
user281377

2
@ammoQ: É óbvio? Então o que é? Um objeto ou um bloco de código?
configurador

configurador: Obviamente um objeto. Nenhuma pessoa sã usaria um rótulo chamado a.
user281377

10

Voltarei ao FORTRAN e à insensibilidade a espaço em branco.

Ele permeava a especificação. O ENDcartã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, 10foi uma instrução de controle de loop, enquanto DO 100 I = 1. 10foi uma instrução que atribuiu o valor 1.1 a uma variável chamada DO10I. (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.


1
Em uma linguagem onde você pode redefinir literais isso é o pior exemplo - Quero dizer que só causou um minúsculo nave espacial para falhar
Martin Beckett

No início, você poderia escrever DAMNATION no lugar de DIMENSION, e funcionaria.
Mike Dunlavey

Ainda está sendo ensinado por pessoas fora do CS. Eu ainda tenho que lidar com aqueles que a) lutam contra declarações, b) lutam contra espaços em branco, c) como "linhas de continuação", d) usam nomes de 6 caracteres ou 4, d) ficam perplexos quando vêem (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.
Mike Dunlavey

1
@ Martin Beckett - redefinir literais no FORTRAN era realmente uma falha do compilador e não um recurso de linguagem. Certamente não era um recurso intencional da linguagem.
Stephen C

1
@osteroster: eu certamente fiz. Posso estar errado, mas lembro-me vagamente da definição de idioma baseada em cartões perfurados. Eles eram a principal maneira de inserir programas FORTRAN naquela época, e a idéia de uma linha de 80 colunas com as colunas 73-80 reservadas é de cartões perfurados.
David Thornley

9

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.


2
+1: qualquer idioma sem módulos e bibliotecas apropriados é um erro esperando para acontecer. A COBOL também sofreu com isso, levando a variantes peculiares que não são compatíveis.
22411 S.Lott

8

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.


4
Eu concordo que qualquer linguagem sem macros decentes é uma falha enorme no design que não pode ser corrigida. Mas o que você quer dizer com ' eles foram deixados de fora '? O pré-processador C não era nenhum tipo de sistema macro decente. Java não é derivado de nenhuma linguagem adequada com macros.
SK-logic

1
Você pode escrever sua DSL em uma linguagem de processamento de macro externa (como m4, por exemplo, entre uma infinidade de outras).
APENAS MINHA OPINIÃO correta

4
@ SK: Não vou dizer que o pré-processador C é um sistema macro decente comparado ao Lisp (por exemplo). Mas, comparado a nada , é extremamente útil.
Mike Dunlavey

4
@reinierpost: Estou pensando em coisas que poderia fazer no Lisp, como introduzir estruturas de controle como execução diferencial e retorno. Isso pode ser feito com macros Lisp. Em C / C ++, eu poderia executar execução diferencial com macros C (e um pouco de disciplina para programadores), mas não para voltar atrás. Com o C #, não posso fazer nenhum dos dois. O que eu troco são coisas como intellisense. BFD.
precisa saber é o seguinte

1
@ David: A maneira como eu fiz isso foi que eu tinha uma macro para envolver o código comum, como uma lista de instruções. Ele pegaria o cdrda lista e formaria um fechamento lambda (ou seja, uma continuação) e passaria como um argumento para o carda 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.
Mike Dunlavey

7

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:

// With statements:
node.listen(function(arg) {
  var result;
  if (arg) {
    result = 'yes';
  } else {
    result = 'no';
  }
  return result;
})

// Without:
node.listen(function(arg) if (arg) 'yes' else 'no')

Estou confuso aqui: você quer apenas uma maneira mais simples de fazer as coisas?
TheLQ

2
Corrigir. Expressões para tudo.
munificent

1
Lisp funciona bem para isso.
precisa

1
@ SK-logic: Eu suspeito que as declarações foram herdadas às cegas da linguagem de máquina, através de FORTRAN, ALGOL e COBOL.
precisa

1
Tenho certeza de que a linguagem de máquina é o ancestral comum, e isso é apenas um reflexo do fato de que os computadores modernos baseados na arquitetura von Neumann executam instruções seqüencialmente e modificam o estado. Por fim, quando o IO acontece, haverá expressões que não produzem dados significativos; portanto, as instruções não são totalmente inúteis para indicar semanticamente que algum código tem apenas efeitos colaterais. Mesmo os idiomas que possuem uma noção de unittipo (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.
Rei Miyasaka

6

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 #.


3
Um dos principais objetivos do C ++ era ser totalmente compatível com o C. Se eles tivessem mudado drasticamente o comportamento semântico, ele pode não ter entendido como o fez (ou pelo menos esse era o pensamento da época)
Ed S.

2
@ Ed S., no entanto, a eliminação do problema "dangling else" poderia ter sido realizada eliminando a produção gramatical <compound_statement> (aka <block>) e incorporando os chavetas nas estruturas de controle condicionais e iterativas, como faziam quando eles adicionada a estrutura de controle de manipulação de exceção try / catch. Não há desculpa para não corrigir essa ambiguidade gramatical em Java e C #. Atualmente, a solução defensiva para essa ambiguidade gramatical é transformar todas as afirmações que seguem uma declaração de controle condicional ou iterativa em uma declaração composta.
bit-twiddler

1
O que você quer dizer com isso? Este não é um "problema gramatical" (é completamente inequívoco). Como você "resolveria" isso? Na verdade, acho as regras em C satisfatórias. Indiscutivelmente, apenas uma sintaxe do tipo Python (= recuo significativo) pode realmente resolver esse problema. Além disso, estou realmente muito feliz que as línguas modernas não exijam chaves. Concordo que todas as sintaxes do tipo C são péssimas, mas pendentes - o resto é o menor de seus problemas.
Konrad Rudolph

1
Continuando: Eu acho que o uso de um recuo pelo Python como meio pelo qual delinear uma lista de declarações é estranho além da crença. Essa técnica viola o princípio da "separação de interesses", acoplando firmemente a verificação lexical à análise de sintaxe. Uma gramática livre de contexto deve poder ser analisada sem saber nada sobre o layout da fonte.
bit-twiddler

3
@ bit-twiddler: Não, não. O lexer Python apenas converte o espaço em branco nos tokens INDENT e DEDENT apropriados. Feito isso, o Python possui uma gramática bastante convencional ( docs.python.org/reference/grammar.html ).
dan04

6

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


1
Como você resolveria isso?
Bjarke Freund-Hansen

Não tenho certeza de que possa ser totalmente resolvido - obviamente, manter as coisas fora da linguagem principal e em uma biblioteca padrão ajuda, então acho que tentaria levar isso o mais longe possível
jk.

1
Compatível com versões anteriores, você quer dizer que os compiladores mais recentes devem poder compilar código antigo?
Rei Miyasaka

Quero dizer, os compiladores mais novos não devem alterar o significado do código antigo, falhar na compilação seria um subconjunto disso
jk.

na maioria dos casos, quando um recurso específico não existe ou deseja mudar, as pessoas criam um novo idioma com base no anterior. C com classes => C ++
umlcat 15/03


4

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

T Parse <T> público estático <T> (Tipo <T>, string str) em que T: Enum
ao lado ou substituindo
objeto estático público Parse (tipo Type, string str)
na Enumclasse permitiria
MyEnum e = Enum.Parse (typeof (MyEnum), str);
ao invés do tautológico
MyEnum e = (MyEnum) Enum.Parse (typeof (MyEnum), str);

tl; 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.


2
A incapacidade de restringir tipos à enumeração é irritante em C #, mas você pode contornar isso dessa maneira. 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.
CodexArcanum

4

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:

void foo()
{
    int a = 8;
    bar(a);
}

Do ponto de chamada, não há como dizer que bar, que pode ser definido em um arquivo completamente diferente, é:

void bar(int& a)
{
    a++;
}

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

bar(&a);

é muito mais legível.


+1 Não concordo com você, mas agradeço seu raciocínio.
Jon Purdy

@ Jon, eu estaria realmente muito interessado no que você pensa. Você usa a visão "não culpe o idioma"?
9111 Jeff

6
@ Jeff: Por um lado, a principal razão pela qual a semântica de referência chegou ao C ++ foi a sobrecarga de operadores, para a qual o comportamento uniforme de referência simplesmente faz sentido. Mais importante, porém, o C ++ foi projetado para ser versátil e fornecer recursos muito detalhados, mesmo que isso acarrete risco significativo de erro do programador. Então sim, pelo menos nesse caso em particular, não culpe o idioma. Prefiro cometer erros do que deixar um idioma atrapalhar.
Jon Purdy

@ Jon concordou, seria muito estranho passar por referência se aplicar a tudo, exceto aos PODs. Eu preferiria que esse recurso estivesse totalmente ausente do C ++ como alternativa, mas podemos concordar em discordar :). Obrigado pela contribuição!
Jeff

Java parece não gostar tanto de ponteiros quanto você.
Mateen Ulhaq

4

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.


Porém, ele possui bons casos de uso - stubbing ou memoization. Em vez de escrever, v() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }você dizv() { result = long(operation); v = () => result; result; }
configurador

4

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:

  1. Estava tão mal definido que não era possível obter um programa para compilar e executar seguindo livros ou exemplos.
  2. Depois de ajustar o programa para compilar e executar em um compilador e sistema operacional, você teria que começar de novo se trocasse de compilador ou plataforma.

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.


1
Não vejo como evitar a falta de definição em uma linguagem em evolução. Ou, nesse caso, em um idioma predefinido em que o designer original perdeu alguns pontos importantes (como Pascal).
David Thornley

@ David Thornley Bem definido é bem definido. Apesar dos erros, a maioria dos designers de linguagens de programação acertam desde o início. As ferramentas podem verificar se uma gramática é inequívoca quando completa (C ++ requer pelo menos três gramáticas), e a semântica deve ser especificada para implementações padrão.
Apalala 13/03

Concordo que linguagens bem definidas são possíveis, mas isso não acontecerá em uma linguagem em evolução como o C ++ pré-padrão. A alternativa é que cada idioma seja completamente projetado antes do lançamento, e essa não é necessariamente a maneira de obter os melhores idiomas. Eu diria que a maioria dos designers de linguagem de programação erra as coisas no início, pois o design de linguagem é extremamente complicado.
David Thornley 13/03

Acho que estou tendo problemas para entender o que você quer dizer com "bem definido". Sua reclamação é que diferentes compiladores C ++ não compilaram o mesmo idioma?
Sean McMillan

3

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.


2
A ocultação de informações é importante porque permite ocultar detalhes específicos da implementação, que provavelmente serão alterados, das partes acessíveis da API (a "interface do usuário" da API), tornando as alterações no programa mais fáceis e menos dolorosas.
Anto

1
A interface do usuário da API ... não, sério, eu não compro.
Jokoon 06/03

3
Essa diferença não é a parte mais revoltante do C ++, nem chega perto. A única diferença é um modificador de acesso padrão (público para estruturas, privado para classes). C ++ é uma linguagem horrível e monstruosa, mas certamente não nesta parte.
SK-logic

sk-logic: bem, eu poderia dizer que os horrores começam por aí.
Jokoon 07/07

2
Ocultar informações é bom; você pode encontrar discussões sobre isso por todo o lado. O único livro de software proeminente em que posso pensar que era contra era "The Mythical Man-Month" de Brooks, e mais tarde ele o considerou o maior erro do livro. Se você não entende as vantagens, não está realmente qualificado para julgar.
11307 David Thornley

3

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:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

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.


A sintaxe do Objective-C é extremamente complexa? Você não viu C ++? E o nome do método real existe tableView:cellForRowAtIndexPath:, o que é muito descritivo na minha opinião.
rightfold

Aprenda a digitar.
finnw

2
  1. caracteres assinados em C - uma abominação inventada para permitir que matemáticos tenham grandes coleções de itens pequenos
  2. Usando case para transportar conteúdo semântico - novamente para matemáticos, que não precisam conversar e nunca têm espaço suficiente para suas fórmulas
  3. Tarefa Let / Plain vs. Set atribuição em dialetos básicos - nenhum matemático envolvido aqui eu acho

Estou perdido quanto ao seu comentário de caracteres assinado, você poderia explicar?
Winston Ewert

1
Para um humano, o conceito de caracteres (não) assinados e a necessidade de dizer ao compilador para usar caracteres não assinados como padrão é certamente tão insano quanto para um matemático a afirmação de que 2! = 2, porque o segundo 2 é maiúsculo e em negrito ou itálico.
precisa

5
O problema é que C confunde o conceito de "char" (ou seja, parte de uma sequência de texto) e "byte" (ou seja (u)int_least8_t). A assinatura faz todo sentido para números inteiros pequenos, mas não faz sentido para caracteres.
dan04

@ dan04: Concordo, gostaria que eles tivessem tipos diferentes para números inteiros pequenos (assinados e não assinados), byte e caractere. Quando você explica aos novatos que, para manipular a memória bruta que eles precisam converter em um char*... como um C-String, eles ficam realmente confusos.
Matthieu M.

C # tem este direito com separadas sbyte, bytee chartipos.
dan04
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.