¿Qué lenguaje de programación genera la menor cantidad de errores difíciles de encontrar? [cerrado]

54

En su opinión, ¿qué lenguaje le permite al programador promedio generar funciones con la menor cantidad de errores difíciles de encontrar? Por supuesto, esta es una pregunta muy amplia, y estoy interesado en respuestas y conocimientos muy amplios y generales.

Personalmente, encuentro que paso muy poco tiempo buscando errores extraños en los programas Java y C #, mientras que el código C ++ tiene su conjunto distintivo de errores recurrentes, y Python / similar tiene su propio conjunto de errores comunes y tontos que serían detectados por el compilador en otros idiomas

También me resulta difícil considerar lenguajes funcionales a este respecto, porque nunca he visto un programa grande y complejo escrito en un código completamente funcional. Su aporte por favor.

Editar: Aclaración completamente arbitraria del error difícil de encontrar: tarda más de 15 minutos en reproducirse, o más de 1 hora para encontrar la causa y corregirlo.

Perdóname si esto es un duplicado, pero no encontré nada sobre este tema específico.

Magnus Wolffelt
fuente
10
¡Me gustaría ver algunas investigaciones sobre este tema! No solo "mi evidencia anecdótica sugiere que el único idioma que conozco es el rey", sino las tasas de errores de grandes proyectos, etc.
Frank Shearar
@ Frank ... si tuvieras MUCHO (y quiero decir MUCHO) tiempo, probablemente podrías extraer algunas estadísticas de ohloh, siempre que puedas identificar parches que corrijan errores de miles de repositorios de código.
Tim Post
En el que solo se permiten comentarios. No hay otras instrucciones :)
Victor Hurdugaci
44
Creo que "difícil de encontrar" necesita ser aclarado. Su pregunta y la mayoría de las respuestas parecen definir "difícil de encontrar" como equivalente a "no compilado por el compilador". Usted menciona que Python tiene errores tontos que serían detectados por el compilador. Lo suficientemente justo. Pero esos errores tontos generalmente no son tan difíciles de encontrar. Ciertamente, no están en la misma categoría que los errores de C ++ derivados de liberar memoria demasiado pronto.
Winston Ewert
@ Winston De acuerdo.
Magnus Wolffelt

Respuestas:

58

Cuanto más poderoso sea el sistema de tipos del lenguaje, más errores se detectarán en el momento de la compilación.

La siguiente figura compara algunos de los lenguajes de programación bien conocidos en términos de potencia, simplicidad y seguridad de sus sistemas de tipos. [ Fuente ]

texto alternativo

* Factoring en la capacidad de usar construcciones inseguras.

C # se inserta en la fila insegura debido a la palabra clave "insegura" y la maquinaria de puntero asociada. Pero si desea pensar en esto como una especie de mecanismo de función externa en línea, no dude en golpear C # hacia el cielo.

He marcado Haskell '98 como puro pero GHC Haskell como no puro debido a la familia de funciones inseguras *. Si deshabilita inseguro *, salte GHC Haskell en consecuencia.

