¿Cuál es el mayor defecto de diseño que has enfrentado en cualquier lenguaje de programación? [cerrado]


29

Todos los lenguajes de programación tienen sus defectos de diseño simplemente porque ni un solo lenguaje puede ser perfecto, al igual que con la mayoría (¿todo?) De otras cosas. Aparte de eso, ¿qué falla de diseño en un lenguaje de programación te ha molestado más a través de tu historia como programador?

Tenga en cuenta que si un idioma es "malo" solo porque no está diseñado para una cosa específica, no es una falla de diseño, sino una característica del diseño, así que no enumere tales molestias de idiomas. Si un lenguaje no es adecuado para lo que está diseñado, eso es, por supuesto, una falla en el diseño. Implementación de cosas específicas y, bajo el capó, las cosas tampoco cuentan.


66
Tenga en cuenta que esto es constructivo para los diseñadores de idiomas (errores que deben evitarse), si alguien quisiera cuestionar qué tan constructiva es esta pregunta.
Anto


1
@greyfade: En realidad no, se trata de fallas en el idioma real, que parece tratarse de cosas que reducen la adopción de un idioma, que podría incluir una biblioteca estándar mala o simplemente un mal sitio web para el idioma. Algunas respuestas enumeran, por ejemplo, una sintaxis incorrecta, pero esa no es una falla de diseño específica
Anto

8
¿El mayor defecto en cualquier lenguaje de programación? Humanos
Joel Etherton

Si estas respuestas son los mayores defectos, entonces estoy realmente impresionado por los lenguajes de programación.
Tom Hawtin - tackline

Respuestas:


42

Una de mis grandes molestias es la forma switchen que los casos en lenguajes derivados de C pasan por defecto al siguiente caso si olvida usarlos break. Entiendo que esto es útil en código de muy bajo nivel (por ejemplo, el dispositivo de Duff ), pero generalmente no es apropiado para el código de nivel de aplicación y es una fuente común de errores de codificación.

Recuerdo que alrededor de 1995, cuando estaba leyendo sobre los detalles de Java por primera vez, cuando llegué a la parte sobre la switchdeclaración, estaba muy decepcionado de que hubieran conservado el comportamiento de incumplimiento predeterminado. Esto simplemente se convierte switchen un glorificado gotocon otro nombre.


3
@ Christopher Mahan: switchno tiene que funcionar de esa manera. Por ejemplo, la declaración caso / cuándo de Ada (equivalente a cambiar / caso) no tiene un comportamiento de caída.
Greg Hewgill

2
@Greg: las switchdeclaraciones similares en lenguajes no relacionados con C no tienen que funcionar de esa manera. Pero si se utiliza el flujo de control tipo C ( {... }, for (i = 0; i < N; ++i), return, etc.), la inferencia lengua hará que la gente espera switchque el trabajo como C, y dándole Ada / Pascal / personas como BASIC semántica habría confundido. C # requiere breaken las switchdeclaraciones por la misma razón, aunque lo hace menos propenso a errores al prohibir la caída silenciosa. (Pero desearía que pudieras escribir en fall;lugar de lo feo goto case.)
dan04

44
entonces no escriba switch, escriba if () más. la razón por la que se queja es lo que hace que el cambio sea mejor: no es un condicional verdadero / falso, es un condicional numérico y eso lo hace diferente. Sin embargo, también podría escribir su propia función de cambio.
jokoon

9
-1 Considero que es un beneficio permitir la caída, no hay más peligro que la estupidez, pero otorga una función adicional.
Orbling

11
El problema no es que switch permita fallthrough. Es que la mayoría de los usos de fallthrough no son intencionales.
dan04

41

Nunca me ha gustado mucho el uso de =para asignación y ==para pruebas de igualdad en lenguajes derivados de C. El potencial de confusión y errores es demasiado alto. Y ni siquiera me hagas comenzar con ===Javascript.

Mejor hubiera sido :=por asignación y =por pruebas de igualdad. La semántica podría haber sido exactamente la misma que en la actualidad, donde la asignación es una expresión que también produce un valor.


3
@Nemanja Trifunovic: Originalmente pensé en esa sugerencia, pero tiene una ambigüedad desafortunada en C con una comparación menor que un número negativo (es decir x<-5). Los programadores de C no tolerarían ese tipo de espacio en blanco requerido :)
Greg Hewgill

77
@Greg: Preferiría :=y ==porque sería demasiado fácil olvidarlo :y no ser notificado como ya es el caso (aunque revertido) cuando olvidas un =hoy. Estoy agradecido por las advertencias del compilador sobre esto ...
Matthieu M.

13
En casi todos los teclados que he usado, ": =" requiere cambiar la tecla Mayús mientras se escribe. En el que estoy usando ahora, ':' está en mayúsculas y '=' está en minúsculas, y he invertido eso. Escribo muchas tareas, y no necesito ese tipo de problemas de escritura.
David Thornley

14
@David Thornley: el código se lee muchas más veces de lo que se escribe. No compro ningún argumento sobre "molestias al escribir".
Greg Hewgill

8
@ Greg Hewgill: Claro, se lee con más frecuencia que se escribe. Sin embargo, el problema entre =y ==no está en la lectura, porque son símbolos distintos. Está por escrito y asegurándose de tener el correcto.
David Thornley

29

La elección de +Javascript para la adición y la concatenación de cadenas fue un terrible error. Como los valores no están tipificados, esto lleva a reglas bizantinas que determinan si +se agregarán o concatenarán, dependiendo del contenido exacto de cada operando.

Hubiera sido fácil al principio introducir un operador completamente nuevo, como la $concatenación de cadenas.


13
@Barry: En realidad no. +tiene mucho sentido como operador de concatenación de cadenas en un lenguaje fuertemente tipado. El problema es que Javascript lo usa pero no está fuertemente tipado.
Mason Wheeler

13
@Mason: +no tiene sentido para la concatenación, porque la concatenación definitivamente no es conmutativa. Es un abuso en lo que a mí respecta.
Matthieu M.

