¿Merece la pena el tipeo estático?

108

Comencé a codificar en Python principalmente donde no hay seguridad de tipo, luego me mudé a C # y Java donde lo hay. Descubrí que podía trabajar un poco más rápido y con menos dolores de cabeza en Python, pero, de nuevo, mis aplicaciones C # y Java tienen un nivel de complejidad mucho mayor, por lo que supongo que nunca le di a Python una verdadera prueba de esfuerzo.

Los campamentos de Java y C # hacen que parezca que sin el tipo de seguridad establecido, la mayoría de las personas se encontrarían con todo tipo de errores horribles que dejarían una derecha y sería más problemático de lo que vale.

Esta no es una comparación de idiomas, por lo tanto, no aborde problemas como compilados o interpretados. ¿Vale la pena la seguridad de tipos para acelerar el desarrollo y la flexibilidad? ¿POR QUÉ?

a las personas que querían un ejemplo de la opinión de que la escritura dinámica es más rápida:

"Utilice un lenguaje de tipo dinámico durante el desarrollo. Le brinda comentarios más rápidos, tiempo de respuesta y velocidad de desarrollo". - http://blog.jayway.com/2010/04/14/static-typing-is-the-root-of-all-evil/

Morgan Herlocker
fuente
15
Esta pregunta es lo opuesto a ¿Qué argumentos hay a favor de la escritura débil? .
Nicole
8
@Prof Plum: ¿puedo requerir una prueba de que hay un éxito en la velocidad de desarrollo y flexibilidad? Dado que estamos hablando de un aspecto particular , la seguridad de tipo , usando Javao C#no sería concluyente, su forma de proporcionarlo NO es la única ...
Matthieu M.
32
Con diligencia en un lenguaje estricto, puede minimizar los "dolores de cabeza" y luego incluso puede ver un aumento de velocidad debido a la finalización automática de IDE, la generación de código y las sugerencias de código.
Nicole
99
@Prof Plum: entiendo, no espero que usted (o alguien realmente) haya probado completamente todos los idiomas que haya creado ^^ El problema es que la mayoría de las personas que he visto se quejan de algún aspecto específico de los lenguajes de programación (Estático Escribir a menudo viene) generalmente se quejan de una implementación particular y no se dan cuenta.
Matthieu M.
55
@Prof Plum, todo lo que la publicación del blog realmente tiene que decir sobre la velocidad es la afirmación calva de que "cualquiera que haya trabajado seriamente con un lenguaje moderno de tipo dinámico como Ruby o Smalltalk, sabe que es más productivo". No hay ejemplos reales de cómo, en términos prácticos, hace que el desarrollo sea más rápido.
Carson63000

Respuestas:

162

Es una especie de mito que los programadores no tengan que preocuparse por los tipos en lenguajes de tipo dinámico.

En idiomas escritos dinámicamente:

  • Todavía tiene que saber si está trabajando con una matriz, un entero, una cadena, una tabla hash, una referencia de función, un diccionario, un objeto o lo que sea.

  • Si es un objeto, debe saber a qué clase pertenece.

  • Asignar uno de estos tipos a una variable o parámetro de función que se espera sea otro tipo casi siempre es un error.

  • En un nivel inferior, cosas como el número de bits o con signo versus sin signo con frecuencia aún deben tenerse en cuenta si está completando un paquete TCP, por ejemplo.

  • Puede encontrarse con problemas donde obtiene un cero donde realmente quería una cadena vacía. En otras palabras, todavía está depurando errores de discrepancia de tipos. La única diferencia real es que el compilador no está captando los errores.

  • Yo diría que ni siquiera está guardando mucha escritura , porque tiende a querer documentar en los comentarios de qué tipo son los parámetros de su función en lugar de documentarlo en su código. Esta es la razón por la cual los bloques de comentarios de estilo doxygen son mucho más populares en la práctica en todo el código escrito dinámicamente, donde en los idiomas escritos estáticamente solo los ves para bibliotecas.

Eso no quiere decir que la programación en lenguajes escritos dinámicamente no se sienta más agradable porque el compilador no siempre está detrás de usted, y los programadores experimentados no tienden a tener dificultades para encontrar y corregir el tipo de errores que la escritura estática podría detectar de todos modos. , pero ese es un problema completamente separado de un supuesto aumento en la eficiencia o reducción en la tasa de errores, para lo cual el tipeo dinámico es mejor incluso con tipeo estático.

usuario61852
fuente
10
Tendría que cuestionar el hecho de que los programadores experimentados no hacen / presentan ese tipo de errores. Los buenos desarrolladores que son humildes y reconocen la posibilidad de que cometan errores (y los desarrolladores experimentados no siempre son así) tienen menos probabilidades de crear esos errores.
Jay Jay Jay
12
No podría estar más de acuerdo con "Argumentaría que ni siquiera está ahorrando mucho escribiendo". Terminas documentando los tipos en comentarios y verificándolos en tus pruebas, lo que, en todo caso, requiere más tipeo y mantenimiento (después de todo, debes recordar actualizar dichos comentarios cada vez que cambien tus tipos, y la mayoría de las veces no lo harás )
Severyn Kozak
Pasamos mucho más tiempo en nuestra tienda de Python documentando tipos de los que ahorramos en un lenguaje detallado de tipo estático como C # o Java. También vale la pena señalar que la nueva generación de lenguajes como Go y Rust usa inferencia de tipos, por lo que está escribiendo "x: = new (Object)" en lugar de "Object x = new Object ()".
weberc2
Estoy de acuerdo contigo cuando dices que el lenguaje dinámico se siente más agradable, pero no sé por qué. ¿Tienes una explicación para eso?
Rodrigo Ruiz
Sí, en lugar de dar el tipo de variables, puede usar valores predeterminados o pruebas unitarias (doctests en línea) en Python. También en Python a veces puedes tener errores extraños con faltas de ortografía [es menos probable que ocurra si usas autocompletar que a menudo se puede usar aunque no todo el tiempo en lenguajes dinámicos] y tienes que averiguar si self.bread = 5 está introduciendo pan o redefiniéndolo.
aoeu256
123

