Tous les langages de programmation ont leurs défauts de conception simplement parce qu’aucun langage ne peut être parfait, tout comme la plupart (toutes?) D’autres choses. Cela mis à part, quel défaut de conception dans un langage de programmation vous a le plus ennuyé tout au long de votre histoire de programmeur?
Notez que si une langue est "mauvaise" simplement parce qu'elle n'est pas conçue pour une chose spécifique, ce n'est pas un défaut de conception, mais une caractéristique de la conception, alors ne listez pas ces ennuis de langues. Si une langue ne convient pas à ce pour quoi elle est conçue, c'est bien sûr un défaut de conception. Les choses spécifiques à la mise en œuvre et les choses sous le capot ne comptent pas non plus.
Réponses:
Un de mes gros ennuis est la façon dont les
switch
cas dans les langages dérivés de C passent par défaut au cas suivant si vous oubliez de les utiliserbreak
. Je comprends que cela est utile dans le code de très bas niveau (par exemple, le périphérique de Duff ), mais il est généralement inapproprié pour le code de niveau application et est une source courante d'erreurs de codage.Je me souviens vers 1995, lorsque je lisais pour la première fois les détails de Java, lorsque je suis arrivé à la partie concernant la
switch
déclaration, j'étais très déçu qu'ils aient conservé le comportement de repli par défaut. Cela fait justeswitch
un glorifiégoto
avec un autre nom.la source
switch
ne doit pas fonctionner de cette façon. Par exemple, l' instruction case / when d' Ada (équivalente à switch / case) n'a pas de comportement d'interruption.switch
instructions similaires à des langages non liés à C ne doivent pas fonctionner de cette façon. Mais si vous utilisez le flux de contrôle de type C ({
...}
,for (i = 0; i < N; ++i)
,return
, etc.), l' inférence linguistique va rendre les gens attendentswitch
à travailler comme C, et en lui donnant Ada / Pascal / BASIC sémantique aurait des gens confus. C # requiertbreak
dans lesswitch
instructions pour la même raison, bien qu'il le rend moins sujet aux erreurs en interdisant les retombées silencieuses. (Mais j'aurais aimé que tufall;
goto case
switch
permette la chute. C'est que la plupart des utilisations de fallthrough ne sont pas intentionnelles.Je n'ai jamais vraiment aimé l'utilisation de
=
pour l'affectation et==
pour les tests d'égalité dans les langages dérivés C. Le risque de confusion et d'erreurs est trop élevé. Et ne me lancez même pas sur===
Javascript.Mieux aurait été
:=
pour les affectations et=
les tests d'égalité. La sémantique aurait pu être exactement la même qu'aujourd'hui, où l'affectation est une expression qui produit également une valeur.la source
x<-5
). Les programmeurs C ne toléreraient pas ce genre d'espace blanc requis :):=
et==
parce qu'il serait trop facile d'oublier le:
et de ne pas être averti comme c'est déjà le cas (bien qu'inversé) lorsque vous oubliez un=
aujourd'hui. Je suis reconnaissant pour les avertissements du compilateur à ce sujet ...=
et==
n'est pas en lecture, car ce sont des symboles distincts. C'est par écrit et s'assurer que vous avez le bon.Le choix de
+
Javascript pour l' addition et la concaténation de chaînes a été une terrible erreur. Étant donné que les valeurs ne sont pas typées, cela conduit à des règles byzantines qui déterminent s'il faut+
ajouter ou concaténer, selon le contenu exact de chaque opérande.Il aurait été facile au début d'introduire un opérateur complètement nouveau comme
$
pour la concaténation de chaînes.la source
+
a beaucoup de sens en tant qu'opérateur de concaténation de chaînes dans un langage fortement typé. Le problème est que Javascript l'utilise mais n'est pas fortement typé.+
n'a pas de sens pour la concaténation, car la concaténation n'est certainement pas commutative. C'est un abus en ce qui me concerne.*
opérateur?Je trouve que Javascript par défaut est global à un problème majeur, et souvent source de bugs si vous n'utilisez pas JSLint ou similaire
la source
var
chaque fois que vous déclarez une variable et vous êtes prêt à partir. Et ne me dites pas que c'est trop de frappe car Java vous oblige à déclarer tous les types deux fois et personne ne se plaint que ce soit un choix de conception merdique.std::map<KEY, VALUE>::const_iterator
suffisamment de variables en C ++ quiauto
sont ajoutées à ce langage."use strict"
directive, ajoutée dans es5, transforme les références non déclarées en erreur.Le préprocesseur en C et C ++ est un kludge massif, crée des abstractions qui fuient comme des tamis, encourage le code spaghetti via les nids de
#ifdef
déclarations de rat , et nécessite desALL_CAPS
noms horriblement illisibles pour contourner ses limites. La racine de ces problèmes est qu'elle opère au niveau textuel plutôt qu'au niveau syntaxique ou sémantique. Il aurait dû être remplacé par de véritables fonctionnalités de langage pour ses différents cas d'utilisation. Voici quelques exemples, bien que certains d'entre eux soient résolus en C ++, C99 ou dans des extensions standard non officielles mais de facto :#include
aurait dû être remplacé par un véritable système de modules.Les fonctions en ligne et les modèles / génériques pourraient remplacer la plupart des cas d'utilisation des appels de fonction.
Une sorte de caractéristique de constante de temps manifeste / compilé pourrait être utilisée pour déclarer de telles constantes. Les extensions d'enum de D fonctionnent très bien ici.
De vraies macros au niveau de l'arbre de syntaxe pourraient résoudre de nombreux cas d'utilisation divers.
Des mixins de chaînes peuvent être utilisés pour le cas d'utilisation de l'injection de code.
static if
ou desversion
instructions peuvent être utilisées pour la compilation conditionnelle.la source
#include
problème, mais le système de modules a été inventé ... après! Et C et C ++ visent un maximum de rétrocompatibilité: /#include
piratage basé sur cpp actuel .On pourrait énumérer des centaines d'erreurs dans des centaines de langues, mais l'OMI n'est pas un exercice utile du point de vue de la conception d'une langue.
Pourquoi?
Parce que quelque chose qui serait une erreur dans une langue ne serait pas une erreur dans une autre langue. Par exemple:
Il y a des leçons à tirer, mais les leçons sont rarement claires, et pour les comprendre, vous devez comprendre les compromis techniques ... et le contexte historique. (Par exemple, l'implémentation lourde de Java des génériques est la conséquence d'une exigence commerciale primordiale pour maintenir la compatibilité descendante.)
OMI, si vous êtes sérieux au sujet de la conception d'une nouvelle langue, vous devez réellement utiliser un large éventail de langues existantes (et étudier les langues historiques) ... et vous faire votre propre idée des erreurs. Et vous devez garder à l'esprit que chacune de ces langues a été conçue dans un contexte historique particulier, pour répondre à un besoin particulier.
S'il y a des leçons générales à tirer, elles sont au niveau "méta":
la source
C et C ++ : tous ces types entiers qui ne veulent rien dire .
Surtout
char
. Est-ce du texte ou un minuscule entier? S'il s'agit de texte, s'agit-il d'un caractère "ANSI" ou d'une unité de code UTF-8? S'il s'agit d'un entier, est-il signé ou non signé?int
était censé être l'entier de taille "native", mais sur les systèmes 64 bits, il ne l'est pas.long
peut ou peut ne pas être plus grand queint
. Il peut ou non avoir la taille d'un pointeur. C'est à peu près une décision arbitraire de la part des rédacteurs du compilateur, que ce soit en 32 bits ou en 64 bits.Certainement une langue des années 1970. Avant Unicode. Avant les ordinateurs 64 bits.
la source
null
.Son inventeur, Tony Hoare, l'appelle "l'erreur du milliard de dollars" .
Il a été introduit dans ALGOL dans les années 60 et existe aujourd'hui dans la plupart des langages de programmation couramment utilisés.
La meilleure alternative, utilisée dans des langages comme OCaml et Haskell, est peut - être . L'idée générale est que les références d'objet ne peuvent pas être nulles / vides / inexistantes à moins qu'il y ait une indication explicite qu'elles peuvent l'être.
(Bien que Tony soit génial dans sa modestie, je pense que presque n'importe qui aurait fait la même erreur, et il s'est avéré être le premier.)
la source
maybe
comme un opt-in nul, alors que l'exception nulle est un opt-out. Bien sûr, il y a certaines choses à dire sur la différence entre les erreurs d'exécution et les erreurs de compilation, mais le fait que null soit essentiellement un comportement d'injection est remarquable en soi.J'ai l'impression que les gens qui ont conçu PHP n'ont pas utilisé de clavier normal, ils n'utilisent même pas de clavier colemak, car ils auraient dû réaliser ce qu'ils faisaient.
Je suis développeur PHP. PHP n'est pas amusant à taper.
Who::in::their::right::mind::would::do::this()
? L'::
opérateur doit maintenir la touche Maj enfoncée, puis appuyer sur deux touches. Quel gaspillage d'énergie.Bien que-> ce-> soit-> pas-> beaucoup-> mieux. Cela nécessite également trois pressions sur les touches, le décalage étant entre les deux symboles.
$last = $we.$have.$the.$dumb.'$'.$character
. Le signe dollar est utilisé énormément de fois et nécessite l'étirement de la récompense jusqu'au sommet du clavier plus une pression sur la touche Maj.Pourquoi n'ont-ils pas pu concevoir PHP pour utiliser des clés beaucoup plus rapides à taper? Pourquoi ne pouvait-on pas
we.do.this()
ou faire démarrer vars avec une clé qui ne nécessite qu'une seule pression sur une touche - ou pas du tout (JavaScript) et juste prédéfinir tous les vars (comme je dois quand même le faire pour E_STRICT)!Je ne suis pas une dactylo lente - mais ce n'est qu'un choix de conception boiteux.
la source
I::have::nothing::against::this->at.all()
L'utilisation de formulaires inspirés du bureau dans asp.net .
Il a toujours ressenti un fudge et a gêné la façon dont le Web fonctionne réellement. Heureusement, asp.net-mvc ne souffre pas de la même manière, mais avec le mérite de Ruby, etc. pour cette inspiration.
la source
Pour moi, c'est le manque absolu de PHP de conventions de nommage et de classement des arguments dans sa bibliothèque standard.
Bien que la nécessité pour JASS d'annuler les références après la libération / suppression de l'objet référencé (ou que la référence fuit et que plusieurs octets de mémoire soient perdus) est plus sérieuse, mais comme JASS est un langage à usage unique, il n'est pas si critique.
la source
Le plus grand défaut de conception auquel je fais face est que python n'a pas été conçu comme python 3.x pour commencer.
la source
Décomposition de tableau en C et par conséquent C ++.
la source
delete
et séparésdelete[]
.Types primitifs en Java.
Ils cassent le principe selon lequel tout est un descendant
java.lang.Object
, ce qui d'un point de vue théorique conduit à une complexité supplémentaire de la spécification du langage, et d'un point de vue pratique, ils rendent l'utilisation des collections extrêmement fastidieuse.Autoboxing a aidé à atténuer les inconvénients pratiques mais au prix de rendre la spécification encore plus compliquée et d'introduire une grosse peau de banane grasse: vous pouvez maintenant obtenir une exception de pointeur nul à partir de ce qui ressemble à une simple opération arithmétique.
la source
Je connais mieux Perl, alors je vais m'en occuper.
Perl a essayé de nombreuses idées. Certains étaient bons. Certains étaient mauvais. Certains étaient originaux et pas largement copiés pour une bonne raison.
L'une est l'idée de contexte - chaque appel de fonction a lieu dans un contexte de liste ou scalaire, et peut faire des choses entièrement différentes dans chaque contexte. Comme je l'ai souligné sur http://use.perl.org/~btilly/journal/36756, cela complique chaque API et conduit souvent à des problèmes de conception subtils dans le code Perl.
Le suivant est l'idée de lier si complètement la syntaxe et les types de données. Cela a conduit à l'invention du lien pour permettre aux objets de se faire passer pour d'autres types de données. (Vous pouvez également obtenir le même effet en utilisant la surcharge, mais l'attachement est l'approche la plus courante en Perl.)
Une autre erreur courante, commise par de nombreuses langues, est de commencer par proposer un cadrage dynamique plutôt que lexical. Il est difficile de revenir plus tard sur cette décision de conception et conduit à des verrues de longue durée. La description classique de ces verrues en Perl est http://perl.plover.com/FAQs/Namespaces.html . Notez que cela a été écrit avant que Perl ait ajouté des
our
variables et desstatic
variables.Les gens sont légitimement en désaccord sur le typage statique contre le typage dynamique. Personnellement, j'aime la frappe dynamique. Cependant, il est important d'avoir une structure suffisante pour permettre la capture des fautes de frappe. Perl 5 fait un bon travail avec strict. Mais Perl 1-4 s'est trompé. Plusieurs autres langues ont des vérificateurs de peluches qui font la même chose que strict. Tant que vous êtes bon dans l'application de la vérification des peluches, cela est acceptable.
Si vous cherchez plus de mauvaises idées (beaucoup d'entre elles), apprenez PHP et étudiez son histoire. Mon erreur passée préférée (corrigée il y a longtemps car elle entraînait tant de failles de sécurité) consistait à autoriser quiconque à définir n'importe quelle variable en passant des paramètres de formulaire. Mais c'est loin d'être la seule erreur.
la source
Ambiguïté JavaScripts pour les blocs de code et les littéraux d'objet.
pourrait être un bloc de code, où
a
est une étiquette etb
est une expression; ou il pourrait définir un objet, avec un attributa
qui a la valeurb
la source
a
.Je vais revenir sur FORTRAN et l'insensibilité des espaces.
Il imprègne la spécification. La
END
carte devait être définie comme une carte avec un «E», un «N» et un «D» dans cet ordre dans les colonnes 7 à 72, et aucun autre non-blanc, plutôt qu'une carte avec «FIN» dans le bon colonnes et rien d'autre.Cela a conduit à une confusion syntaxique facile.
DO 100 I = 1, 10
était une instruction de contrôle de boucle, tandisDO 100 I = 1. 10
qu'une instruction affectait la valeur 1.1 à une variable appeléeDO10I
. (Le fait que des variables puissent être créées sans déclaration, leur type dépendant de leur première lettre, y a contribué.) Contrairement à d'autres langages, il n'y avait aucun moyen d'utiliser des espaces pour séparer les jetons pour permettre la désambiguïsation.Cela a également permis à d'autres personnes d'écrire du code vraiment déroutant. Il y a des raisons pour lesquelles cette fonctionnalité de FORTRAN n'a plus jamais été dupliquée.
la source
(test ? a : b)
, e) insistent lors de l'utilisation**
, f) ne peut pas gérer la casse. La plupart de cela était dû à des raccourcis clavier dans les années 50.L'un des plus gros problèmes avec BASIC était l'absence de méthode bien définie pour étendre le langage au-delà de ses premiers environnements, conduisant à un tas d'implémentations complètement incompatibles (et à une tentative post-facto presque non pertinente de normalisation).
Presque toutes les langues seront utilisées par un programmeur fou. Il est préférable de planifier cet usage général au début au cas où cette idée folle prend son envol.
la source
Je crois aux DSL (domaines spécifiques au domaine) et une chose que j'apprécie dans une langue est si cela me permet de définir un DSL par-dessus.
En Lisp, il existe des macros - la plupart des gens considèrent cela comme une bonne chose, tout comme moi.
En C et C ++, il y a des macros - les gens s'en plaignent, mais j'ai pu les utiliser pour définir des DSL.
En Java, ils ont été laissés de côté (et donc en C #), et leur absence a été déclarée comme une vertu. Bien sûr, cela vous permet d'avoir Intellisense, mais pour moi, ce n'est qu'une œuvre . Pour faire mon DSL, je dois développer à la main. C'est une douleur, et cela me fait ressembler à un mauvais programmeur, même si cela me permet d'en faire beaucoup plus avec des tonnes de code en moins.
la source
cdr
la liste et en ferait une fermeture lambda (c'est-à-dire une continuation) et la passerait en argument àcar
la liste. Cela a été fait de manière récursive, bien sûr, et "ferait la bonne chose" pour les conditions, les boucles et les appels de fonction. Ensuite, la fonction "choix" s'est transformée en boucle normale. Pas joli, mais c'était robuste. Le problème, c'est qu'il est très facile de créer des boucles trop imbriquées.Déclarations , dans toutes les langues qui en ont. Ils ne font rien que vous ne pouvez pas faire avec les expressions et vous empêchent de faire beaucoup de choses. L'existence d'un
?:
opérateur ternaire n'est qu'un exemple de la nécessité de les contourner. En JavaScript, ils sont particulièrement ennuyeux:la source
unit
type (aka()
) au lieu d'instructions ont une attention particulière pour s'assurer qu'ils ne lancent pas d'avertissement ou ne se comportent pas de manière étrange.Pour moi, c'est le problème de conception qui afflige tous les langages dérivés du C; à savoir, le « balançant d'autre ». Ce problème grammatical aurait dû être résolu en C ++, mais il a été appliqué à Java et C #.
la source
Je pense que toutes les réponses jusqu'à présent pointent vers un seul échec de nombreuses langues dominantes:
Il n'y a aucun moyen de changer le langage principal sans affecter la compatibilité descendante.
Si cela est résolu, à peu près tous ces autres reproches peuvent être résolus.
MODIFIER.
cela peut être résolu dans les bibliothèques en ayant différents espaces de noms, et vous pourriez concevoir de faire quelque chose de similaire pour la plupart du cœur d'un langage, bien que cela puisse signifier que vous devez prendre en charge plusieurs compilateurs / interprètes.
En fin de compte, je ne pense pas savoir comment le résoudre d'une manière totalement satisfaisante, mais cela ne signifie pas qu'il n'existe pas de solution, ou que plus ne peut être fait.
la source
Débordement arithmétique d' entier silencieux de Java
la source
Java et C # ont tous deux des problèmes gênants avec leurs systèmes de type en raison du désir de maintenir la compatibilité descendante lors de l'ajout de génériques. Java n'aime pas mélanger les génériques et les tableaux; C # n'autorisera pas certaines signatures utiles car vous ne pouvez pas utiliser des types de valeur comme limites.
À titre d'exemple de ce dernier, considérons que
à côté ou en remplacement dans laEnum
classe permettrait plutôt que tautologiquetl; dr: pensez au polymorphisme paramétrique lorsque vous commencez à concevoir votre système de type, pas après avoir publié la version 1.
la source
MyMethod<T>(T value) where T : struct, IComparable, IFormattable, IConvertible
Mais vous devez toujours tester une énumération et c'est un hack. Je pense que le plus grand manque dans les génériques C # n'est pas un support pour les types supérieurs, ce qui ouvrirait vraiment le langage à certains concepts sympas.J'ai l'impression de m'ouvrir pour être flambé, mais je déteste vraiment la possibilité de passer de vieux types de données simples par référence en C ++. Je déteste seulement légèrement pouvoir passer des types complexes par référence. Si je regarde une fonction:
Du point d'appel, il n'y a aucun moyen de dire que
bar
, qui peut être défini dans un fichier complètement différent, est:Certains pourraient faire valoir que faire quelque chose comme ça peut simplement être une mauvaise conception de logiciel, et ne pas blâmer la langue, mais je n'aime pas que la langue vous permette de le faire en premier lieu. Utiliser un pointeur et appeler
est beaucoup plus lisible.
la source
MODIFIER
Lorsque j'ai appris COBOL, l'instruction ALTER faisait toujours partie de la norme. En résumé, cette instruction vous permet de modifier les appels de procédure pendant l'exécution.
Le danger était que vous puissiez mettre cette déclaration dans une section obscure du code à laquelle on accédait rarement et qui avait le potentiel de changer complètement le flux du reste de votre programme. Avec plusieurs instructions ALTER, vous pourriez rendre presque impossible de savoir ce que faisait votre programme à un moment donné.
Mon professeur d'université, très catégoriquement, a déclaré que s'il voyait cette déclaration dans l'un de nos programmes, il nous ferait automatiquement échouer.
la source
v() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }
vous ditesv() { result = long(operation); v = () => result; result; }
Le pire péché d'un langage de programmation n'est pas bien défini. Un cas dont je me souviens est le C ++, qui, dans ses origines:
Si je me souviens bien, il a fallu environ une décennie pour que le C ++ soit suffisamment bien défini pour le rendre aussi fiable sur le plan professionnel que C. C'est quelque chose qui ne devrait plus jamais se reproduire.
Quelque chose d'autre que je considère comme un péché (devrait-il aller dans une réponse différente?) Est d'avoir plus d'une "meilleure" façon de faire une tâche commune. C'est le cas (encore) de C ++, Perl et Ruby.
la source
Les classes en C ++ sont une sorte de modèle de conception forcée dans le langage.
Il n'y a pratiquement pas de différence au moment de l'exécution entre une structure et une classe, et il est tellement déroutant de comprendre quel est le véritable avantage de programmation de la "dissimulation d'informations" que je souhaite la mettre là.
Je vais être rétrogradé pour cela, mais de toute façon, les compilateurs C ++ sont si difficiles à écrire que ce langage ressemble à un monstre.
la source
Bien que chaque langue ait ses défauts, aucune n'est une nuisance une fois que vous les connaissez. Sauf pour cette paire:
Syntaxe complexe couplée à une API verbeuse
Cela est particulièrement vrai pour un langage comme Objective-C. Non seulement la syntaxe est extrêmement complexe, mais l'API utilise des noms de fonction comme:
Je suis tout à fait explicite et sans ambiguïté, mais c'est ridicule. Chaque fois que je m'assois avec xcode, je me sens comme un n00b, et c'est vraiment frustrant.
la source
tableView:cellForRowAtIndexPath:
, qui est très descriptif à mon avis.la source
(u)int_least8_t
). La signature est parfaitement logique pour les petits entiers, mais n'a aucun sens pour les caractères.char*
... comme une chaîne C, ils deviennent vraiment confus.sbyte
,byte
etchar
types.