12
@ Matthieu: Umm ... ¿por qué eso importa? La concatenación no es conmutativa (como lo es la suma), pero la adición de dos cadenas no tiene sentido, por lo que nadie piensa de esa manera. Estás inventando un problema donde no existe ninguno.
Mason Wheeler

44
simplemente cambie a C ++ y agregue cualquier tipo de sobrecarga esotérica al operador que elija
Newtopian

8
@ Matthieu: La conmutatividad no es el problema aquí. Si JS tuviera una clase Matrix, ¿consideraría un abuso tener un *operador?
dan04

24

Encuentro Javascript predeterminado en global a un problema importante, y a menudo fuente de errores si no usa JSLint o similares


2
¿Predeterminado a global? Me alegro de no usar JS entonces ...
Anto

55
En realidad, es un lenguaje muy agradable, con algunas malas elecciones de diseño. Este es el principal, si no establece una variable, la definirá como global. Lo bueno es que al usar el programa Jslint de Doug Crockford puede detectar este error y muchos más.
Zachary K

1
Solo utilícelo varcuando declare una variable y está listo para comenzar. Y no me digas que es demasiado escribir porque Java te obliga a declarar todos los tipos dos veces y nadie se queja de que sea una elección de diseño de mierda.
davidk01

3
@ davidk01: La gente se queja de eso. Del mismo modo, las personas se quejaron de tener que declarar std::map<KEY, VALUE>::const_iteratorvariables en C ++ lo suficiente autocomo para agregarlas a ese lenguaje.
dan04

1
La "use strict"directiva, agregada en es5, cambia las referencias no declaradas en un error.
Sean McMillan

22

El preprocesador en C y C ++ es un error masivo, crea abstracciones que se filtran como tamices, fomenta el código de espagueti a través de nidos de #ifdefdeclaraciones de ratas y requiere ALL_CAPSnombres horriblemente ilegibles para solucionar sus limitaciones. La raíz de estos problemas es que opera a nivel textual más que a nivel sintáctico o semántico. Debería haber sido reemplazado con características de lenguaje real para sus diversos casos de uso. Estos son algunos ejemplos, aunque es cierto que algunos de estos se resuelven en C ++, C99 o extensiones estándar no oficiales pero de facto :

  • #include debería haber sido reemplazado por un sistema de módulo real.

  • Las funciones en línea y las plantillas / genéricos podrían reemplazar la mayoría de los casos de uso de llamadas a funciones.

  • Se podría utilizar algún tipo de característica constante de tiempo de manifiesto / compilación para declarar tales constantes. Las extensiones de D de enum funcionan muy bien aquí.

  • Las macros de nivel de árbol de sintaxis real podrían resolver muchos casos de uso diversos.

  • Los mixins de cadena podrían usarse para el caso de uso de inyección de código.

  • static ifo las versiondeclaraciones podrían usarse para compilación condicional.


2
@dsimcha: Estoy de acuerdo con el #includeproblema, pero el sistema de módulos fue inventado ... ¡luego! Y C y C ++ apuntan a un máximo de compatibilidad con versiones anteriores: /
Matthieu M.

@Matthieu: Sí, los sistemas de módulos se inventaron después, pero de todos modos estamos hablando de retrospectiva aquí.
dsimcha

3
Estoy de acuerdo en que esto es un dolor enorme en varias partes del cuerpo, pero el diseño de múltiples pasos tiene la ventaja de la simplicidad: un compilador de C no necesita saber mucho sobre el contexto de operación antes de que pueda compilar con éxito un fragmento de código C . Esto solo puede calificarse como un error de diseño si puede demostrar que los costos de usar un hipotético sistema de módulos dentro del lenguaje C en sí mismo (por ejemplo, clases similares a C ++) siempre son inferiores o comparables al actual #includepirateo basado en cpp .
reinierpost

Estoy de acuerdo. Sin embargo, algunas personas piensan que el preprocesamiento es algo bueno y se quejan de que no es compatible con (por ejemplo) Java.
Stephen C

55
@Stephen: estoy de acuerdo en que Java con un preprocesador podría ser mejor que Java sin él, pero solo porque Java no tiene varias de las características "reales" necesarias para reemplazar el preprocesador. En lenguajes como D, que incluyen tales características y Python, que obtiene flexibilidad de otras maneras al ser dinámico, no me lo pierdo ni un poco.
dsimcha

21

Uno podría enumerar cientos de errores en cientos de idiomas, pero en mi opinión, no es un ejercicio útil desde la perspectiva del diseño del lenguaje.

¿Por qué?

Porque algo que sería un error en un idioma no sería un error en otro idioma. Por ejemplo:

  • Hacer de C un lenguaje administrado (es decir, recolectar basura) o vincular los tipos primitivos limitaría su utilidad como lenguaje de bajo nivel semi-portátil.
  • Agregar administración de memoria de estilo C a Java (por ejemplo, para abordar problemas de rendimiento) lo rompería.

No son lecciones que aprender, pero las lecciones se cortan raramente clara, y para entenderlos hay que entender las ventajas y desventajas técnicas ... y el contexto histórico. (Por ejemplo, la engorrosa implementación de genéricos en Java es consecuencia de un requisito comercial primordial para mantener la compatibilidad con versiones anteriores).

En mi opinión, si te tomas en serio el diseño de un nuevo idioma, debes utilizar una amplia gama de idiomas existentes (y estudiar idiomas históricos) ... y decidir cuáles son los errores. Y debe tener en cuenta que cada uno de estos idiomas fue diseñado en un contexto histórico particular, para satisfacer una necesidad particular.

Si hay lecciones generales que aprender, están en el nivel "meta":

  • No puede diseñar un lenguaje de programación que sea ideal para todos los propósitos.
  • No puede evitar cometer errores ... especialmente cuando se ve desde atrás.
  • Muchos errores son dolorosos de corregir ... para los usuarios de su idioma.
  • Debe tener en cuenta los antecedentes y las habilidades de su público objetivo; es decir, programadores existentes.
  • No se puede complacer a todo el mundo.