A medida que los tipos se fortalecen, pueden ayudarte más, si los usas correctamente en lugar de luchar contra ellos. Diseñe sus tipos para reflejar el espacio del problema y es más probable que los errores lógicos se conviertan en desajustes de tipo en tiempo de compilación en lugar de fallas en el tiempo de ejecución o resultados sin sentido.

geekosaur
fuente
37
+1! "Es más probable que los errores lógicos se conviertan en desajustes de tipo en tiempo de compilación en lugar de fallas en el tiempo de ejecución o resultados sin sentido": ¡Muy buena respuesta! Cuando tomo más tiempo para diseñar mis tipos, el código sigue de forma más natural y, a menudo, es correcto tan pronto como se compila. Diseñar un tipo significa comprender un dominio y sus operaciones.
Giorgio
78

Descargo de responsabilidad: soy un amante de los tipos;)

Su pregunta es difícil de responder: ¿Cuáles son esas compensaciones ?

Tomaré un ejemplo extremo: Haskell , está estáticamente escrito. Quizás uno de los idiomas más fuertemente tipados que existen, de hecho.

Sin embargo, Haskell admite la programación genérica , en el sentido de que usted escribe métodos que funcionan con cualquier tipo conforme a un determinado concepto (o interfaz).

Además, Haskell usa la inferencia de tipos , por lo que nunca tendrá que declarar el tipo de sus variables. Se calculan estáticamente durante la compilación, de la misma manera que un intérprete de Python los calcularía ejecutando el programa.

Descubrí que la mayoría de las personas que critican el tipeo estático, en realidad se quejaban de otra cosa (verbosidad, dolor de cambiar un tipo a favor de otro), pero Haskell no muestra ninguno de esos problemas, mientras está tipado estáticamente ...


Ejemplo de brevedad:

-- type
factorial :: Integer -> Integer

-- using recursion
factorial 0 = 1
factorial n = n * factorial (n - 1)

Además del soporte incorporado, es difícil ser más breve.

Ejemplo de programación genérica:

> reverse "hell­o" -- Strings are list of Char in Haskell
=> "olleh"
> reverse [1, 2, 3, 4, 5]
=> [5,4,3,2,1]

Ejemplo de inferencia de tipo:

> :t rever­se "hell­o"
:: [Char]

que se puede calcular simplemente:

  • "hello"es una lista de Char(expresado como [Char])
  • reverseaplicado a un tipo [A]devuelve un tipo[A]

Pruébalo en tu navegador

Matthieu M.
fuente
44
Para jugar al abogado del diablo, una compensación a favor de los lenguajes dinámicos (al menos durante la creación de prototipos) es que, en la medida en que las declaraciones de tipo pueden tener el mismo propósito que algunas pruebas unitarias, también pueden solidificar las interfaces de la misma manera que las pruebas unitarias ( aunque ciertamente con menos gastos generales). Además, los lenguajes tipados estáticamente sin coerción, aunque son más seguros, requieren una conversión de tipo explícita (particularmente cuando un tipo no es lo suficientemente genérico), lo que puede restarle claridad.
TR
77
No conozco a Haskell, pero +1 para "en realidad se quejaba de otra cosa (verbosidad, dolor de cambiar de un tipo a otro)"
Nicole
1
@Aidan: Haskell es un lenguaje en evolución. Haskell 98 fue una mejora sobre Haskell 1.4; Haskell 2010 fue una mejora sobre eso. Mientras tanto, vale la pena señalar que durante la mayor parte de su vida, la razón de ser de Haskell fue ayudar a explorar los sistemas de tipos; Las clases de tipos de parámetros múltiples son un ejemplo de dónde logró dilucidar una extensión útil del sistema de tipos. (Por otro lado, las dependencias funcionales parecen ser un callejón sin salida)
Geekosaur
44
@Matthieu: WRT "Quizás uno de los idiomas más fuertemente tipados que existen, de hecho", veré tu Haskell y te criaré Agda y Coq . (Reconoceré que es probablemente el lenguaje prácticamente útil más tipeado )
Geekosaur
1
@Matthieu: los asistentes de prueba son una aplicación directa de la correspondencia de Curry-Howard, por lo que son, en esencia, lenguajes de programación (aunque con bibliotecas estándar bastante limitadas). Están a la vanguardia de la investigación de tipos dependientes porque necesita tipos dependientes para hacer un buen uso de la correspondencia de "los tipos son proposiciones".
geekosaur
37

Me gustan los lenguajes de tipo estático y de tipo dinámico. Las dos mayores ventajas de la seguridad de tipo para mí son:

1) A menudo puede deducir lo que hace una función únicamente a partir de su firma de tipo (esto es particularmente cierto en lenguajes funcionales como Haskell).

2) Cuando haces una refactorización significativa, el compilador te dice automáticamente todo lo que tienes que hacer para que todo funcione. Cuando refactorizo ​​algo en C ++, mi procedimiento a menudo es simplemente a) cambiar la parte que sé que quiero cambiar, luego b) corregir cada error de compilación.

dfan
fuente
Exactamente lo mismo conmigo, y cada vez que quiero refactorizar algo (principalmente uso golang / typescript / java), sí, esos 2 pasos son los que cualquiera necesitaría. cambie una parte, luego corrija todos los errores de compilación :) respuesta perfecta.
Nishchal Gautam
29

Personalmente, creo que la seguridad de tipos me ayuda a desarrollarme más rápido en mi trabajo actual. El compilador realiza muchas comprobaciones de cordura por mí casi mientras escribo, lo que me permite concentrarme más en la lógica de negocios que estoy implementando.

La conclusión para mí es que, aunque pierdo algo de flexibilidad, gano algo de tiempo que de otro modo se gastaría en rastrear problemas de tipo.

Michael K
fuente
13