faltantefaktor
fuente
8
¡Esto es brillante!
77
No pude encontrar Common Lisp en el gráfico: (let ((itbe '())) ... )...
duros
77
¿Qué? ¿No quedaba espacio en el lado izquierdo para PHP?
pestaa 01 de
77
C # permite punteros seguros : se verifica toda la aritmética de punteros. Por lo tanto, creo que debería estar a la altura de Java.
Alex ten Brink
11
@missingfaktor: Creo que C # no está bien posicionado. C # permite y promueve estilos de programación más seguros. No debe medirse por lo peor que permite.
back2dos
20

En mi opinión, Haskell te ayuda a evitar algunas fuentes comunes de errores:

  • es puramente funcional: las funciones no pueden tener efectos secundarios (no intencionales) y esto hace que la programación multinúcleo sea más fácil y menos propensa a errores
  • está fuertemente tipado: no puede, por ejemplo, mezclar accidentalmente valores bool, char, int y float
  • está estáticamente escrito: muchos errores de programación se detectan en tiempo de compilación
  • nullno es parte de las definiciones de tipo de valor: con esto evitas el error de mil millones de dólares
  • hay muchas funciones de orden superior listas para usar que puede reutilizar en lugar de escribir sus propias implementaciones, posiblemente defectuosas
  • tiene un recolector de basura: los errores de memoria casi se eliminan (a excepción de "fugas de espacio" debido a su estrategia de evaluación diferida)
LennyProgrammers
fuente
11
No rechazaré esto, pero estoy tentado porque no cumple con los criterios. Se supone que permite que "el programador promedio genere funciones" con un recuento mínimo de errores, pero el programador promedio, para decirlo sin rodeos, no puede hacer cara o cruz de Haskell en primer lugar.
Mason Wheeler
55
Muchos puntos buenos, pero al eliminar algunas clases de error (efectos secundarios no deseados, conversiones de tipo inesperadas) Haskell agrega una clase completamente nueva de error: fugas de espacio. (Además, aunque no existe null, existe undefinedun miembro de todos los tipos)
J_random_hacker
55
@Mason: Si no escribe en él, no puede tener errores en él :)
Michael K
2
Supongo que no existe un almuerzo gratis: puede tener errores fáciles de encontrar o código fácil de escribir, pero no ambos :)
Benjol
66
@Mason, ¿qué es exactamente un "programador promedio"? La pregunta comienza con "qué lenguaje de programación ...", no "cuál entre C, C ++ y Java ...";)
Agos
18

Tradicionalmente, los errores más difíciles de encontrar son las condiciones de carrera en aplicaciones de subprocesos múltiples, ya que son

  • casi imposible de reproducir
  • puede ser muy sutil

Por lo tanto, necesita idiomas que manejen el paralismo por usted de la manera más sencilla y discreta posible. Estos aún no son convencionales. Java hace algo, pero te deja con la parte difícil.

Según tengo entendido, necesita un lenguaje funcional ya que "sin efectos secundarios" es lo que, en primer lugar, hace que desaparezcan las dos viñetas. He visto que el trabajo está en curso para hacer que Haskell sea un lenguaje eficiente de múltiples hilos, y creo que Fortress está diseñado desde cero para ser un lenguaje paralelo eficiente.


Editar: en Java Executorsmaneja aún más partes duras. Debe hacer que las tareas individuales se ajusten a la Callableinterfaz.

usuario1249
fuente
55
++ Condiciones de carrera. Puedes decir eso otra vez.
Mike Dunlavey
Sí. Tenga en cuenta que la computación paralela en general es difícil, incluso con asistencia de idiomas. (Las macros comunes de Lisp son difíciles, a pesar de la gran cantidad de soporte de idiomas, porque lo que hacen es muy poderoso. El mismo director.)
David Thornley
¿Qué hay de Erlang?
Malfist
@Malfist, no sé lo suficiente sobre Erlang para responder eso. ¿Quizás debería abrir una pregunta si realmente quiere saber?
2
Erlang es una programación funcional diseñada para hacer que los subprocesos múltiples sean simples y seguros. No compartes variables, pasas mensajes. Lea su página de wikipedia.
Malfist
13

Ada está diseñado para que se capture todo lo posible en tiempo de compilación en lugar de tiempo de ejecución. Lo que esto significa es que a menudo toma alrededor de 10 veces más tiempo para que un programa en Ada se compile de lo que lo haría el equivalente en Java, pero cuando lo hace, puede estar mucho más seguro de que no se manifestarán clases enteras de errores cuando el programa correr.