9
-1: los lenguajes de programación siguen los objetivos de diseño. Una característica de un lenguaje que funciona en contra de estos objetivos es una falla de diseño, a menos que sea un compromiso necesario para uno de sus otros objetivos. No se hacen muchos idiomas con la intención de satisfacer a todos, pero todos los idiomas deben intentar satisfacer a las personas que se propone satisfacer en primer lugar. Este tipo de corrección política posmoderna es bastante sofocante para la investigación y el desarrollo del lenguaje de programación.
Rei Miyasaka

Mi punto es que es difícil aprender lecciones sin tener en cuenta los objetivos de diseño.
Stephen C

1
Me parece que el OP ya explicó su respuesta en el segundo párrafo de la pregunta.
Aidan Cully

20

C y C ++ : todos esos tipos enteros que no significan nada.

Especialmente char. ¿Es texto o es un número entero pequeño? Si es texto, ¿es un carácter "ANSI" o una unidad de código UTF-8? Si es un entero, ¿está firmado o no?

int estaba destinado a ser el número entero "nativo", pero en los sistemas de 64 bits, no lo es.

longpuede o no ser mayor que int. Puede o no ser del tamaño de un puntero. Es casi una decisión arbitraria por parte de los escritores del compilador si es de 32 bits o de 64 bits.

Definitivamente un lenguaje de los años setenta. Antes de Unicode. Antes de las computadoras de 64 bits.


10
C no fue diseñado para ser un lenguaje estándar para todos, por lo tanto, no puede ser un error de diseño. Fue diseñado para modelar una CPU como ensamblador portátil evitando el código de ensamblador específico de la CPU. Sin embargo, se solucionó en Java.

77
Creo que el mayor problema son los idiomas más nuevos que continúan usando los mismos términos sin sentido, a pesar de que la historia nos muestra que es una idea terrible. Al menos los chicos C notaron su error y crearon tipos int estándar.
Mark H

55
Un char no es una unidad utf-8. Un personaje utf-8 puede tomar más de 8 bits para almacenar. C no es un lenguaje de la década de 1970, lo estoy usando para un proyecto ahora (voluntariamente).
dan_waterworth

44
C es poco más que una abstracción de alto nivel del procesador PDP-11. Por ejemplo, el incremento previo y posterior fueron apoyados directamente por el PDP-11.
bit-twiddler

55
Esta es una respuesta terriblemente equivocada. En primer lugar, C y C ++ no son intercambiables. En segundo lugar, la especificación del lenguaje define claramente qué es un carácter: un objeto declarado como tipo carácter es lo suficientemente grande como para almacenar cualquier miembro del conjunto de caracteres de ejecución básico. . Tercero, C no es un "lenguaje de los años 70", es un lenguaje que vive cerca del hardware y es probablemente el lenguaje que finalmente permite que todas sus abstracciones de alto nivel tengan sentido para una CPU. Dejas de ser una persona que solo conoce idiomas de alto nivel y no aprecia cómo funcionan las cosas. -1
Ed S.

18

null.

Su inventor, Tony Hoare, lo llama el "error de mil millones de dólares" .

Fue introducido en ALGOL en los años 60, y existe en la mayoría de los lenguajes de programación de uso común en la actualidad.

La mejor alternativa, utilizada en lenguajes como OCaml y Haskell, es quizás . La idea general es que las referencias a objetos no pueden ser nulas / vacías / inexistentes a menos que haya una indicación explícita de que pueden serlo.

(Aunque Tony es asombroso en su modestia, creo que casi cualquiera habría cometido el mismo error, y resultó ser el primero).


En desacuerdo, incluso con su inventor !!! nulo es el valor vacío para el puntero / tipo de datos de referencia. Las cadenas tienen una cadena vacía, los conjuntos tienen un conjunto vacío (Pascal conjunto vacío = []), los enteros tienen 0. La mayoría de los lenguajes de programación que usan nulo / nulo / lo que sea, si una variable se asigna correctamente, se puede evitar un error nulo.
umlcat

44
@ user14579: todos los idiomas que admiten cualquier tipo de conjunto, cadena o matriz tienen {}, pero aún así es semánticamente apropiado y no se bloqueará a menos que ya tenga algo que pueda causar un error en los límites de la matriz, pero ese es otro problema. Puede procesar una cadena vacía para poner en mayúscula todos los caracteres, lo que dará como resultado una cadena vacía. Intenta lo mismo en una cadena nula, y sin la debida consideración, se bloqueará. El problema es que esta consideración adecuada es tediosa, a menudo olvidada, y dificulta la escritura de funciones de expresión única (es decir, lambdas).
Rei Miyasaka

1
Gano dinero cada vez que escribo nulo ... oh, claro, alguien pierde dinero cada vez que escribo nulo. Excepto esta vez.
kevpie

3
@umlcat: cuando tiene idiomas con coincidencia de patrones como Ocaml, Haskell y F #, usando el botón Quizás x | Ninguno de los patrones le impide olvidar el caso nulo en tiempo de compilación. Ninguna cantidad de trucos en tiempo de compilación puede detectar un error en idiomas donde null es el idioma establecido. Dado que tiene que elegir explícitamente no tratar el caso nulo en los idiomas que tienen la mónada Maybe y Some, tienen una gran ventaja sobre el enfoque "nulo".
JasonTrue

1
@ Jason: me gusta pensar que maybees una opción nula, mientras que la excepción nula es una opción nula. Por supuesto, hay algunas cosas que decir sobre la diferencia entre los errores de tiempo de ejecución y los errores de tiempo de compilación también, pero el hecho de que nulo es esencialmente un comportamiento de inyección es notable en sí mismo.
Rei Miyasaka

14

Tengo la sensación de que las personas que diseñaron PHP no usaron un teclado normal, ni siquiera usan un teclado colemak, porque deberían haberse dado cuenta de lo que estaban haciendo.

Soy un desarrollador de PHP. PHP no es divertido de escribir.

Who::in::their::right::mind::would::do::this()? El ::operador requiere mantener presionada la tecla shift y luego presionar dos teclas. Que desperdicio de energía.