Hay muchas opiniones fuertes en torno al debate, pero obviamente esto no es realmente una cuestión de opinión, es una cuestión de hechos . Entonces deberíamos mirar la investigación empírica . Y la evidencia de eso es clara:

, la escritura estática vale la pena, y no solo un poco, sino de hecho de manera sustancial . De hecho, la evidencia sólida muestra que la escritura estática puede reducir la cantidad de errores en el código en al menos un 15% (y esta es una estimación baja, el porcentaje real es casi seguro mayor). Ese es un número sorprendentemente alto : creo que incluso la mayoría de los defensores de la escritura estática no habrían pensado que marcara una diferencia tan drástica.

Considere esto: si alguien le dijo que había una manera simple de reducir los errores en su proyecto en un 15% durante la noche, sería obvio. 1 Es casi la proverbial bala de plata.

La evidencia se presenta en el documento To Type or Not to Type: Quantifying Detectable Bugs in JavaScript por Zheng Gao, Christian Bird y Earl T. Barr. Animo a todos a leerlo, es un artículo bien escrito que presenta una investigación ejemplar.

Es difícil resumir sucintamente cuán rigurosamente los autores realizaron su análisis, pero aquí hay un bosquejo (muy aproximado):

TypeScript y Flow son dos lenguajes de programación basados ​​en JavaScript que, si bien son compatibles, agregan sugerencias de tipo y verificación de tipo estático. Esto permite aumentar el código existente por tipos y luego escribirlo.

Los investigadores recopilaron proyectos de código abierto escritos en JavaScript desde GitHub, analizaron los informes de errores resueltos e intentaron reducir cada uno de los errores informados a un código que sería atrapado por el verificador de tipo estático de TypeScript o Flow. Esto les permitió estimar que un límite inferior del porcentaje de errores podría repararse mediante el uso de la escritura estática.

Los investigadores tomaron precauciones estrictas para asegurarse de que su análisis no consideraría un error no relacionado con el tipo como relacionado con los tipos. 2

En comparación con estudios anteriores, este nuevo estudio tiene fortalezas particulares:

  • Hay una comparación directa de la tipificación estática frente a la dinámica, con pocos (si los hay) factores de confusión, ya que la única diferencia entre JavaScript y TypeScript / Flow es la tipificación.
  • Realizan la replicación a través de múltiples dimensiones al verificar TypeScript y Flow (es decir, diferentes sistemas de tipos), y al hacer que diferentes personas reproduzcan la anotación de tipo (manual) para corregir los errores. Y realizan esto en una gran cantidad de bases de código de diferentes proyectos.
  • El documento mide el impacto directo de la escritura estática en errores reparables (en lugar de algunos de calidad más vaga).
  • Los autores definen un modelo riguroso de qué medir y cómo, por adelantado. Además, su descripción es increíblemente clara y facilita el análisis de fallas (siempre es bueno cuando un trabajo de investigación se abre a los ataques: si ningún ataque logra dañar sus argumentos, se vuelve aún más fuerte). 3
  • Realizan un análisis de potencia adecuado para que su tamaño de muestra sea suficiente y su posterior análisis estadístico sea hermético.
  • Son demasiado conservadores para excluir explicaciones confusas, y solo miden una sola parte móvil. Además, restringen su análisis a errores que se pueden corregir de inmediato al incluir tipos y excluyen cualquier cosa que pueda requerir una refactorización más avanzada para acomodar la escritura. Entonces, en realidad, el efecto es mucho más grande, pero ciertamente no más pequeño de lo que informaron.
  • Y finalmente, no encuentran un ligero efecto, sino una asombrosa diferencia. A pesar de su procedimiento demasiado conservador, incluso en el extremo inferior del intervalo de confianza del 95% descubren que hay al menos el 10% de los errores que simplemente desaparecerían con un mínimo de controles de tipo agregado.

A menos que haya una falla fundamental en el documento que nadie haya descubierto aún, el documento muestra de manera concluyente un gran beneficio del tipeo estático, casi sin costo. 4 4


En una nota histórica, la investigación sobre las disciplinas de mecanografía en la programación ha tenido un comienzo difícil porque, durante mucho tiempo, la evidencia no estaba clara en absoluto. La razón de esto es que hacer experimentos sistemáticos para examinar el efecto del tipeo estático vs dinámico no es fácil: un experimento sistemático debe aislar el efecto que estamos investigando. Y desafortunadamente no podemos aislar el efecto de la disciplina de escritura, ya que está vinculada a los lenguajes de programación.

En realidad, había lenguajes de programación que permitían la escritura tanto estática como dinámica en diferentes dialectos (por ejemplo, VB con Option Strict Ono Off, o Lisp con escritura estática). Sin embargo, estos no eran adecuados para una comparación directa, lo más importante porque no existían bases de código suficientemente grandes que permitieran la comparación directa. En el mejor de los casos, podríamos compararlos en "entornos de laboratorio", donde los sujetos de prueba resuelven aleatoriamente una tarea en la variante del lenguaje escrita de forma estática o dinámica.

Desafortunadamente, estas tareas de programación artificial no modelan bien el uso en el mundo real. En particular, muchos de ellos tienen un alcance pequeño y resuelven un problema bien definido que puede resumirse en media página de texto.

Afortunadamente, eso está en el pasado, porque TypeScript, Flow y JavaScript son, de hecho, los mismos lenguajes, excepto por la escritura estática, y porque hay un extenso conjunto de datos del mundo real de código y errores de los que se puede probar.


1 Inspirado en una cita del artículo original.

2 No estoy del todo contento con esto: una de las principales fortalezas de los lenguajes estáticamente tipados es que los problemas aparentemente no relacionados con el tipo de texto pueden expresarse de manera que se puedan verificar de forma estática. Esto transforma muchos errores lógicos en errores de tipo, lo que aumenta drásticamente la tasa de errores que pueden ser detectados por la escritura estática. De hecho, el documento clasifica aproximadamente los errores no relacionados con el tipo y afirmo que un gran porcentaje de ellos podría ser atrapado por la escritura estática.

