A menudo escuché la afirmación de que los idiomas escritos dinámicamente son más productivos que los idiomas escritos estáticamente. ¿Cuáles son las razones de este reclamo? ¿No se trata solo de herramientas con conceptos modernos como la convención sobre la configuración, el uso de programación funcional, modelos de programación avanzados y el uso de abstracciones consistentes? Es cierto que hay menos desorden porque las declaraciones de tipo (por ejemplo, en Java) a menudo redundantes no son necesarias, pero también puede omitir la mayoría de las declaraciones de tipo en lenguajes estáticamente tipados que usan inferencia de tipos, sin perder las otras ventajas del tipeo estático. Y todo esto también está disponible para los idiomas modernos de tipo estático como Scala.
Entonces: ¿qué hay para decir sobre la productividad con la tipificación dinámica que realmente es una ventaja del modelo de tipo en sí?
Aclaración: estoy más interesado en proyectos grandes / medianos que en hacks rápidos. :-)
fuente
Respuestas:
De hecho, creo que está muy cerca. Tanto la escritura dinámica como la escritura estática tienen sus ventajas.
Razones para que la escritura dinámica sea más productiva:
Razones para que la escritura estática sea más productiva:
En promedio, mi conclusión (después de muchos años de experiencia en ambos lados de la cerca) es que la escritura dinámica puede ser más productiva a corto plazo, pero en última instancia se hace difícil de mantener a menos que tenga muy buenas suites de pruebas y disciplina de prueba.
Por otro lado, en realidad prefiero los enfoques de tipo estático en general, porque creo que los beneficios de corrección y el soporte de herramientas le brindan una mejor productividad a largo plazo.
fuente
Con los lenguajes dinámicos, puede escribir códigos basura más rápido que cuando usa un lenguaje escrito.
Una vez que haya creado rápidamente su enorme montón de cosas dinámicas, puede pasar de manera segura a otro proyecto sin tener que preocuparse por el mantenimiento a largo plazo.
Esto es ganancia de productividad :)
Estoy bromeando, pero después de haber estado involucrado en un proyecto usando 'lenguaje dinámico', me asusta la cantidad de pruebas innecesarias, documentaciones y convenciones con las que tiene que lidiar si desea tener un producto que funcione.
Y con la alegría de muchos errores de tiempo de ejecución que podrían haberse detectado en la compilación.
¡Oh, también olvidé despotricar sobre todos esos hacks y vudúes que la metaprogramación te permite introducir en tu código!
Por lo tanto, la ganancia de productividad podría ser un mito para proyectos medianos / grandes durante su vida útil.
fuente
También hay una visión teórica de este problema: un sistema de tipo estático es esencialmente un probador de teoremas especializado que solo acepta el programa cuando puede probar la corrección de tipo del mismo. Todos los sistemas de tipo estático rechazan algunos programas válidos porque ningún sistema de tipo estático decidible es lo suficientemente potente como para probar todos los posibles programas de tipo correcto.
Se podría argumentar que los programas que no son comprobables por un typechecker estático son hacks y / o mal estilo, pero si ya tiene un programa válido y el typechecker no lo acepta, ciertamente está afectando su productividad a corto plazo.
Algunos casos en los que puede notar que el verificador de tipos se interpone es con contenedores genéricos y co-/ contravarianza en argumentos y tipos de retorno.
fuente
Una ventaja que he encontrado con la mayoría de los lenguajes dinámicos es que hacen que sea más fácil escribir código más genérico. Es mucho más fácil escribir en un nivel superior de abstracción cuando no tienes que luchar contra el sistema de tipos para hacerlo.
No tiene que pensar tanto en ello: escribir código que haga algo no trivial con cualquier objeto en Java es difícil y probablemente requiera una reflexión que básicamente se tipea dinámicamente; con algo como JavaScript, escribir una función que haga algo interesante para todos los objetos es una segunda naturaleza. Un ejemplo perfecto sería una función que escribí recientemente que toma un objeto y reemplaza todos sus métodos por otros que hacen lo mismo pero que también activan un evento. No tengo idea de cómo abordar algo como esto en Java. Sin embargo, no estoy seguro de cuánto de esto se debe a los sistemas de tipos y cuánto se debe a otras diferencias de idioma.
Sin embargo, recientemente comencé a usar Haskell. Haskell me permite escribir código abstracto y genérico tan fácilmente como cualquier lenguaje escrito dinámicamente que haya usado. Mi ejemplo de Java / JavaScript anterior no tiene sentido en Haskell porque no tiene objetos, métodos, eventos o incluso mucha mutación, pero otros tipos de código genérico son realmente fáciles de escribir.
De hecho, Haskell puede escribir un código genérico que los lenguajes escritos dinámicamente no pueden; Un ejemplo perfecto es la
read
función que es básicamente lo contrario detoString
. Usted puede obtener unaInt
o unaDouble
o cualquier tipo que desee (siempre y cuando sea en un determinado tipo de clase). Usted puede incluso tener polimórficos constantes , por lo quemaxBound
puede ser el máximoInt
,Double
,Char
... etc., Todo dependiendo de qué tipo se supone que debe ser.Mi teoría ahora es que la ganancia de productividad al usar un lenguaje dinámico siempre es en comparación con lenguajes como Java con sistemas de tipos menos capaces, más detallados y menos flexibles.
Sin embargo, incluso el sistema de tipos de Haskell tiene algunos problemas molestos que no tendría en un lenguaje de tipo dinámico. El más grande que me ha molestado es la forma en que se manejan los números; por ejemplo, tiene que jugar con el sistema de tipos para usar
length
(de una lista) como doble, algo con lo que no tendría problemas sin un sistema de tipos. Otra cosa molesta con la que me he encontrado es trabajar conWord8
(un tipo int sin signo) y funciones que esperanInt
.Por lo tanto, en última instancia, no tener un sistema de tipos hace que sea más fácil escribir código genérico sin pensar demasiado y también evita molestias molestas de los sistemas de tipos. Nunca tiene que luchar contra el sistema de tipos en un lenguaje dinámico, pero tampoco puede confiar en él.
fuente
Int
devuelto por una lista es trivial; ejemplo:1.0 + fromIntegral (length myList)
es decir, solo usarfromIntegral
.P: A menudo escuché la afirmación de que los idiomas escritos dinámicamente son más productivos que los idiomas escritos estáticamente. ¿Cuáles son las razones de este reclamo? "
Esto tiene razones históricas. Si retrocede algunas décadas, los lenguajes dinámicos fueron indiscutiblemente mucho más productivos que los lenguajes estáticos (aunque también significativamente más lentos). Perl es claramente mucho más productivo que C si conoce ambos y la tarea en cuestión lo permite. Pero con el tiempo, los idiomas se han prestado mucho entre sí, y los idiomas más nuevos están reduciendo la brecha (tanto en productividad como en rendimiento).
Aquí hay algunos puntos a considerar:
Recolección de basura : la recolección de basura es un gran impulso de productividad. Creo que Java fue el primer lenguaje estático convencional con GC. Antes de esto, estático básicamente significaba administración manual de memoria. (Nota: Aquí y en lo siguiente solo estoy considerando los idiomas principales. Existen muchos lenguajes experimentales y especializados que proporcionarán contraejemplos a cualquier punto que haga).
Seguridad de la memoria : es una mejora de la productividad de la que no tiene que preocuparse por dispararse en el pie. Antes de los lenguajes estáticos "administrados" como Java, estático normalmente significaba acceso directo a la memoria. La depuración también es parte de la productividad, y el acceso inseguro a la memoria puede conducir a errores realmente oscuros.
Sistemas de tipo engorroso. Antes de la introducción de tipos parametrizados (como plantillas o genéricos) en lenguajes estáticos, las limitaciones de los sistemas de tipos estáticos solían ser una carga. Por ejemplo, en Java, tenía que rechazar explícitamente cada vez que seleccionaba un elemento de una colección. Entonces tienes la sobrecarga sintáctica de un elenco y ningún tipo de seguridad. Teniendo en cuenta lo ubicuas que son las colecciones en la programación, este fue un gran inconveniente.
Tener que declarar el tipo de todo es una gran cantidad de tipeo redundante, pero con la inferencia de tipos moderna, esto puede reducirse significativamente.
Gran biblioteca estándar. Python fue famoso como "baterías incluidas" debido a la gran biblioteca estándar. Esto en comparación con C, que tiene una biblioteca estándar muy minimalista. Pero con plataformas como Java y .net, una gran biblioteca estándar se está convirtiendo en estándar, y los lenguajes más nuevos como Scala y F # están heredando esto "gratis".
Estructuras de datos de primera clase. Los lenguajes dinámicos como Perl y Python tienen estructuras de datos integradas de primera clase como listas y mapas con atajos sintácticos convenientes para operaciones comunes. En comparación con esto, C no tiene colecciones integradas, excepto matrices de tamaño fijo.
Closures y sintaxis lambda : los lenguajes dinámicos suelen haber tenido esto desde el principio, pero los lenguajes estáticos lo han adoptado, Java más recientemente.
REPL la capacidad de probar rápidamente fragmentos de código de forma interactiva es una gran bendición. Pero aunque las herramientas IDE, como la ventana "inmediata" en Visual Studio, los lenguajes estáticos pueden emular esto hasta cierto punto.
Herramientas avanzadas : además de los puntos anteriores donde los lenguajes estáticos se están acercando a la conveniencia de los lenguajes dinámicos, los editores modernos están aprovechando el análisis estático de una manera que los lenguajes dinámicos tienen dificultades para hacer coincidir. Por ejemplo, los editores pueden proporcionar refactorizaciones automáticas seguras, algo que es estrictamente hablando imposible en un lenguaje dinámico.
En pocas palabras: Históricamente era cierto, pero hoy la respuesta es menos clara.
P: Entonces: ¿qué hay para decir sobre la productividad con la tipificación dinámica que realmente es una ventaja del modelo de tipo en sí?
Es algo difícil separar el modelo de escritura dinámica de los lenguajes dinámicos, pero como ejemplo, C # ha adoptado características más dinámicas con el tiempo, aunque su núcleo es un lenguaje estático. Esto es realmente una prueba de beneficio del modelo de tipo dinámico. Ejemplos:
Reflexión La reflexión es fundamentalmente una característica de escritura dinámica. Usted inspecciona los tipos de objetos en tiempo de ejecución que en tiempo de compilación. Cuando se introdujo, estaba mal visto, pero en C # el uso de la reflexión se vuelve cada vez más omnipresente, por ejemplo, ASP.Net MVC usa la reflexión en gran medida.
Atributos Los atributos son un ejemplo de tipeo dinámico. Puede agregar atributos arbitrarios a una clase en tiempo de compilación, y luego inspeccionar en tiempo de ejecución (a través de la reflexión) y manipular objetos basados en ella. Algo así como MEP es básicamente un marco de extensión basado en un modelo de tipo dinámico.
Linq a SQL, EF mv. Los diversos transformadores Linq inspeccionan las consultas como objetos de tiempo de ejecución y generan sql sobre la marcha. No se vuelve más dinámico que inspeccionar el código en tiempo de ejecución. CodeDom es el otro lado de la moneda, donde se puede generar código en tiempo de ejecución
Roslyn Roslyn básicamente implementa
eval
, lo que alguna vez se consideró la característica definitoria de un lenguaje verdaderamente dinámico.Dinámico El
dynamic
tipo es la característica más explícitamente dinámica en C #, y se anuncia para hacer que la interacción con objetos externos y lenguajes sea más simple y productiva. Pero también se usa en Asp.net MVC por conveniencia.El beneficio de todas las características anteriores muestra que el modelo dinámico tiene ventajas definidas incluso en un lenguaje estático con tipos parametrizados, tipos estructurales e inferencia de tipos.
fuente
El conjunto de todas las características del lenguaje moderno es tan grande, que el tipeo estático versus dinámico solo no tiene mucho peso.
La regla es, cuanto mejores sean las características de su idioma, más corto será su código. Eso es bastante simple. Java muestra cómo la escritura estática puede salir muy mal, lo que le da a sus oponentes mucho de qué alimentarse. Las características de lenguaje mal diseñadas generalmente tienen un costo, y la escritura estática en Java es, en primer lugar, una característica obligatoria (de lo contrario, la mayoría de las personas probablemente ni siquiera la usaría) y, en segundo lugar, se ejecuta de manera deficiente.
Es por eso que, en comparación, la mayoría de los lenguajes dinámicos brillan, aunque yo diría que PHP realmente no mejora su vida en general (al menos hasta hace poco), debido a muchas otras peculiaridades no relacionadas con los sistemas de tipos.
Por otro lado, tiene muchos idiomas con sistemas de tipo expresivo que no se interponen en su camino y que incluso no son obligatorios. Y algunos de ellos incluso permiten incrustar código sin tipo, siempre que necesite escapar del sistema de tipos.
Personalmente, uso haXe, que es un lenguaje con inferencia de tipos, subtipo nominal y estructural, código sin tipo opcional, tipos de función de primera clase, tipos de datos algebraicos y macros léxicas (no del todo maduras pero extremadamente potentes), evitando al mismo tiempo la sintaxis arcana. Después de usar haXe durante aproximadamente 3 años, he llegado a una conclusión simple:
La programación se vuelve mucho más fácil, cuando su idioma no lo encierra en elecciones religiosas sobre paradigmas, sino que trata de ser una buena herramienta. Hay varios lenguajes estáticos y dinámicos y lenguajes mixtos , que tienen éxito en eso. Algunos de ellos son fáciles de aprender, los más difíciles de dominar.
Su poder proviene de la forma en que se pueden componer sus características individuales para crear fácilmente soluciones simples a problemas complejos. Esto impide una cierta ortogonalidad que solo se puede lograr a través de un delicado equilibrio de inclusión u omisión de todas las características del lenguaje exploradas hasta ahora. Si intentas agregar tipeo estático a Ruby, lo paralizarás, si intentas quitárselo a Haskell, lo aplastarías. En contraste con eso: si se lo quitaras a C, la gente apenas lo notaría y si se lo quitases a Java, algunos podrían agradecerte.
Desde mi experiencia personal, puedo decirte esto: me gusta Ruby. Amplió mis horizontes y la forma en que diseño los sistemas. En mi humilde opinión, se debe utilizar para enseñar a la gente a programar en primer lugar. Es discreto, poderoso, conciso, divertido. Entiendo por qué alguien que viene de un idioma ortodoxo lo disfrutará.
Sin embargo, a la larga, la tipificación estática permite diferir el trabajo al analizador estático y, con la inferencia de tipos, esto se realiza básicamente sin costo. El resultado es un código que es más fácil de mantener y que a menudo se ejecuta más rápido.
Pero de nuevo, la escritura estática por sí sola no puede hacer nada. Es una cuestión de combinación. Creo que en algún lugar entre F #, Scala, Nemerle, OCaml o haXe puedes encontrar tu propio óptimo. Pero eso depende en última instancia de usted, porque el lenguaje debería permitirle incrustar sus pensamientos sin esfuerzo, en lugar de obligarlo a doblarlos. Y, después de todo, nada produce más ganancia de productividad que si la programación fuera divertida.
fuente
Personalmente, la única razón por la que la escritura dinámica ayudaría es si usted es un mecanógrafo realmente lento o si desarrolla funciones / métodos / locuciones gigantes que son difíciles de navegar. También debe abordar el problema de las pruebas de unidad completa. Los tipos dinámicos requieren (a menos que le guste escribir código roto) pruebas de unidad vigorosas (para asegurarse de que sus tipos dinámicos no exploten inesperadamente (es decir, la variable es principalmente duck, pero a veces es accidental). La estática se esforzará mucho más para evitar esto (y sí, puede argumentar a favor de pruebas unitarias vigorosas)
fuente
Creo que antes que nada debes definir la "productividad". ¿Qué significa e incluye "productividad"?
Si por "más productivo" te refieres a escribir menos líneas de código para implementar la misma característica, entonces sí, los lenguajes de programación de escritura dinámica son más "productivos" que los lenguajes de escritura estática.
Sin embargo, si también considera el tiempo dedicado a la depuración y la corrección de errores, entonces los lenguajes de escritura dinámica pueden no ser tan productivos, porque los lenguajes de escritura dinámica tienden a llevar la verificación de errores al tiempo de ejecución mientras que, por el contrario, los lenguajes de escritura estática puede realizar alguna comprobación de errores en tiempo de compilación. Como se acepta comúnmente que, por lo general, cuanto más tarde se encuentra un error, más costoso es solucionarlo. Por lo tanto, el código de escritura dinámica puede generar una productividad generalmente igual o incluso menor que la del código de escritura estática.
fuente
La gran ventaja del tipeo dinámico es la productividad.
Python, Ruby, etc. tienen muchos otros impulsores de productividad además del tipeo dinámico (parámetros predeterminados, diccionarios como tipos incorporados, etc., etc.), el efecto acumulativo en la productividad del programador es impresionante.
Las penalizaciones en términos de velocidad (¡o falta de ellas!) Y consumo de recursos no son tan malas como cabría esperar y, en la mayoría de los casos, se ven más que compensadas por la velocidad y flexibilidad de desarrollo.
Hay una (muy viejo!) De papel sobre el tema aquí. Es uno de los pocos estudios realizados adecuadamente sobre la productividad del programador y muchas de las conclusiones aún son válidas.
Lo que (probablemente) sería diferente sería el estudio que se realizaría hoy:
Entonces, el mensaje es a menos que el rendimiento sea un problema realmente serio, los lenguajes dinámicos aumentarán su productividad.
fuente