Aunque-> esto-> es-> no-> mucho-> mejor. Eso también requiere tres pulsaciones de teclas con el cambio entre los dos símbolos.

$last = $we.$have.$the.$dumb.'$'.$character. El signo de dólar se usa una cantidad tremenda de veces y requiere que el premio se extienda hasta la parte superior del teclado más una tecla shift.

¿Por qué no podrían diseñar PHP para usar teclas que son mucho más rápidas de escribir? ¿Por qué no podían we.do.this()o los vars comenzaban con una tecla que solo requería una sola pulsación de tecla, o ninguna (JavaScript) y simplemente predefinía todos los vars (como tengo que hacer para E_STRICT de todos modos)!

No soy un mecanógrafo lento, pero esta es solo una elección de diseño poco convincente.


Perl comparte este dolor.
Daenyth

2
También lo hace C ++, y por alguna extraña razón inexplicable, PowerShell.
Rei Miyasaka

2
Utilice un editor más potente y defina sus propias secuencias de teclas para esos operadores.
Kevin Cline

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

1
Tal vez no usan teclados qwerty. $ y: no es necesario presionar la tecla Mayús en todos los teclados como la azertía.
Arkh

13

El uso de formularios inspirados en el escritorio dentro de asp.net .

Siempre se sintió un dulce y se interpuso en el camino o cómo funciona realmente la web. Afortunadamente asp.net-mvc no sufre de la misma manera, aunque con crédito a Ruby, etc. por esa inspiración.


18
¿No es eso una biblioteca?
Nikkie

@nikie ese es un buen punto;)
paloma

1
@nikie En realidad, el código ASPX basado en XML es un lenguaje, por lo que puede modificarlo para que funcione. : D
CodexArcanum

2
El verdadero problema con ASP.NET, creo, es cuán duro trata de ocultar los detalles de la web del programador. En realidad, están sucediendo algunas cosas realmente buenas y útiles en ASP.NET, pero tienes que luchar muy duro y cavar tan profundo para llegar a eso.
CodexArcanum

1
Por otro lado, hay miles y miles de aplicaciones de recopilación de datos simples y exitosas que se combinaron con la aplicación de escritorio "clásica". Lo único malo fue que hasta MVC la única opción de Microsoft eran los formularios de Windows.
ElGringoGrande

13

Para mí es la absoluta falta de convenciones de ordenamiento de nombres y argumentos de PHP en su biblioteca estándar.

Aunque la necesidad de JASS de anular referencias después de que el objeto referenciado fue liberado / eliminado (o la referencia se filtraría y se perderían varios bytes de memoria) es más grave, pero dado que JASS es un lenguaje de propósito único, no es tan crítico.


9
La falta de convenciones en stdlib de PHP es discutible, no una falla de diseño de lenguaje .

3
@delnan: La falta de convenciones es el resultado de cómo se diseñó PHP y, por lo tanto, tiene mucho que ver con el diseño del lenguaje. Tampoco está claro para mí que haya una distinción clara entre bibliotecas e idioma. Lisp, en particular, tiene una orgullosa tradición de poner un idioma encima de otro.
btilly

1
¡Lo realmente notable de JASS fue que tenía referencias contando con manijas, pero no las limpiaba a menos que fueran destruidas manualmente (y la interfaz gráfica creaba funciones que perdían memoria en todas partes)!
Craig Gidney

13

El mayor defecto de diseño que enfrento es que Python no fue diseñado como Python 3.x para empezar.


1
Bueno, ni siquiera Guido puede hacer todo bien a la vez ...

55
@delnan, oh lo sé, y python <3 sigue siendo un lenguaje asombrosamente bueno, pero es un poco molesto tener un mejor lenguaje en forma de python 3.x que no puedo usar porque rompe todos los módulos que Necesito.
dan_waterworth

¡Continúe presionando para sus módulos python 3.x! Mientras tanto, seguiré escribiendo en 2.5.4. Gracias a SO, me recuerda que 3.x está vivo y bien.
kevpie

@kevpie Primero, haga lobby para agregar una compilación condicional a Python para facilitar la transición a los mantenedores de la biblioteca. 2to3 no es una solución mantenible a largo plazo.
Evan Plaice

12

Array decaimiento en C y, en consecuencia, C ++.


Deseo un soporte de matriz adecuado también. En C ++ puede evitar la descomposición utilizando la sintaxis y las plantillas de abscons ... pero es más un truco: /
Matthieu M.

1
Tenga en cuenta que esta es la razón por la que C ++ tiene que tener operadores separados deletey delete[].
dan04

Siempre puede colocar una matriz en una estructura y hacerla pasar por valor si lo desea, pero esto suele ser más incómodo que el problema original. En C ++, generalmente puede evitar la necesidad de usar matrices.
David Thornley

2
Al menos en el caso de C, el argumento en contra del soporte adecuado de la matriz es "la verificación de los límites de la matriz es costosa", particularmente dada la forma en que funciona la aritmética del puntero C.
Stephen C

@Stephen C: ¿Qué verificación de límites de matriz tiene que ver con la descomposición de la matriz?
Nemanja Trifunovic

11

Tipos primitivos en Java.

Rompen el principio de que todo es un descendiente java.lang.Object, lo que desde un punto de vista teórico conduce a una complejidad adicional de la especificación del lenguaje, y desde una perspectiva práctica hacen que el uso de colecciones sea extremadamente tedioso.

El autoboxing ayudó a aliviar los inconvenientes prácticos, pero a costa de hacer que la especificación sea aún más complicada e introducir una piel de plátano grande y gorda: ahora puede obtener una excepción de puntero nulo de lo que parece una operación aritmética simple.


Causaría un terrible problema de rendimiento si eliminara los tipos primitivos. Y el autoboxing puede estropearse con bastante facilidad, por lo que no mejora nada.
deadalnix 05 de

En ese momento, esta fue una decisión sensata de los diseñadores de Java. Las ganancias de rendimiento dadas las máquinas / máquinas virtuales disponibles en los años 90 superaron las ventajas conceptuales de unificar todo alrededor de java.lang.Object.
mikera