3 Invito a cualquiera, especialmente a los defensores de la tipificación dinámica, a tratar de encontrar fallas no abordadas en el análisis. No creo que haya muchos (si los hay), y estoy seguro de que ningún defecto potencial alteraría materialmente el resultado.

4 Sospecho que el costo real de la escritura estática en proyectos reales a gran escala es inexistente, ya que luego se convierte en una parte natural de la arquitectura e incluso podría simplificar la planificación. La reparación de errores de tipo estático lleva tiempo, pero mucho menos que los errores descubiertos más tarde. Esto ha sido ampliamente estudiado empíricamente y se conoce desde hace décadas (véase, por ejemplo, Código completo ).

Konrad Rudolph
fuente
3
Sé que esta es una respuesta tardía a esta pregunta, pero creo que la nueva evidencia (que explico aquí) cambia el debate estático vs dinámico completo.
Konrad Rudolph
2
Sin duda es interesante, pero me pregunto cuánto se relaciona con el sistema de tipos particular de javascript. El sistema de tipos de Python (especialmente python3) es mucho más estricto con muchas menos conversiones implícitas.
Peter Green
@PeterGreen Sí, eso es sin duda cierto. Tal vez tengamos suerte, y las sugerencias de tipo de Python conducirán a un análisis similar en el futuro (aunque lo dudo, ya que el propósito expreso en PEP484 y PEP526 es no implementar la escritura estática).
Konrad Rudolph
1
Solo leyendo el resumen ya puedo decir que la metodología es fundamentalmente defectuosa. No puede usar una base de código escrita con una disciplina y luego simplemente agregar tipos para justificar argumentos en una disciplina totalmente diferente. El código escrito como disciplina estática se ve fundamentalmente muy diferente de la disciplina dinámica, no debería escribir Java en Python, al igual que no debería escribir Python en Java. Incluso el mecanografiado y javascript son lenguajes fundamentalmente diferentes, a pesar de las similitudes superficiales.
Mentira Ryan
2
@LieRyan Si hay algo que hace que el análisis sea demasiado conservador, como se señaló en mi descripción y en otros lugares. De ninguna manera invalida el análisis. Su estimación del 1% es, sinceramente, ridícula. Está completamente apagado, tu intuición te está decepcionando. Del mismo modo, su caracterización de problemas con la escritura estática es típica de un profesional de la escritura dinámica que tenía poca experiencia real con la escritura estática moderna (es decir, no solo Java).
Konrad Rudolph
12

¿Vale la pena la seguridad de tipos para acelerar el desarrollo y la flexibilidad?

Así que realmente esto se reduce a lo que estás haciendo. Si está programando, por ejemplo, los sistemas de respaldo para aviones, escriba seguridad es probablemente el camino a seguir.

La programación de lenguaje dinámico vs lenguaje estático es realmente dos animales diferentes. Ambos requieren un enfoque fundamentalmente diferente el uno del otro. En su mayoría, puede portar un método de enfoque entre estático y dinámico, pero perderá las ventajas del otro.

Es una mentalidad realmente. ¿Es uno mejor que el otro? Eso realmente depende de quién eres y cómo piensas. La mayoría de las personas con las que trabajo nunca tocarían un lenguaje dinámico si no tuvieran que hacerlo, porque sienten que hay demasiado margen de error. ¿Se equivocan al pensar esto? No, por supuesto que no, pero significa que se han dado cuenta de que su enfoque de aplicar su estilo de codificación no funcionará en un entorno dinámico. Otras personas con las que voy a grupos de usuarios son exactamente lo contrario. Encuentran la escritura estática demasiado engorrosa, ya que limita su enfoque para resolver ciertos tipos de problemas.

Puedo decir honestamente, salto mucho entre JavaScript y C #. Ahora, saber y trabajar en ambos idiomas influye en el otro hasta cierto punto, pero en verdad, el código que escribo en cada aspecto es completamente diferente del otro. Requieren un enfoque diferente, porque son fundamentalmente diferentes. Lo que he encontrado es que si te encuentras pensando: "Hombre, esto es mucho más difícil de hacer en lenguaje X", tu enfoque probablemente sea un poco desagradable. Aquí hay un ejemplo, la gente habla sobre la forma "pitónica" de hacer las cosas. Lo que significa es que hay una forma en que el lenguaje Python funciona para facilitar un problema. Hacerlo de otra manera es generalmente más difícil y más engorroso. Tienes que superar la dificultad de saber cómo funciona un idioma para que realmente funcione para ti. Eso'

kemiller2002
fuente
Durante un tiempo he tenido la impresión de que los lenguajes de programación solo deberían ocultar las características de su código en las que nunca tendrá que pensar. Esto es válido para llevar el código de la máquina hasta algo de nivel superior como Java porque ese nivel inferior de implementación es algo con lo que básicamente nunca tiene que lidiar. Este no es el caso para los tipos de objetos. En mi opinión, el tipeo dinámico simplemente dificulta la programación porque introduce una clase completa de errores que debes atrapar.
MCllorf
7

Recientemente se hizo una pregunta similar: lenguajes dinámicos vs estáticamente escritos para sitios web

Para reafirmar el núcleo de mi respuesta:

A medida que los sistemas crecen, los lenguajes de tipo estático aseguran solidez a nivel de componente y, por lo tanto, flexibilidad a nivel de sistema.

Sí, Java está estrictamente tipado y sí, Java apesta (sin ofender. Es horrible. Gran plataforma y ecosistema, pero uno de los peores lenguajes jamás utilizados (en realidad)).
Pero deduciendo de eso, que escribir estrictamente apesta es solo una falacia. Es como apuntar a PHP e inferir que el tipeo dinámico es una mierda (de nuevo, sin ofender. Está mejorando lentamente, te doy eso).