Mark Pim
fuente
77
+1 por notar, correctamente, que Ada capta cosas en el compilador que otros idiomas ignoran. -1 para la afirmación de que esto significa que se tarda 10 veces más en obtener un programa Ada para compilar. ¡Ada recompensa al programador que DISEÑA! su código, que PIENSA !!! sobre lo que está haciendo antes de comenzar a escribir locamente. Mi experiencia personal, haciendo programación de producción en Ada para el trabajo de defensa, fue que no tomó mucho más tiempo para que el código Ada compilara que C / C ++ o FORTRAN, pero el código Ada tuvo significativamente menos problemas más tarde. Pratt y Whitney notaron algo similar.
John R. Strohm
1
@john r strohm, por lo que entiendo, no está hablando del tiempo que le toma al compilador compilar el código, sino más bien el tiempo para compilar el código.
Malfist
Estoy de acuerdo en obtener el código compilable. Puedo recordar muchos comentarios sexistas hechos por programadores que aprenden el lenguaje sobre su selección de liendres. Por lo general, como Well, si nombras un idioma después de una mujer ...
Michael Shaw
@Ptolomeo, si los barcos pueden tener nombres de mujeres (excepto aparentemente para los transportistas estadounidenses), entonces los lenguajes de programación también pueden. Prefiero que se llame "Ada" que "USS Ronald Reagan" :)
1
Una vez que superé la curva de aprendizaje, en realidad empecé a escribir código Ada bastante rápido, después de haber pasado mucho tiempo pensando en el diseño. Ada definitivamente no es el lenguaje de un hacker. Actualmente trabajo en Java, y algunas de las cosas que veo en mis proyectos actuales me hacen extrañar un poco a Ada (nunca pensé que diría eso).
James Adam
7

Primero una definición: un error difícil de encontrar, según tengo entendido, es un error que se puede reproducir pero la causa es difícil de encontrar.

Probablemente el aspecto más importante es lo que yo llamaría estrechez , es decir, qué tan lejos puede escapar un error, qué tan grande es el alcance que puede influir un error. En lenguajes como C, un error, por ejemplo, un índice de matriz negativo o un puntero no inicializado, puede afectar literalmente todo en todas partes en todo el programa, por lo que en el peor de los casos, debe verificar todo en todas partes para encontrar la fuente de su problema.

Los buenos idiomas en ese sentido admiten modificadores de acceso y los aplican de una manera que hace que sea difícil o imposible eludirlos. Los buenos lenguajes lo alientan a limitar el alcance de sus variables, en lugar de hacer que sea demasiado fácil tener variables globales (por ejemplo, "todo lo que no se declara explícitamente es una variable global con un tipo y valor predeterminados").

El segundo aspecto importante es la concurrencia . Las condiciones de carrera son generalmente difíciles de reproducir y, por lo tanto, difíciles de encontrar. Los buenos idiomas ofrecen mecanismos de sincronización fáciles de usar, y sus bibliotecas estándar son seguras para subprocesos cuando sea necesario.

Esto ya completa mi lista; otras cosas, como la tipificación fuerte, ayudan a detectar errores en el momento de la compilación, pero esos errores probablemente no serían difíciles de encontrar más adelante.

Teniendo en cuenta todo eso, diría que Java y C #, y muchos otros lenguajes en el mundo JVM y .net, son adecuados para evitar errores difíciles de encontrar.

usuario281377
fuente
El bloqueo manual puede parecer un "mecanismo de sincronización fácil de usar", pero en realidad solo es "simple de usar, pero te espera momentos divertidos mientras persigues ese punto muerto". Y, bueno, puede hacer concurrencia basada en actores en bastantes idiomas.
Frank Shearar
Frank: al menos el bloqueo es lo suficientemente simple como para que todos puedan hacerlo sin pasar días, averiguar qué API usar, etc.
user281377
Luego está el punto de vista de que una solución de concurrencia que es "lo suficientemente simple como para que todos puedan hacerlo" es como dar sierras de mesa a preescolares.
David Thornley
@David: veo su punto, pero en muchos casos, una solución simple es realmente todo lo que se necesita. Por ejemplo, considere un servlet utilizado solo por un puñado de usuarios, que tiene que usar un recurso compartido (por ejemplo, la conexión de la base de datos). Sin sincronización, las condiciones de carrera ocurren de vez en cuando; pero no es exactamente ciencia de cohetes poner todas las operaciones de acceso a la base de datos en un bloque sincronizado (conexión) {}.
user281377
+1 Para esta definición "qué tan grande es el alcance que un error puede influir potencialmente". Tuve algunos errores muy desagradables con lenguajes dinámicos donde un objeto del tipo de datos incorrecto llegó muy lejos en el código (gracias a la escritura de pato) antes de manifestarse como un error.
Giorgio
7

Dado que Excel es el DSL más utilizado, iré con Excel. (excluyendo VBA, por supuesto)