10

Conozco a Perl mejor, así que lo elegiré.

Perl probó muchas ideas. Algunos fueron buenos. Algunos fueron malos. Algunos eran originales y no se copiaban ampliamente por una buena razón.

Una es la idea del contexto : cada llamada a una función tiene lugar en un contexto de lista o escalar, y puede hacer cosas completamente diferentes en cada contexto. Como señalé en http://use.perl.org/~btilly/journal/36756, esto complica cada API y con frecuencia conduce a problemas sutiles de diseño en el código Perl.

La siguiente es la idea de vincular completamente la sintaxis y los tipos de datos. Esto condujo a la invención de la vinculación para permitir que los objetos se enmascaren como otros tipos de datos. (También puede lograr el mismo efecto usando la sobrecarga, pero el empate es el enfoque más común en Perl).

Otro error común, cometido por muchos idiomas, es comenzar ofreciendo un alcance dinámico en lugar de léxico. Es difícil revertir esta decisión de diseño más tarde, y conduce a verrugas duraderas. La descripción clásica de esas verrugas en Perl es http://perl.plover.com/FAQs/Namespaces.html . Tenga en cuenta que esto se escribió antes de que Perl agregara ourvariables y staticvariables.

La gente está legítimamente en desacuerdo con la escritura estática versus la dinámica. Personalmente me gusta la escritura dinámica. Sin embargo, es importante tener una estructura suficiente para que los errores tipográficos sean detectados. Perl 5 hace un buen trabajo de esto con estricto. Pero Perl 1-4 se equivocó. Varios otros idiomas tienen correctores de pelusa que hacen lo mismo que estricto. Siempre que sea bueno para hacer cumplir la verificación de pelusas, eso es aceptable.

Si está buscando más ideas malas (muchas de ellas), aprenda PHP y estudie su historia. Mi error pasado favorito (solucionado hace mucho tiempo porque conducía a tantos agujeros de seguridad) fue dejar de permitir que cualquiera establezca cualquier variable pasando parámetros de formulario. Pero eso está lejos de ser el único error.


55
Sí, Perl tiene muchos errores, porque la gente que lo construyó estaba probando nuevas ideas, y cuando haces eso, a menudo te equivocas. (Perl también tiene algunas cosas muy buenas, y es el estándar para Regexps que todos los demás parecen haber copiado)
Zachary K

@ zachary-k: absolutamente de acuerdo. Y traté de aclarar eso antes de comenzar a analizar los problemas.
btilly

44
Los Lisps originalmente tenían un alcance dinámico y, con el tiempo, cambiaron a un alcance léxico (al menos en Scheme y Common Lisp). No es imposible cambiar.
David Thornley

44
@ david-thornley: es imposible a menos que sacrifiques la compatibilidad con versiones anteriores en alguna parte. El esquema siempre tuvo un alcance léxico. Common Lisp tuvo un alcance léxico desde el momento en que se estandarizó, pero varias comunidades de Lisp tuvieron dificultades para adoptarlo. Y Emacs Lisp todavía está utilizando el alcance dinámico, a pesar de que ha habido un deseo de cambiarlo durante mucho tiempo.
btilly

1
Por cierto, muchas de las cosas que a la gente le disgusta Perl no fueron inventadas en Perl sino tomadas de otros idiomas, principalmente el shell Bourne.
reinierpost

10

Ambigüedad de JavaScripts para bloques de código y literales de objeto.

  {a:b}

podría ser un bloque de código, donde aes una etiqueta y bes una expresión; o podría definir un objeto, con un atributo aque tenga el valorb


De hecho, me gusta esto sobre JavaScript. La simplicidad de la estructura del lenguaje es agradable y el propósito debe ser evidente si el desarrollador sabe lo que está haciendo.
Xeoncross

2
Xeoncross: Generalmente no me gustan las ambigüedades. En este caso, es obvio para el desarrollador, pero eval () necesita paréntesis adicionales.
user281377

2
@ammoQ: ¿Es obvio? ¿Entonces que es? ¿Un objeto o un bloque de código?
configurador

configurador: Obviamente un objeto. Ninguna persona cuerda usaría una etiqueta llamada a.
user281377

10

Voy a volver a FORTRAN y la insensibilidad a los espacios en blanco.

Permeó la especificación. La ENDtarjeta tenía que definirse como una tarjeta con una 'E', una 'N' y una 'D' en ese orden en las columnas 7-72, y no otros espacios en blanco, en lugar de una tarjeta con "FIN" en el columnas y nada más.

Condujo a una fácil confusión sintáctica. DO 100 I = 1, 10era una declaración de control de bucle, mientras que DO 100 I = 1. 10era una declaración que asignaba el valor 1.1 a una variable llamada DO10I. (El hecho de que las variables se pudieran crear sin declaración, su tipo dependiendo de su primera letra, contribuyó a esto). A diferencia de otros idiomas, no había forma de usar espacios para separar los tokens para permitir la desambiguación.

También permitió a otras personas escribir código realmente confuso. Hay razones por las que esta característica de FORTRAN nunca se volvió a duplicar.


1
En un lenguaje en el que puedes redefinir los literales, ese es el peor ejemplo: quiero decir, solo causó el choque de una pequeña y pequeña nave espacial
Martin Beckett,

Al principio, podrías escribir DAMNATION en lugar de DIMENSION, y funcionaría.
Mike Dunlavey

Todavía lo están enseñando personas ajenas a la CS. Todavía tengo que tratar con aquellos que a) luchan contra las declaraciones, b) luchan contra los espacios en blanco, c) como "líneas de continuación", d) usan nombres de 6 caracteres, o 4, d) quedan perplejos cuando ven (test ? a : b), e) insisten sobre el uso **, f) no puede manejar mayúsculas y minúsculas. La mayor parte de esto se debió a golpes de teclado en los años 50.
Mike Dunlavey

1
@ Martin Beckett: redefinir los literales en FORTRAN fue realmente una falla del compilador en lugar de una característica del lenguaje. Ciertamente no fue una característica intencional del lenguaje.
Stephen C