Personalmente, hago la mayor parte de mi desarrollo en haXe , que tiene un sistema de tipo estático. No solo es significativamente más expresivo que el de Java y requiere mucho menos esfuerzo debido a la inferencia de tipos, sino que también es opcional. En caso de que alguna vez se interponga en tu camino, simplemente lo evitas.

La seguridad de tipografía es una característica (esto es algo que muchos idiomas supuestamente de alto nivel no entienden bien) para ayudarlo a evitar dispararse en el pie .
Y sobre cualquier lenguaje exitoso escrito dinámicamente sería simplemente mejor, si tuviera la opción de verificar su tipo de código a voluntad.
Por ejemplo, ciertamente disfruté experimentar Ruby, pero eso se debió principalmente a que Ruby está totalmente orientado a objetos, lo que es completamente ortogonal a la presencia de un sistema de compilación de tiempo.

Creo que la afirmación de que los sistemas de tipo estático son obstructivos se basa simplemente en la falta de conocimiento de buenos sistemas de tipo estático. Hay varios idiomas que lo hacen bien, haXe es uno de ellos, y posiblemente ni siquiera el mejor en ese sentido.

Ejemplo de código haXe:

class Car {
    public function new();
    public function wroom() trace('wroooooooom!')
}
class Duck {
    public function new();
    public function quack(at) trace('quackquack, ' + at + '!')
}

function letQuack(o) o.quack();
letQuack(new Car());
letQuack(new Duck());

Esto producirá un error de tiempo de compilación:

Car should be { quack : Void -> Unknown<0> }
Car has no field quack
For function argument 'o'
Duck should be { quack : Void -> Unknown<0> }
Invalid type for field quack :
to : String -> Void should be Void -> Unknown<0>
For function argument 'o'

Realmente no puedes decir que tuve que poner mucho esfuerzo en la seguridad de tipos.

Decir que no necesitas la seguridad de tipos, porque tienes pruebas es aún más idiota. Escribir exámenes es aburrido y repetitivo. Y realmente no quiero escribir una prueba, solo para descubrir que una instancia de Car no graznará y un Duck necesita a alguien a quien graznar.

Al final del día, descubrirá que, no importa cuánto le haya costado la seguridad del tipo de sobrecarga, eventualmente queda inmortalizado (incluso en Java, aunque tal vez no tan pronto).

back2dos
fuente
En python, los doctests solo se copian y pegan desde el repl / shell, como fuente de documentación y verificación posterior. docs.python.org/3/library/doctest.html
aoeu256
5

Por alguna razón, ya no cometo errores relacionados con el tipo de objeto. En lenguajes como C #, es más probable que cometa errores relacionados con los lanzamientos de tiempo de ejecución que es probable que cometa un error de seguridad de tipo detectable por el compilador, lo cual, reconozco, generalmente es causado por la necesidad ocasional de evitar la estática de un estático lenguaje escrito Cuando escribo ruby, el código tiende a insinuar con bastante fuerza el tipo de objeto y la disponibilidad de un REPL significa que ya verifiqué experimentalmente que existen los métodos / atributos deseados, o tendré una prueba unitaria que sí lo hace. básicamente lo mismo, por lo que rara vez me encuentro con problemas de seguridad tipo en ruby.

Pero eso no quiere decir que los sistemas con tipos estáticos no puedan ser mejores de lo que son.

Dentro de los idiomas tipados estáticamente, el sistema de tipos realmente importa mucho también. Como ejemplo, con algo como la mónada Some en lenguajes funcionales (tipo <Algunos>: = sí x | no), obtiene comprobaciones en tiempo de compilación que esencialmente evitan la temida NullReferenceException común en la mayoría de los sistemas de tipos; cuando se ejecuta el código de coincidencia de patrones, obtiene errores de tiempo de compilación que le indican que no pudo manejar la condición nula (si usa ese mecanismo para declarar el tipo). También reduce tipos similares de errores cuando usa cosas como el operador de canalización |> en F #.

En la tradición Hindley-Milner de tipeo estático, puede construir cosas que le brindan mucho más que una garantía de que un tipo afirma admitir la interfaz X, y una vez que tenga esas cosas, diría que el sistema estáticamente tipado se vuelve mucho más valioso.

Cuando esa no es una opción, las extensiones de Diseño por contrato para C # pueden agregar otro conjunto de mecanismos que aumentan el valor del sistema de tipo estático, pero aún requieren más disciplina que algunos de esos paradigmas funcionales.

JasonTrue
fuente
5

Depende.

Los modos de falla humana son a menudo estadísticos. La verificación de tipo fuerte reduce la probabilidad de algunos tipos determinados de fallas humanas (que causan código defectuoso). Pero solo porque puedes fallar no siempre significa que lo harás (Murphy no lo soporta).

Si esta reducción en las probabilidades de fallas potenciales vale el costo depende.

Si está escribiendo código para una planta de energía nuclear o un sistema ATC, cualquier reducción del modo de falla humana podría ser extremadamente importante. Si está creando un prototipo rápido de una idea de un sitio web que no tiene especificaciones y tiene consecuencias de falla casi nulas, entonces la reducción en los modos o probabilidades de falla puede o no comprarle algo, pero puede costarle tiempo de desarrollo (más pulsaciones de teclas, etc.), y en las células cerebrales distraídas al memorizar los tipos actuales requeridos.

hotpaw2
fuente
3
Se sugiere que su escenario de creación rápida de prototipos es incorrecto en el artículo de Paul Hudak sobre un estudio de la marina estadounidense que requería desarrollar una simulación similar a AEGIS en diferentes idiomas, uno de los cuales era Haskell. Cumple con casi todos sus criterios: fue la creación rápida de prototipos, los requisitos estaban mal definidos y el costo de la falla fue casi cero (este es un experimento extremadamente informal). ¡Haskell salió ganador en todas las categorías: tiempo de desarrollo, excediendo los requisitos, requiriendo menos LOC y produciendo el único ejemplo de trabajo entre todos los concursantes!
Andres F.
2
El artículo: Haskell vs ..., un experimento en productividad de prototipos de software - Paul Hudak y Mark P. Jones . Describe los resultados de un experimento ordenado por ARPA y la Marina de los EE. UU.
Andres F.
¿No fue el ganador Relacional Lisp? Hombre, desearía que hubiera videos que mostraran a personas codificando cosas en Lisp con todas esas extensiones extrañas y potentes como Shen (un marco lógico-relacional que te permite dar tipos dependientes al código y mezclar y combinar códigos de tipo con código que no es de tipo ), marcos súper CLOS con despacho de predicados, etc ...
aoeu256
4