Se ajusta a la factura:

  • Siempre es fácil de reproducir (aquí hay una hoja de cálculo, no funciona)
  • Es bastante fácil encontrar el error, ya que es totalmente "funcional": comience con la celda que está mal y rastree todas sus dependencias.
Scott Whitlock
fuente
Esto puede ser cierto siempre y cuando no agregue errores fáciles de encontrar.
Mouviciel
+1 Un poco descarado ya que Excel son datos, no un lenguaje. Me dio una buena risa :)
recursion.ninja
2
@awashburn - oh, no lo sé. Creo que califica como idioma. Cada celda es una "variable". Cada variable se establece declarativamente como un literal (como 123o ABC) o una función ( =SUM(A2:A5)). Excel luego evalúa todas las variables, determinando el orden para resolver las dependencias, etc. Ciertamente no se trata solo de datos.
Scott Whitlock
2
Retracto mi declaración, resulta que Excel está Turing completo ... Aprendí algo completamente inquietante ...
recursion.ninja
1
"... el verdadero maestro [Lisp] se da cuenta de que todos los datos son código". Quizás esto también se aplica a Excel, curiosamente. blogs.msdn.com/b/sriram/archive/2006/01/15/lisp-is-sin.aspx
James Mishra
7

Esta es una pregunta difícil porque la mayoría de los errores no son culpa del lenguaje en sí, sino que son culpa de los desarrolladores que cometen errores en la forma en que usan el lenguaje.

Creo que hay varios aspectos de las características del lenguaje que afectan la probabilidad de errores:

  • Interactividad : los lenguajes dinámicos con REPL fomentan la interacción / experimentación con programas en ejecución y ciclos de código / prueba mucho más pequeños. Si cree que la iteración es una buena manera de descubrir soluciones simples y limpias y detectar / eliminar errores, esto tenderá a favorecer los lenguajes interactivos.

  • Expresividad : si el código es más corto y tiene menos complejidad / complejidad incidental, entonces es más fácil ver errores / errores lógicos.

  • Tipo de seguridad : cuanto más tiempo se compruebe el tiempo de compilación, el compilador detectará más errores, por lo que, en general, la seguridad de tipo es algo bueno. Sin embargo, estos errores no suelen ser difíciles de encontrar , incluso en un lenguaje completamente dinámico, el tipo incorrecto en una estructura de datos generalmente causará un error de tiempo de ejecución muy obvio, y TDD casi siempre detecta este tipo de errores.

  • Inmutabilidad : muchos errores duros se deben a interacciones complejas de estado mutable. Los idiomas que enfatizan la inmutabilidad (Haskell, Clojure, Erlang) tienen una enorme ventaja al evitar la mutabilidad

  • Programación funcional: los enfoques funcionales para escribir código tienden a ser más "demostrablemente correctos" que el código orientado a objetos con secuencias complejas de efectos / interacciones. Mi experiencia es que FP ayuda a evitar errores difíciles: creo que hay algunas investigaciones académicas en algún lugar que actualmente no puedo encontrar que respalden esto.

  • Soporte de concurrencia: los problemas de concurrencia son particularmente difíciles de detectar y depurar, por eso es tan importante. Cualquier cosa que requiera un bloqueo manual está condenada al fracaso (y esto incluye casi todos los enfoques orientados a objetos de concurrencia). El mejor lenguaje que conozco a este respecto es Clojure: tiene un enfoque único para administrar la concurrencia que combina la memoria transaccional de software con estructuras de datos inmutables para obtener un marco de concurrencia novedoso, confiable y composable. Consulte http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey para obtener más información

mikera
fuente
Las personas como yo, apasionadas de la programación funcional, aprecian esta respuesta. La expresividad es tu amiga.
Sridhar Sarnobat
1
¡La mejor respuesta hasta ahora! Creo que esto está respaldado por los siguientes dos análisis de frecuencia de errores: deliberate-software.com/safety-rank-part-2 y macbeth.cs.ucdavis.edu/lang_study.pdf . Ambos muestran que cuanto más puro, más funcional, más expresivo y más seguro es el idioma, menos errores tiene. Del mismo modo, ambos muestran que Clojure y Ruby escapan a la regla de seguridad, lo que probablemente indica que la interactividad tiene el mismo impacto que usted señaló.
Didier A.
@DidierA. desafortunadamente el enlace ucdavis está roto (sin archivo wbm) - Creo que lo obtuvo de la página de Prem Devanbu que ahora apunta a un enlace actualizado: un estudio a gran escala de lenguajes de programación y calidad de código en Github
icc97
5