1
@oosterwal: Ciertamente lo hice. Podría estar equivocado, pero recuerdo vagamente la definición del lenguaje basada en tarjetas perforadas. Eran la forma principal de ingresar programas FORTRAN en aquel entonces, y la idea de una línea de 80 columnas con las columnas 73-80 reservadas es de tarjetas perforadas.
David Thornley

9

Uno de los mayores problemas con BASIC fue la falta de un método bien definido para extender el lenguaje más allá de sus entornos iniciales, lo que condujo a un montón de implementaciones completamente incompatibles (y un intento post facto casi irrelevante de cualquier estandarización).

Casi cualquier lenguaje se doblará en el uso general por algún programador loco. Es mejor planificar para ese uso general al principio en caso de que la idea loca despegue.


2
+1: Cualquier idioma sin módulos y bibliotecas adecuados es un error esperando a suceder. COBOL también sufrió esto, lo que condujo a variantes peculiares que no son compatibles.
S.Lott

8

Creo en los DSL (lenguajes específicos de dominio) y una cosa que valoro en un idioma es si me permite definir un DSL encima.

En Lisp hay macros, la mayoría de las personas consideran esto algo bueno, como yo.

En C y C ++ hay macros; las personas se quejan de ellas, pero pude usarlas para definir DSL.