Se han escrito muchos sistemas muy complicados en Lisp, y no he escuchado a ningún Lisper quejarse de que querían escribir estática. Cuando trabajé con él, no recuerdo ningún problema que me haya frenado tanto como un sistema de tipos estático (y puede especificar tipos estáticamente en Common Lisp) habría detectado.

Por otra parte, los lenguajes de tipo estático convencionales no parecen ser adecuados para detectar errores. En el diseño de un diseño, lo que es importante es que un cierto número es una medida vertical en la página, no si se trata de int, unsigned, float, o double. El compilador, por otro lado, a menudo marca las conversiones de tipos que considera inseguras, y felizmente me permite agregar una medida vertical y el número de caracteres en una cadena. Esta debilidad del sistema de tipo estático fue la idea original detrás de la notación húngara de Simonyi, antes de convertirse en una fea inutilidad.

David Thornley
fuente
4

Los tipos son restricciones en las interfaces, por lo que son un subconjunto de lo que es posible que desee probar con las pruebas unitarias, por lo que muchas de las compensaciones son similares:

  • Los tipos estáticos brindan retroalimentación anterior sobre si el código cumple o no con los requisitos que puede expresar el sistema de tipos, a cambio de retrasar la retroalimentación al construir algo mínimamente funcional (como la retroalimentación del cliente o las pruebas de nivel superior).
  • Saber que el código cumple con ciertos requisitos puede facilitar la refactorización y la depuración, pero también agrega gastos generales a las interfaces cambiantes y los requisitos cambiantes.
  • Particularmente si un lenguaje de tipo estático carece de coerción, proporciona seguridad adicional contra el código que se usa en datos que causarían errores (reduciendo la necesidad de condicionales y afirmaciones), pero las restricciones excesivamente restrictivas requieren que el usuario escriba más código para masajear sus datos en un forma aceptable (como la conversión de tipos explícita).
  • Las anotaciones de tipo explícito pueden ayudar a la comprensión al leer el código, o pueden saturar el código con información redundante o innecesaria.
  • Dependiendo de la implementación, puede disminuir la brevedad. Esto depende de cosas como si las anotaciones de tipo son necesarias o inferidas, qué tan bien el sistema de tipos puede expresar tipos / interfaces genéricos, la sintaxis y si pretendía probar o no las restricciones que puede expresar el sistema de tipos (es decir, Es probable que la misma prueba sea más concisa como una función de idioma que como una prueba unitaria, pero es posible que no haya tenido la intención de probarla).
  • Además (pero no relacionado con TDD), los tipos estáticos pueden ayudar a la optimización del tiempo de compilación, a expensas de exigir que se verifiquen los tipos (y tomarse el tiempo para verificarlos y realizar las optimizaciones), y se puede hacer una mejor optimización si los datos están restringidos a tipos ese mapa bien al hardware. Esto facilita el desarrollo de código con requisitos de rendimiento, pero puede causar problemas para el código que no se ajusta bien a estas restricciones (según el punto 3).

Para resumir, diría que los lenguajes dinámicos son particularmente útiles para la creación de prototipos, mientras que si necesita asegurarse de que su código sea correcto, debe favorecer un sistema de tipo fuerte.

T.R.
fuente
3

Sí definitivamente. Una cosa que encontrará al usar tanto los lenguajes fuertemente tipados como Python (Python está fuertemente tipado) más es que el código más bien escrito en lenguajes dinámicos tiende a seguir muchas de las mismas convenciones que el código fuertemente tipado de todos modos. La escritura dinámica es muy útil para la serialización y la deserialización, pero para la mayoría de las otras cosas realmente no aporta mucha ventaja. Y a menos que la mayor parte de su código esté relacionado con la serialización, ¿por qué descartar la verificación de errores gratuita?

Mason Wheeler
fuente
44
Los lenguajes fuertemente tipados como Java y C # manejan la deserialización automáticamente mediante el uso de Reflection.
Matthieu M.
3

Morgan, tengo una idea interesante para que pruebes: escritura estática + dinámica. Mencionaste Python, C # y Java. ¿Sabía que hay algunos puertos bastante buenos de Python para .NET y Java? En ambos casos, los puertos le permiten usar las bibliotecas de esas plataformas y / o interoperar con el código existente. Esto le brinda varias posibilidades:

  1. Mantenga el código heredado en lenguaje estático e inflexible. Usa Python para cosas nuevas.
  2. Use Python para crear prototipos de cosas nuevas sobre plataformas maduras. Vuelva a codificar los componentes que desea mantener en el lenguaje más maduro.
  3. Use el lenguaje dinámico para las porciones que cambia con frecuencia.
  4. Posiblemente use el lenguaje dinámico para jugar con ideas como modificar el código en ejecución.
  5. Haga todo en el lenguaje dinámico, excepto las partes críticas donde utiliza el lenguaje fuertemente tipado.

Utilicé estos enfoques desde fines de los 90 para evitar el dolor de desarrollar C / C ++. Necesitaba las bibliotecas nativas y, a veces, el rendimiento. Sin embargo, quería la mejor sintaxis, flexibilidad, seguridad, etc. Por lo tanto, el truco consistía en combinarlos cuidadosamente para obtener las compensaciones correctas. A menudo era mejor en la práctica que tirar todo el lenguaje y el código heredado a otro idioma / plataforma.