Cuanto menos poderoso es un idioma, menos opciones le ofrece para disparar su propio pie.

Los lenguajes de alto nivel como Java y C # producirán menos errores que los lenguajes de bajo nivel como C ++.

Dicho esto, creo que Java es más seguro que C #. Java está limitado artificialmente para que un programador promedio sin conocimientos avanzados pueda dominarlo y producir programas estables.

usuario8685
fuente
44
+1 para "Cuanto menos poderoso es un idioma, menos opciones te ofrece disparar tu propio pie".
Michael K
El gráfico de missingfaktor parece decir que C # y C ++ están en "pie de igualdad" en la medida en que poder dispararte allí va y eleva a Java por encima de eso. Sin embargo, no estoy de acuerdo con esa parte de la tabla. Estás obligado a pasar por aros para hacer algunos disparos en C #.
Jesse C. Slicer
66
La seguridad y el poder no son inversamente proporcionales (que es lo que parece pensar ;-) Haskell, por ejemplo, es EXTREMADAMENTE poderoso y, sin embargo, tiene muy pocas formas de dispararse a sí mismo.
missingfaktor
3
Si el idioma es débil, solo necesitas un pie más grande.
77
@missingfaktor, eso es porque es un efecto secundario dispararte en el pie.
3

En su opinión, ¿qué lenguaje le permite al programador promedio generar funciones con la menor cantidad de errores difíciles de encontrar?

En mi opinión, Delphi. Al estar basado en Pascal, el lenguaje es lo suficientemente simple e intuitivo como para que el programador promedio (o incluso los codificadores inexpertos) lo descubra fácilmente, y su rica herramienta y soporte de biblioteca hacen que la mayoría de los errores sean fáciles de encontrar.

  • Escritura fuerte y un compilador estricto que detecta muchos errores comunes.
  • Sintaxis intuitiva que no fomenta errores comunes. ("El último error del mundo" if (alert = RED) {LaunchNukes;}, por ejemplo, no se compilará).
  • Un modelo de objetos bien diseñado que elimina muchos de los errores comunes de C ++ OOP.
  • Verificación de límites y verificación de rango integrada en el lenguaje, reduciendo drásticamente las posibilidades de problemas de seguridad.
  • Probablemente el compilador más rápido conocido por el hombre, lo que aumenta su productividad y hace que sea más difícil perder su tren de pensamiento mientras espera una compilación.
  • El depurador El depurador de Visual Studio quiere ser como cuando crezca.
  • El seguimiento de fugas integrado directamente en el administrador de memoria hace que la búsqueda y reparación de fugas de memoria sea trivial.
  • Una biblioteca estándar grande y madura que proporciona formas preconstruidas y probadas para realizar tareas comunes sin tener que construir sus propias implementaciones, posiblemente con errores.
  • Se envía con herramientas útiles, como un potente sistema de registro y un generador de perfiles, para facilitar el seguimiento de los problemas.
  • Fuerte apoyo de la comunidad para problemas comunes que no están en la biblioteca estándar, incluida una poderosa biblioteca de concurrencia de terceros .