En Java, se excluyeron (y, por lo tanto, en C #), y la falta de ellos se declaró una virtud. Claro que te permite tener inteligencia, pero para mí eso es solo una obra . Para hacer mi DSL, tengo que expandirme a mano. Es un dolor, y me hace ver como un mal programador, a pesar de que me permite hacer mucho más con toneladas de código.


44
Estoy de acuerdo en que cualquier lenguaje sin macros decentes es un gran defecto de diseño no reparable. Pero, ¿a qué te refieres con " quedaron fuera "? El preprocesador C no era ningún tipo de sistema macro decente. Java no se deriva de ningún lenguaje apropiado con macros.
SK-logic

1
Puede escribir su DSL en un lenguaje de procesamiento de macro externo (como m4, por ejemplo, entre una miríada de otros).
SOLO MI OPINIÓN correcta

44
@SK: No diré que el preprocesador C es un sistema macro decente en comparación con Lisp (por ejemplo). Pero, comparado con nada , es extremadamente útil.
Mike Dunlavey

44
@reinierpost: Estoy pensando en cosas que podría hacer en Lisp, como introducir estructuras de control como ejecución diferencial y retroceso. Esto podría hacerse con macros Lisp. En C / C ++ podría hacer una ejecución diferencial con macros C (y un poco de disciplina de programador), pero no retroceder. Con C # no puedo hacer nada. Lo que obtengo a cambio es cosas como intellisense. BFD
Mike Dunlavey

1
@David: La forma en que lo hice fue que tenía una macro para envolver el código ordinario, como una lista de declaraciones. Tomaría el cdrde la lista y formaría un cierre lambda fuera de él (es decir, una continuación) y lo pasaría como un argumento al carde la lista. Eso se hizo recursivamente, por supuesto, y "haría lo correcto" para condicionales, bucles y llamadas a funciones. Entonces la función "elección" se convirtió en un bucle normal. No es bonito, pero era robusto. El problema es que hace que sea muy fácil crear bucles demasiado anidados.
Mike Dunlavey

7

Declaraciones , en todos los idiomas que las tiene. No hacen nada que no puedas hacer con expresiones y te impiden hacer muchas cosas. La existencia de un ?:operador ternario es solo un ejemplo de tener que tratar de sortearlos. En JavaScript, son particularmente molestos:

// 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')

Estoy confundido aquí: ¿solo quieres una forma más simple de hacer las cosas?
TheLQ

2
Correcto. Expresiones para todo.
munificente

1
Lisp funciona bien para esto.
David Thornley

1
@ SK-logic: sospecho que las declaraciones se heredaron ciegamente del lenguaje de máquina, a través de FORTRAN, ALGOL y COBOL.
David Thornley

1
Estoy bastante seguro de que el lenguaje de máquina es el ancestro común, y eso es solo un reflejo del hecho de que las computadoras modernas basadas en la arquitectura von Neumann ejecutan instrucciones secuencialmente y modifican el estado. En última instancia, cuando ocurre IO, habrá expresiones que no arrojarán datos significativos, por lo que las declaraciones no son completamente inútiles para indicar semánticamente que algunos códigos solo tienen efectos secundarios. Incluso los lenguajes que tienen una noción de unittipo (aka ()) en lugar de declaraciones tienen especial consideración para asegurarse de que no arrojen advertencias o se comporten de manera extraña.
Rei Miyasaka

6

Para mí, es el problema del diseño el que afecta a todos los lenguajes derivados de C; a saber, el " colgar más ". Este problema gramatical debería haberse resuelto en C ++, pero se llevó a cabo en Java y C #.


3
Uno de los objetivos centrales de C ++ era ser totalmente compatible con la versión anterior de C. Si hubieran cambiado drásticamente el comportamiento semántico, es posible que no se haya entendido como lo hizo (o al menos, ese era el pensamiento en ese momento)
Ed S.

2
@Ed S., sin embargo, la eliminación del problema de "colgar otro" podría haberse logrado eliminando la producción gramatical <compound_statement> (también conocido como <bloque>) e incorporando las llaves en las estructuras de control condicionales e iterativas como lo hicieron cuando Se agregó la estructura de control de manejo de excepciones try / catch. No hay excusa para no rectificar esta ambigüedad gramatical en Java y C #. Actualmente, el trabajo defensivo para esta ambigüedad gramatical es hacer que cada declaración que sigue a una declaración de control condicional o iterativa sea una declaración compuesta.
bit-twiddler

1
¿Qué quieres decir con eso? Este no es un "problema gramatical" (es completamente inequívoco). ¿Cómo lo "resolverías"? De hecho, las reglas en C me parecen satisfactorias. Podría decirse que solo una sintaxis similar a Python (= sangría significativa) realmente puede resolver este problema. Además, estoy muy contento de que los idiomas modernos no exijan llaves. Estoy de acuerdo en que todas las sintaxis tipo C apestan, pero colgar, de lo contrario, es el menor de sus problemas.
Konrad Rudolph

1
Continuando: Creo que el uso que hace Python de una muesca como medio para delinear una lista de declaraciones es raro más allá de lo creíble. Esta técnica viola el principio de "separación de preocupaciones" al acoplar estrechamente el escaneo léxico con el análisis de sintaxis. Una gramática libre de contexto debe poder analizarse sin saber nada sobre el diseño de la fuente.
bit-twiddler

3
@ bit-twiddler: No, no lo hace. El lexer de Python simplemente convierte el espacio en blanco en los tokens INDENT y DEDENT apropiados. Una vez hecho esto, Python tiene una gramática bastante convencional ( docs.python.org/reference/grammar.html ).
dan04

6

Creo que todas las respuestas hasta ahora apuntan a un solo fallo de muchos idiomas principales:

No hay forma de cambiar el idioma principal sin afectar la compatibilidad con versiones anteriores.

Si esto se resuelve, entonces se pueden resolver casi todas esas otras quejas.

EDITAR.

Esto puede resolverse en bibliotecas al tener diferentes espacios de nombres, y podría concebir hacer algo similar para la mayor parte del núcleo de un lenguaje, aunque esto podría significar que necesita admitir múltiples compiladores / intérpretes.

En última instancia, no creo que sepa cómo resolverlo de una manera totalmente satisfactoria, pero eso no significa que no exista una solución o que no se pueda hacer más.


1
¿Cómo harías para "resolver" esto?
Bjarke Freund-Hansen

No estoy seguro de que pueda resolverse por completo; obviamente, mantener las cosas fuera del lenguaje central y en una biblioteca estándar ayuda, así que creo que trataría de llevar esto lo más lejos posible
jk.

1
Por compatible con versiones anteriores, ¿quiere decir que los compiladores más nuevos deberían poder compilar el código antiguo?
Rei Miyasaka

Quiero decir que los compiladores más nuevos no deberían cambiar el significado del código antiguo, no compilar sería un subconjunto de eso
jk.

La mayoría de los casos, cuando no existe una característica en particular, o si desea cambiar, las personas crean un nuevo lenguaje basado en el anterior. C con clases => C ++
umlcat


4

Tanto Java como C # tienen problemas molestos con sus sistemas de tipos debido al deseo de mantener la compatibilidad con versiones anteriores al agregar genéricos. A Java no le gusta mezclar genéricos y matrices; C # no permitirá algunas firmas útiles porque no puede usar tipos de valores como límites.

Como ejemplo de esto último, considere que

public static T Parse <T> (Tipo <T> tipo, cadena str) donde T: Enum
junto o reemplazando
objeto estático público Parse (tipo de tipo, cadena str)
en la Enumclase permitiría
MyEnum e = Enum.Parse (typeof (MyEnum), str);
en lugar de lo tautológico
MyEnum e = (MyEnum) Enum.Parse (typeof (MyEnum), str);

tl; dr: piense en el polimorfismo paramétrico cuando comience a diseñar su sistema de tipos, no después de publicar la versión 1.


2
La incapacidad de restringir los tipos a enum es molesto en C #, pero puede solucionarlo de esta manera. MyMethod<T>(T value) where T : struct, IComparable, IFormattable, IConvertible Pero aún tiene que probar una enumeración y es un truco. Creo que la mayor falta en los genéricos de C # no es compatible con los tipos superiores, lo que realmente abriría el lenguaje a algunos conceptos geniales.
CodexArcanum

4

Siento que me estoy abriendo para ser criticado, pero realmente odio la capacidad de pasar tipos de datos antiguos por referencia en C ++. Solo odio un poco poder pasar tipos complejos por referencia. Si estoy mirando una función:

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

Desde el punto de vista, no hay forma de saber que bar, que se puede definir en un archivo completamente diferente, es:

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

Algunos podrían argumentar que hacer algo como esto puede ser un mal diseño de software y no culpar al lenguaje, pero no me gusta que el lenguaje te permita hacer esto en primer lugar. Usando un puntero y llamando

bar(&a);

Es mucho más legible.


+1 No estoy de acuerdo contigo, pero aprecio tu razonamiento.
Jon Purdy

@ Jon En realidad estaría muy interesado en lo que piensas. ¿Tiene la vista "no culpe al idioma"?
Jeff

66
@Jeff: Por un lado, la razón principal por la que la semántica de referencia llegó a C ++ fue la sobrecarga del operador, para lo cual el comportamiento de referencia uniforme simplemente tiene sentido. Sin embargo, lo que es más importante, C ++ está diseñado para ser versátil y proporcionar funciones muy finas, incluso si esto conlleva un riesgo significativo de error del programador. Así que sí, al menos en este caso particular, no culpes al lenguaje. Prefiero ser capaz de cometer errores que dejar que un idioma se interponga en mi camino.
Jon Purdy

@Jon estuvo de acuerdo, sería muy extraño que el pase por referencia se aplicara a todo, además de los POD. Preferiría que esta característica faltara por completo en C ++ como alternativa, pero podemos estar de acuerdo en no estar de acuerdo :). ¡Gracias por el aporte!
Jeff

Parece que a Java no le gustan los punteros tanto como a usted.
Mateen Ulhaq

4

ALTERAR

Cuando aprendí COBOL, la declaración ALTER todavía era parte del estándar. En pocas palabras, esta declaración le permitiría modificar las llamadas a procedimientos durante el tiempo de ejecución.

El peligro era que podía poner esta declaración en una sección oscura del código a la que rara vez se accedía y tenía el potencial de cambiar completamente el flujo del resto de su programa. Con múltiples declaraciones ALTER, puede hacer que sea casi imposible saber qué estaba haciendo su programa en cualquier momento.