(Nota: una respuesta ya lo dijo, pero también quiero volver a enfatizar que la escritura dinámica! = No / escritura débil. Muchos sistemas de tipos dinámicos usan escritura fuerte en el interior. La forma en que pienso sobre lo que hace una dinámica de tipos es que un tipo de variables se determina en tiempo de ejecución, no necesita una anotación de tipo y / o puede cambiar en tiempo de ejecución.

Nick P
fuente
2

No obtendrá una respuesta realmente objetiva a eso, pero mi experiencia es que la seguridad de tipos es invaluable hasta que domine TDD. Una vez que tiene una gran cobertura de pruebas unitarias, donde las pruebas se han escrito antes del código, la verificación del compilador se convierte en una molestia y en realidad comienza a interponerse en su camino.

pdr
fuente
este es un control de calidad subjetivo, así que estoy bien con eso.
Morgan Herlocker
1
¿Alguien quiere explicar los votos negativos?
pdr
No puedo ayudarte con la explicación pero te di un +1, creo que esta es una contribución útil. Uno de los temores clave con la escritura dinámica es que hará un cambio en algún lugar y romperá algo en otro lugar debido a los supuestos que el compilador habría aplicado en un lenguaje estáticamente escrito. La cobertura de prueba de unidad pesada lo protegerá aquí.
Carson63000
55
No hice un voto negativo ya que hiciste un punto válido, aunque no fue una ofensa, pero tu publicación parece un poco fanático de TDD, lo que probablemente sea la razón por la que votaron negativamente.
Karl Bielefeldt
@Karl, Sin ofender, fue una pregunta genuina. Puedo ser sin disculpas pro-TDD, lo admito
pdr
2

Veo que esta pregunta surge mucho y creo que la calidad de su software (y la falta de errores) tiene más que ver con su proceso de desarrollo, cómo está diseñado su sistema y el compromiso de usted y sus pares con la calidad del código.

Mi último trabajo fue principalmente desarrollo de Python. Trabajé para una gran empresa internacional de alojamiento web y teníamos equipos de desarrollo en los EE. UU., Canadá y Corea del Sur. Marco web personalizado de Python para la aplicación de cliente front-end que permitió a los usuarios administrar sus nombres de dominio y cuentas de alojamiento web. Backend: todo python también. Servicio web de Python para hablar con servidores individuales para hacer cosas como aprovisionar un nuevo sitio de alojamiento web, crear un nuevo blog, crear entradas de DNS en nuestro sistema de servicio de nombres; etc, etc. En mi trabajo actual, las aplicaciones cliente están todas en Java; Nuestro producto principal es una mezcla de Java y Flash. Marco web java personalizado para nuestras aplicaciones más antiguas, wicket para nuestras herramientas internas más nuevas.

Después de haber trabajado en ambos, debo decir que esta pregunta me molesta cada vez que la veo. Si está utilizando un lenguaje de tipo dinámico y realmente prueba su código, estará bien. Si el sistema está bien diseñado y sigue los estándares, estará bien. Nunca surgieron muchos errores debido a la falta de un compilador que verificara los tipos. La mayoría de los errores fueron errores lógicos, al igual que mi trabajo en Java hoy.

LGriffel
fuente
2

¿Vale la pena la seguridad de tipos para acelerar el desarrollo y la flexibilidad? ¿POR QUÉ?

La escritura estática es un aumento neto en la velocidad y flexibilidad del desarrollo a lo largo del ciclo de vida del software. Reduce el esfuerzo total y las molestias, pero mueve mucho el esfuerzo y las molestias por adelantado, donde es más notable. La barrera de entrada para tener un código de trabajo es mayor, pero una vez que superas esa barrera (al satisfacer el verificador de tipo), extender y mantener ese código requiere mucho menos trabajo.

Siempre habrá algunos dolores de cabeza en el desarrollo de software debido a:

  • La complejidad inherente de lo que está tratando de lograr.

  • La falibilidad inherente de los humanos, especialmente teniendo en cuenta que cometemos más errores cuando intentamos hacer algo más complejo

Tarde o temprano, debe tomarse un tiempo para abordar estos desafíos. No hay forma de evitar eso. La escritura estática simplemente aborda estos desafíos más temprano que tarde. Más pronto es mejor que más tarde, porque cuanto más tarde descubras un error (no una cuestión de si , sino cuándo ), más cuesta corregir ese error.

Cuesta mucho menos corregir un error informado por un verificador de tipo que depurar una excepción relacionada con el tipo generada en tiempo de ejecución. Aplazar la verificación de tipo al tiempo de ejecución es simplemente barrer el problema debajo de la alfombra.

Jordán
fuente
1

Esta es solo mi propia opinión, pero no, no creo que el tipo de seguridad valga la pena. Ni siquiera por un segundo.

He sido desarrollador durante mucho tiempo. Comenzando con c ++, c #, luego se movió a javascript (frontend y backend a través de node.js). Desde que he estado desarrollando JavaScript, mi productividad se ha disparado, hasta el punto de que realmente me agrado usando lenguajes basados ​​en tipos. También estoy en contra de la compilación, quiero que todo esté en tiempo de ejecución ahora. Los idiomas interpretados es realmente donde encontré mi amor por la programación.

En cuanto a los tipos, simplemente no veo ningún beneficio. Ahora veo los tipos de la misma manera que veo la administración de memoria. Completamente innecesario. Los idiomas del mañana deberían proteger completamente al desarrollador de saber algo sobre los tipos. La computadora debe comprender los tipos y dejar al desarrollador fuera de ella.

Aquí hay un ejemplo. Estaba usando Swift (el nuevo lenguaje de Apple) con la esperanza de que realmente estuviera a la altura de su nombre hace un día e intenté hacerlo: var n = 1/2 no funcionó. Yo pensaba, ¿qué está pasando aquí? y luego tristemente me di cuenta de que tenía que hacer var n: Float = 1/2. Esto me recordó cuánto odio los sistemas tipográficos y cuánto de una molestia innecesaria son.

Incluso haría un esfuerzo adicional para decir que ni siquiera quiero tipos definidos por el usuario (como las clases). No quiero tipos en absoluto. Todo lo que quiero es var y objetos. Donde cualquier objeto puede ser usado como cualquier objeto. Y los objetos son dinámicos y cambian constantemente. Donde se convierte en un problema de tiempo de ejecución en cuanto a lo que funciona y lo que no.

A los desarrolladores les encanta decir que los lenguajes mal escritos no son buenos para grandes proyectos. Pero yo diría que es todo lo contrario. Los idiomas fuertemente tipados son horrendos para grandes proyectos. Y si dice que javascript no funciona para grandes proyectos, pregúntele a Uber una compañía de más de 40 mil millones que ejecuta todo su backend en node.js / javascript o Facebook que comenzó con PHP.

En cuanto a los idiomas tipados estáticamente, no es bueno para las iteraciones rápidas de hoy. Aquí hay un ejemplo simple: tiene 10 desarrolladores trabajando en un proyecto .net con un servidor de integración continua, un desarrollador envía un error y toda la compilación se rompe, a pesar de que los 10 desarrolladores están trabajando en cosas diferentes, ahora todos están detenidos y esperando para que el desarrollador infractor corrija su error. Hablar de eficiente ¿eh? El sistema de tipos / lenguajes estáticos son interdependientes de esa manera y hacen que su código sea interdependiente. Sin embargo, los archivos de script nunca son interdependientes. Si hay un problema con uno de los scripts que no detiene la producción, todos los problemas que vería quedan en el tiempo de ejecución. Y el tiempo de ejecución nunca se detiene. Nunca se rompe Puede producir resultados incorrectos pero no

revs usuario19718
fuente
1
Una gran cantidad de "yo", no mucha sustancia de argumento. Y, por cierto, si un error "se rompe" o no, la compilación no tiene nada que ver con estática o dinámica. Si tiene pruebas unitarias y una falla, "su compilación está rota" y, con suerte, no se implementará en producción hasta que se corrija
nafg
¿Qué te hizo pensar que implicaba algo así?
nafg
Su productividad en javascript no se disparó porque javascript carecía de tipos. Su productividad se disparó porque C ++ y C # son lenguajes pesados. Los tipos Javascript + realmente harán que su productividad se dispare aún más. Nadie dijo que JavaScript es imposible para grandes proyectos. Javascript en grandes proyectos es ciertamente factible. Sin embargo, no es lo ideal. Las pruebas unitarias toman el lugar de la verificación de tipo, también las pruebas unitarias tienen cobertura de tipo limitada, mientras que la verificación de tipo tiene una cobertura del 100%.
Brian Yeh
1
@BrianYeh c ++ y c # son lenguajes pesados ​​porque se centran en los tipos. Acabo de comenzar a usar reactjs en mi trabajo y mi productividad se ha desplomado una vez más debido a su uso incesante en tipos y componentes. si te gustan los tipos y las pruebas unitarias, bien por ti. No todos compartimos este estilo de programación.
1
@foreyez reactjs no tiene tipos. Probablemente te estés refiriendo al flujo. Las pruebas unitarias toman el lugar de la verificación de tipo, por lo que si no tiene una verificación de tipo, necesita más pruebas unitarias. Las pruebas unitarias y los tipos son fuerzas opuestas. Su productividad bajando es una ilusión. Cualquier error de tipo que detecte en un idioma de tipo seguro es un error no detectado en un idioma de tipo dinámico. Solo aparece más rápido. El lenguaje de tipo seguro lo obliga a lidiar con esos errores por adelantado.
Brian Yeh
0

SI.

He trabajado en aplicaciones PHP, donde los tipos no son tan "fuertes" como en Java o C #. Por lo general, terminé "simulando tipos" porque, para evitar malas conversiones automáticas o validar datos.

Los lenguajes de tipo dinámico son buenos para los scripts del sistema operativo y las aplicaciones pequeñas y rápidas, no para aplicaciones complejas.

Resumen: si tengo que elegir entre un lenguaje de programación "Tipo débil" o "Tipo dinámico", o un Lenguaje de programación "Tipo fuerte" para una aplicación empresarial compleja, elijo el Lenguaje de programación "Tipo fuerte" .

umlcat
fuente
0

Creo que vale la pena dar un paso atrás y considerar cuándo la escritura dinámica causa problemas.

Un caso es cuando una rama de código no se prueba en absoluto, pero, francamente, es probable que el código que nunca se prueba tenga errores, ya sea que se use o no la escritura dinámica.

Sin embargo, otro problema más sutil es la sustituibilidad imperfecta.

Si un tipo es completamente incorrecto, a menos que nunca se use una ruta de código particular, es probable que se detecte rápidamente.

Por otro lado, si un tipo es un sustituto imperfecto, el código puede funcionar principalmente, pero se rompe de manera sutil que no se detectará hasta mucho más tarde.

Dos de los tipos más comunes en la programación son números y cadenas. En muchos idiomas dinámicos son sustitutos imperfectos entre sí. Por ejemplo, javascript o php, si proporciona un número en el que se espera una cadena o viceversa, su programa se ejecuta sin generar un error, pero puede comportarse mal de maneras bastante sutiles.

Python evitó ese problema en particular, los números y las cadenas no se sustituyen entre sí y tratar de usar uno donde se espera el otro normalmente conducirá a una falla rápida.

Sin embargo, no evitó por completo el problema de sustituibilidad imperfecta. Diferentes tipos de números pueden ser sustitutos imperfectos entre sí, al igual que diferentes tipos de secuencias.


Lo que estoy obteniendo aquí es que no creo que sea posible comparar los beneficios y los costos de la escritura estática y dinámica de manera genérica, porque creo que tanto los beneficios como los costos dependen de la variación particular de la escritura estática o dinámica de un idioma. usos.

Peter Green
fuente