Mason Wheeler
fuente
Soy un jinete de Delphi desde hace mucho tiempo, pero desapareció de sus raíces de Pascal cuando me permitió escribir cualquier cosa a otra cosa, a la C / C ++:var I: Integer; Pointer(I)^ := $00;
Jesse C. Slicer
1
@Jesse: Quizás, pero lo veo como una concesión necesaria al pragmatismo. Kernighan hizo muchos puntos buenos cuando escribió Por qué Pascal no es mi lenguaje de programación favorito. Los tipos de letra son necesarios para hacer muchas cosas importantes de bajo nivel. Pero uno de los puntos fuertes de Delphi es la forma en que sus bibliotecas encapsulan los detalles de bajo nivel y hacen que la mayoría de los elementos inseguros de puntero y tipografía sean innecesarios.
Mason Wheeler
No estoy en desacuerdo con que puede ser necesario, pero afirmar que Strong Typing es algo negado por esto. Pascal original no permitía tales travesuras y, por lo tanto, estaba fuertemente tipado. Pero no iría tan lejos para llamar a Delphi débilmente tipado, es una especie de 'medio bien tipeado'.
Jesse C. Slicer
1
@Jesse: ¿La versión Wirth original de Pascal no permitía registros de variantes? IIRC eventualmente se volvieron tan comúnmente utilizados para subvertir la escritura fuerte que Borland y otros decidieron simplemente introducir textos tipográficos para simplificarlo porque todos lo hacían de todos modos.
Mason Wheeler
en.wikipedia.org/wiki/Pascal_(programming_language)#Divisions y en.wikipedia.org/wiki/Pascal_(programming_language)#Criticism así como pascal-central.com/ppl/chapter3.html parecen indicar que era parte de el primer estándar en 1983. Veo algunas referencias de Wirth que parecen datarse de 1974, por lo que diría que sí. Creo que la parte problemática era permitir que se subvierta como tal (es decir, los campos variantes que toman la misma memoria, como las uniones en C). Si simplemente se usaran como un mecanismo de alcance y el diseño de la memoria fuera para el superconjunto, sería un tipo más fuerte.
Jesse C. Slicer
2

Una cosa a tener en cuenta es el tiempo de respuesta.

Durante los últimos cinco años más o menos, he desarrollado principalmente aplicaciones web en Java (JSF, Seam, etc.). Recientemente obtuve un nuevo trabajo y estamos usando Perl (con Catalyst y Moose).

Soy mucho más productivo en Perl que en Java.

No es necesario compilar e implementar (en caliente), es una de las razones. También encuentro que escribir casos de uso es más fácil, ya que se puede hacer de una manera más iterativa. Y los marcos en Java parecen ser innecesariamente complejos, al menos para los proyectos en los que he estado involucrado.

Supongo que la cantidad de errores en mi código Perl es más o menos igual a la cantidad de errores en mi código Java, incluso podría ser mayor. Pero, encuentro y más fácil y rápido encontrar y corregir estos errores.

slu
fuente
1

Quizás estudiar una cantidad de herramientas disponibles para el análisis de código estático y dinámico para cada lenguaje de programación podría dar una idea. Cuantas más herramientas para un idioma, es más probable que el idioma sea muy popular entre los usuarios o muy popular en la generación de errores difíciles de encontrar. Pero no puedo lograr que Google me señale ningún estudio realizado sobre este tema. También se debe tener en cuenta que algunos lenguajes como C se pueden usar para solucionar los errores de hardware subyacentes, así como para evitar el desgaste del hardware a medida que envejece.

vpit3833
fuente
1
"evitar el desgaste del hardware a medida que envejece" ...?
j_random_hacker
He leído que algunos sistemas operativos Unix que se ejecutan en máquinas de misión crítica verifican el estado de la CPU, la RAM y otro hardware. serverfault.com/questions/56192/… discute sobre esto con cierta profundidad. Si algunas líneas en un módulo RAM se vuelven defectuosas con el tiempo, el SO no usará esos módulos defectuosos y no los informará en la memoria física total disponible. Tales cosas podrían hacerse también en otro hardware.
vpit3833
Es un dato interesante, pero no veo cómo es relevante aquí. Tampoco hay nada en su enlace que mencione estos sistemas operativos Unix de reparación automática: solo habla de formas de probar el hardware de una PC.
j_random_hacker
1
Mencioné que significa que los programas por sí solos podrían no ser la fuente de errores, sino también el hardware u otros factores externos.
vpit3833
1

En lugar de hablar sobre idiomas, ¿qué hay de hablar sobre las características del lenguaje?

  • Java te obliga a pensar en excepciones (tiros ...) y debes publicar o manejar estas excepciones. ¿Eso realmente me impide olvidar situaciones de error o estoy usando más excepciones derivadas de SystemException que no necesitan este manejo?
  • ¿Qué pasa con el "diseño por contrato" (http://en.wikipedia.org/wiki/Design_by_contract) que me obliga a pensar en las condiciones previas y posteriores. He leído que ahora es posible con c # -4.0.
k3b
fuente