Mi instructor universitario, muy enfáticamente, declaró que si alguna vez veía esa declaración en alguno de nuestros programas, automáticamente nos rechazaría.


Sin embargo, tiene buenos casos de uso: tropezar o memorizar. En lugar de escribir v() { if (not alreadyCalculatedResult) { result = long(operation); alreadyCalculatedResult = true; } result; }, dicesv() { result = long(operation); v = () => result; result; }
configurador el

4

El peor pecado de un lenguaje de programación no está bien definido. Un caso que recuerdo es C ++, que, en sus orígenes:

  1. Estaba tan mal definido que no podía obtener un programa para compilar y ejecutar siguiendo libros o ejemplos.
  2. Una vez que modificó el programa para compilar y ejecutar bajo un compilador y sistema operativo, tendría que comenzar de nuevo si cambiara compiladores o plataformas.

Como recuerdo, me llevó cerca de una década definir C ++ lo suficientemente bien como para que fuera tan profesionalmente confiable como C. Es algo que nunca debería volver a suceder.

Algo más que considero un pecado (¿debería ir en una respuesta diferente?) Es tener más de una "mejor" forma de hacer una tarea común. Es el caso de (nuevamente) C ++, Perl y Ruby.


1
No veo cómo evitar la mala definición en un lenguaje en evolución. O, para el caso, en un lenguaje prediseñado donde el diseñador original perdió algunos puntos importantes (como Pascal).
David Thornley

@David Thornley La buena definición está bien definida. A pesar de los errores, la mayoría de los diseñadores de lenguaje de programación lo hacen desde el principio. Las herramientas pueden verificar que una gramática es inequívoca cuando está completa (C ++ requiere al menos tres gramáticas), y se debe especificar la semántica para implementaciones estándar.
Apalala

Estoy de acuerdo en que los lenguajes bien definidos son posibles, pero eso no sucederá en un lenguaje en evolución como el C ++ preestándar. La alternativa es que cada idioma esté completamente diseñado antes del lanzamiento, y esa no es necesariamente la forma de obtener los mejores idiomas. Diría que la mayoría de los diseñadores de lenguaje de programación se equivocan al principio, ya que el diseño del lenguaje es extremadamente complicado.
David Thornley

Creo que tengo problemas para entender lo que quieres decir con "bien definido". ¿Su queja es que los diferentes compiladores de C ++ en realidad no compilan el mismo lenguaje?
Sean McMillan

3

Las clases en C ++ son algún tipo de patrón de diseño forzado en el lenguaje.

Prácticamente no hay diferencia en tiempo de ejecución entre una estructura y una clase, y es tan confuso entender cuál es la verdadera ventaja real de la programación de "ocultar información" que quiero ponerla allí.

Voy a ser rechazado por eso, pero de todos modos, los compiladores de C ++ son tan difíciles de escribir que este lenguaje se siente como un monstruo.


2
La ocultación de información es importante porque le permite ocultar detalles específicos de la implementación, que probablemente cambien, de las partes accesibles de la API (la "IU" de la API), por lo que hacer cambios en el programa se vuelve más fácil y menos doloroso.
Anto

1
La interfaz de usuario de la API ... no, en serio, no la compro.
jokoon

3
Esta diferencia no es la parte más repugnante de C ++, ni siquiera está cerca. La única diferencia es un modificador de acceso predeterminado (público para estructuras, privado para clases). C ++ es un lenguaje horrible y monstruoso, pero ciertamente no en esta parte.
SK-logic

sk-logic: bueno, podría decir que los horrores comienzan allí.
jokoon

2
La ocultación de información es buena; puedes encontrar discusiones sobre eso por todas partes. El único libro de software destacado en el que puedo pensar que estaba en contra de él fue "The Mythical Man-Month" de Brooks, y más tarde lo consideró el mayor error del libro. Si no comprende las ventajas, realmente no está calificado para hacer el juicio que está haciendo.
David Thornley

3

Aunque cada idioma tiene sus fallas, ninguna es una molestia una vez que las conoces. Excepto por este par:

Sintaxis compleja junto con una API de Wordy

Esto es particularmente cierto en un lenguaje como Objective-C. La sintaxis no solo es abrumadoramente compleja, sino que la API usa nombres de funciones como:

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

Estoy totalmente de ser explícito y no ambiguo, pero esto es ridículo. Cada vez que me siento con xcode, me siento como un n00b, y eso es realmente frustrante.


La sintaxis de Objective-C es abrumadoramente compleja? ¿No has visto C ++? Y el nombre real del método que hay tableView:cellForRowAtIndexPath:, que es muy descriptivo en mi opinión.
derecha el

Aprende a escribir al tacto.
finnw

2
  1. caracteres firmados en C: una abominación inventada para que los matemáticos tengan grandes colecciones de artículos pequeños
  2. Usando el caso para llevar contenido semántico, nuevamente para matemáticos, que no necesitan hablar y nunca tienen suficiente espacio para sus fórmulas
  3. Asignación Let / Plain vs. Asignación Set en dialectos básicos: creo que no hay matemáticos involucrados aquí

Estoy perdido en cuanto a tu comentario de caracteres firmado, ¿podrías explicarlo?
Winston Ewert

1
Para un humano, el concepto de caracteres (no) firmados y la necesidad de decirle al compilador que use caracteres no firmados como predeterminados seguramente es tan loco como para un matemático la afirmación de que 2! = 2, porque el segundo 2 es mayúscula, negrita o cursiva.
Ekkehard.Horner

55
El problema es que C confunde el concepto de "char" (es decir, parte de una cadena de texto) y "byte" (es decir, (u)int_least8_t). La firma tiene mucho sentido para los enteros pequeños, pero no tiene ningún sentido para los personajes.
dan04

@ dan04: Estoy de acuerdo, desearía que tuvieran diferentes tipos de enteros pequeños (con y sin signo), byte y carácter. Cuando le explica a los novatos que para manipular la memoria sin procesar que necesitan convertir a char*... como una C-String, se confunden mucho.
Matthieu M.

C # tiene este derecho con separadas sbyte, bytey chartipos.
dan04
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.