programming languages - tipados - Lenguajes de tipo dinámico frente a lenguajes de tipo estático
javascript lenguaje dinamico (9)
¿Cuáles son las ventajas y limitaciones de los lenguajes de tipo dinámico en comparación con los lenguajes de tipo estático?
Ver también : ¿Qué pasa con el amor por los lenguajes dinámicos (un hilo mucho más argumentativo ...)?
Bueno, ambos son muy, muy, muy, muy mal entendidos y también dos cosas completamente diferentes. que no son mutuamente excluyentes
Los tipos estáticos son una restricción de la gramática del lenguaje. Podría decirse estrictamente que los lenguajes tipificados estáticamente no son libres de contexto. La simple verdad es que se convierte en un inconveniente para expresar un lenguaje en forma de gramáticas libres de contexto que no trata a todos sus datos simplemente como vectores de bits. Los sistemas de tipo estático son parte de la gramática del lenguaje, si es que lo tienen, simplemente lo restringen más de lo que lo podría hacer una gramática libre de contexto, por lo que los controles gramaticales ocurren en dos pases sobre la fuente. Los tipos estáticos corresponden a la noción matemática de la teoría de tipos, la teoría de tipos en matemáticas simplemente restringe la legalidad de algunas expresiones. Al igual que, no puedo decir 3 + [4,7]
en matemáticas, esto se debe a la teoría de tipos.
Los tipos estáticos no son, por lo tanto, una manera de "prevenir errores" desde una perspectiva teórica, son una limitación de la gramática. De hecho, siempre que +, 3 y los intervalos tengan las definiciones teóricas del conjunto habitual, si eliminamos el tipo de sistema 3 + [4,7]
tiene un resultado bastante bien definido que es un conjunto. Los ''errores de tipo de tiempo de ejecución'' en teoría no existen, el uso práctico del sistema de tipos es para evitar operaciones que para los seres humanos no tendrían sentido. Las operaciones son solo el cambio y la manipulación de bits, por supuesto.
El problema con esto es que un sistema de tipos no puede decidir si tales operaciones van a ocurrir o no si se permitirá su ejecución. Al igual que en, particione exactamente el conjunto de todos los programas posibles en aquellos que van a tener un ''error de tipo'', y los que no lo son. Solo puede hacer dos cosas:
1: probar que se van a producir errores de tipo en un programa
2: demostrar que no van a ocurrir en un programa
Esto podría parecer que me estoy contradiciendo. Pero lo que hace un verificador de tipo C o Java es que rechaza un programa como "no gramatical", o como lo llama "error de tipo" si no puede tener éxito en 2. No puede probar que no van a ocurrir, eso no significa que no van a ocurrir, simplemente significa que no puede probarlo. Podría muy bien ser que un programa que no tenga un error de tipo sea rechazado simplemente porque no puede ser probado por el compilador. Un ejemplo simple es if(1) a = 3; else a = "string";
if(1) a = 3; else a = "string";
Sin duda, dado que siempre es cierto, la rama else nunca se ejecutará en el programa y no se producirá ningún error de tipo. Pero no puede probar estos casos de manera general, por lo que es rechazado. Esta es la mayor debilidad de muchos idiomas tipificados estáticamente, al protegerlo contra usted mismo, también está necesariamente protegido en los casos en que no lo necesite.
Pero, contrariamente a la creencia popular, también hay lenguajes tipificados estáticamente que funcionan según el principio 1. Simplemente rechazan todos los programas de los que pueden probar que van a causar un error de tipo, y pasan todos los programas de los que no pueden. Por lo tanto, es posible que permitan programas que tengan errores de tipo, un buen ejemplo es Typed Racket, es híbrido entre la escritura dinámica y estática. Y algunos dirían que obtienes lo mejor de ambos mundos en este sistema.
Otra ventaja de la escritura estática es que los tipos se conocen en tiempo de compilación y, por lo tanto, el compilador puede usar esto. Si en Java hacemos "string" + "string"
o 3 + 3
, los dos tokens en el texto al final representan una operación y dato completamente diferentes, el compilador sabe cuál elegir solo de los tipos.
Ahora, voy a hacer una declaración muy controvertida aquí, pero tengan paciencia: la ''escritura dinámica'' no existe .
Suena muy controvertido, pero es cierto, las lenguas tipificadas dinámicamente son desde una perspectiva teórica sin tipo . Solo son idiomas tipificados estáticamente con un solo tipo. O simplemente, son lenguajes que, de hecho, son generados gramaticalmente por una gramática libre de contexto en la práctica.
¿Por qué no tienen tipos? Debido a que cada operación está definida y permitida en cada operante, ¿qué es exactamente un ''error de tipo de tiempo de ejecución''? Es de un ejemplo teórico puramente un efecto secundario . Si hacer una print("string")
que imprime una cadena es una operación, entonces también lo es la length(3)
, la primera tiene el efecto secundario de escribir una string
en la salida estándar, la última simplemente error: function ''length'' expects array as argument.
, Eso es. Desde una perspectiva teórica no existe tal cosa como un lenguaje tipificado dinámicamente. Son desatados
De acuerdo, la ventaja obvia del lenguaje "tipificado dinámicamente" es el poder expresivo, un sistema de tipos no es más que una limitación del poder expresivo. Y, en general, los idiomas con un sistema de tipos tendrían un resultado definido para todas aquellas operaciones que no están permitidas si el sistema de tipos se ignorara, los resultados simplemente no tendrían sentido para los humanos. Muchos idiomas pierden su integridad de Turing después de aplicar un sistema de tipos.
La desventaja obvia es el hecho de que pueden ocurrir operaciones que producirían resultados sin sentido para los humanos. Para protegerse contra esto, los idiomas tipificados dinámicamente típicamente redefinen esas operaciones, en lugar de producir ese resultado sin sentido, lo redefinen para tener el efecto secundario de escribir un error, y posiblemente detener el programa por completo. Esto no es un ''error'' en absoluto, de hecho, la especificación del lenguaje generalmente implica esto, este es tanto el comportamiento del lenguaje como la impresión de una cadena desde una perspectiva teórica. Los sistemas tipográficos obligan al programador a razonar sobre el flujo del código para asegurarse de que esto no suceda. O, de hecho, la razón para que suceda también puede ser útil en algunos puntos para la depuración, demostrando que no es un ''error'' en absoluto, sino una propiedad bien definida del idioma. En efecto, el único resto de "escritura dinámica" que tienen la mayoría de los idiomas es protegerse contra una división por cero. Esto es lo que significa la tipificación dinámica, no hay tipos, no hay más tipos que ese cero es un tipo diferente que todos los otros números. Lo que la gente llama un ''tipo'' es simplemente otra propiedad de un dato, como la longitud de una matriz, o el primer carácter de una cadena. Y muchos idiomas escritos dinámicamente también le permiten escribir cosas como "error: the first character of this string should be a ''z''"
.
Otra cosa es que los idiomas escritos dinámicamente tienen el tipo disponible en el tiempo de ejecución y, por lo general, pueden verificarlo, manejarlo y decidirlo. Por supuesto, en teoría no es diferente a acceder al primer carácter de una matriz y ver qué es. De hecho, puede crear su propio C dinámico, solo use un tipo como long long int y use los primeros 8 bits del mismo para almacenar su ''tipo'' y escribir las funciones correspondientes para verificarlo y realizar una suma de números flotantes o enteros. Usted tiene un idioma escrito de forma estática con un tipo o un lenguaje dinámico.
En la práctica, todo esto muestra que los lenguajes de tipo estático se usan generalmente en el contexto de la escritura de software comercial, mientras que los lenguajes de tipo dinámico se usan en el contexto de resolver algunos problemas y automatizar algunas tareas. Escribir código en idiomas tipificados estáticamente simplemente toma mucho tiempo y es engorroso porque no puede hacer cosas que sabe que van a salir bien, pero el sistema de tipos aún lo protege contra usted mismo por los errores que no comete. Muchos programadores ni siquiera se dan cuenta de que lo hacen porque está en su sistema, pero cuando usted codifica en lenguajes estáticos, a menudo se resuelve el hecho de que el sistema de tipos no le permitirá hacer cosas que no pueden salir mal, porque No puedo probar que no saldrá mal.
Como señalé, ''tipificado estáticamente'' en general significa el caso 2, culpable hasta que se pruebe que es inocente. Pero algunos lenguajes, que no derivan su sistema de tipos de la teoría de tipos, utilizan la regla 1: Inocente hasta que se demuestre su culpabilidad, lo que podría ser el híbrido ideal. Por lo tanto, quizás Typed Racket es para ti.
Además, bueno, para un ejemplo más absurdo y extremo, actualmente estoy implementando un lenguaje donde los "tipos" son realmente el primer carácter de una matriz, son datos, datos del "tipo", "tipo", que es en sí mismo. un tipo y un dato, el único dato que tiene a sí mismo como un tipo. Los tipos no son finitos o limitados estáticamente, pero se pueden generar nuevos tipos basados en información de tiempo de ejecución.
De la escritura de Artima : Fuerte contra débil, Estático contra dinámico artículo:
la tipificación fuerte evita las operaciones de mezcla entre tipos no coincidentes. Para mezclar tipos, debes usar una conversión explícita
La escritura débil significa que puede mezclar tipos sin una conversión explícita
En el artículo de Pascal Costanza, Mecanografía dinámica frente a estática: un análisis basado en patrones (PDF), afirma que, en algunos casos, la tipificación estática es más propensa a errores que la tipificación dinámica. Algunos idiomas escritos de forma estática te obligan a emular manualmente la escritura dinámica para hacer "lo correcto". Se discute en Lambda the Ultimate .
Depende del contexto. Hay muchos beneficios que son apropiados para el sistema de tipo dinámico, así como para el tipo fuerte. Soy de la opinión de que el flujo de tipos dinámicos del lenguaje es más rápido. Los lenguajes dinámicos no están limitados por los atributos de clase y el compilador que piensa en lo que sucede en el código. Usted tiene algunos un poco de libertad. Además, el lenguaje dinámico suele ser más expresivo y da como resultado menos código, lo que es bueno. A pesar de esto, es más propenso a errores, lo que también es cuestionable y depende más de la cobertura de las pruebas unitarias. Es un prototipo fácil con lenguaje dinámico, pero el mantenimiento puede convertirse en pesadilla.
La ganancia principal sobre el sistema de tipo estático es el soporte IDE y seguramente el analizador de código estático. Usted se vuelve más seguro de código después de cada cambio de código. El mantenimiento es paz de la torta con tales herramientas.
Hay muchas cosas diferentes sobre los lenguajes estáticos y dinámicos. Para mí, la principal diferencia es que en los lenguajes dinámicos las variables no tienen tipos fijos; En cambio, los tipos están vinculados a los valores. Debido a esto, el código exacto que se ejecuta es indeterminado hasta el tiempo de ejecución.
En las implementaciones tempranas o ingenuas, este es un gran arrastre de rendimiento, pero los JIT modernos se acercan de manera tentadora a lo mejor que se puede obtener al optimizar los compiladores estáticos. (En algunos casos marginales, incluso mejor que eso).
La capacidad del intérprete para deducir las conversiones de tipo y tipo hace que el tiempo de desarrollo sea más rápido, pero también puede provocar fallas en el tiempo de ejecución que no puede obtener en un lenguaje de tipo estático donde se detectan en tiempo de compilación. Pero cuál es mejor (o incluso si eso siempre es cierto) se discute en la comunidad en estos días (y desde hace mucho tiempo).
Una buena forma de ver el problema es de Mecanografía estática donde sea posible, Escritura dinámica cuando sea necesario: El final de la Guerra Fría entre lenguajes de programación por Erik Meijer y Peter Drayton en Microsoft:
Los defensores de la tipificación estática argumentan que las ventajas de la tipificación estática incluyen la detección temprana de errores de programación (por ejemplo, la prevención de agregar un número entero a un booleano), una mejor documentación en forma de firmas de tipo (por ejemplo, la incorporación de números y tipos de argumentos al resolver nombres), más oportunidades para optimizaciones del compilador (por ejemplo, reemplazo de llamadas virtuales por llamadas directas cuando se conoce estáticamente el tipo exacto del receptor), mayor eficiencia en el tiempo de ejecución (por ejemplo, no todos los valores tienen que ser dinámicos), y una mejor experiencia de desarrollador en tiempo de diseño (por ejemplo, conocimiento el tipo de receptor, el IDE puede presentar un menú desplegable de todos los miembros aplicables). Los fanáticos de la escritura estática intentan hacernos creer que "los programas bien escritos no pueden salir mal". Si bien esto ciertamente suena impresionante, es una afirmación bastante vacía. La comprobación de tipos estáticos es una abstracción en tiempo de compilación del comportamiento en tiempo de ejecución de su programa y, por lo tanto, es necesariamente parcial e incompleta. Esto significa que los programas aún pueden salir mal debido a las propiedades que no son rastreadas por el comprobador de tipos, y que hay programas que, si bien no pueden salir mal, no pueden ser verificados. El impulso de hacer que la escritura estática sea menos parcial y más completa hace que los sistemas de tipos se vuelvan demasiado complicados y exóticos, como lo demuestran conceptos como "tipos fantasma" [11] y "tipos tambaleantes" [10]. Esto es como tratar de correr una maratón con una bola y una cadena atada a tu pierna y gritar triunfalmente que casi lo lograste aunque te fuiste a la fuga después de la primera milla.
Los defensores de los lenguajes de tipos dinámicos argumentan que la tipificación estática es demasiado rígida y que la suavidad de los lenguajes dinámicos los hace ideales para sistemas de prototipos con requisitos cambiantes o desconocidos, o que interactúan con otros sistemas que cambian de forma impredecible (integración de datos y aplicaciones). Por supuesto, los lenguajes de tipo dinámico son indispensables para tratar con un comportamiento de programa verdaderamente dinámico, como la intercepción de métodos, la carga dinámica, el código móvil, la reflexión en tiempo de ejecución, etc. En la madre de todos los documentos sobre scripting [16], John Ousterhout argumenta que los sistemas tipificados estáticamente los lenguajes de programación hacen que el código sea menos reutilizable, más detallado, no más seguro y menos expresivo que los lenguajes de scripting dinámicamente escritos. Este argumento es repitido literalmente por muchos defensores de los lenguajes de scripting tipificados dinámicamente. Argumentamos que esto es una falacia y cae en la misma categoría que argumentar que la esencia de la programación declarativa es eliminar la asignación. O como dice John Hughes [8], es lógicamente imposible hacer un lenguaje más poderoso al omitir características. Defender el hecho de que retrasar todas las comprobaciones de tipos al tiempo de ejecución es algo bueno, es jugar tácticas de avestruz con el hecho de que los errores deben detectarse tan pronto como sea posible en el proceso de desarrollo.
Los sistemas de tipo estático buscan eliminar ciertos errores de forma estática, inspeccionando el programa sin ejecutarlo e intentando probar la solidez en ciertos aspectos. Algunos sistemas tipográficos son capaces de detectar más errores que otros. Por ejemplo, C # puede eliminar las excepciones de puntero nulo cuando se usa correctamente, mientras que Java no tiene tal poder. Twelf tiene un sistema de tipo que realmente garantiza que las pruebas terminarán , "resolviendo" el problema de detención .
Sin embargo, ningún sistema tipográfico es perfecto. Para eliminar una clase particular de errores, también deben rechazar ciertos programas perfectamente válidos que violan las reglas. Esta es la razón por la que Twelf no resuelve realmente el problema de la detención, simplemente lo evita arrojando un gran número de pruebas perfectamente válidas que terminan de forma impar. Del mismo modo, el sistema de tipos de Java rechaza la implementación de PersistentVector
de Clojure debido a su uso de arreglos heterogéneos. Funciona en tiempo de ejecución, pero el sistema de tipos no puede verificarlo.
Por esa razón, la mayoría de los sistemas de tipo proporcionan "escapes", formas de anular el comprobador estático. Para la mayoría de los idiomas, estos toman la forma de casting, aunque algunos (como C # y Haskell) tienen modos completos que están marcados como "inseguros".
Subjetivamente, me gusta la escritura estática. Implementado correctamente (pista: no Java), un sistema de tipo estático puede ser de gran ayuda para eliminar los errores antes de que colapsen el sistema de producción. Los idiomas tipificados dinámicamente tienden a requerir más pruebas unitarias, lo cual es tedioso en el mejor de los casos. Además, los lenguajes tipificados estáticamente pueden tener ciertas características que son imposibles o inseguras en los sistemas de tipo dinámico (las conversiones implícitas vienen a la mente). Todo es cuestión de requerimientos y gustos subjetivos. No construiría más el próximo Eclipse en Ruby de lo que intentaría escribir un script de respaldo en Assembly o parchear un kernel utilizando Java.
Ah, y las personas que dicen que " x escribir es 10 veces más productivo que y " simplemente están echando humo. La escritura dinámica puede "sentirse" más rápido en muchos casos, pero pierde terreno una vez que realmente intenta ejecutar su aplicación elegante. Del mismo modo, la tipificación estática puede parecer que es la red de seguridad perfecta, pero un vistazo a algunas de las definiciones de tipos genéricos más complicadas en Java envía a la mayoría de los desarrolladores a buscar anteojeras. Incluso con los sistemas de tipos y la productividad, no hay bala de plata.
Nota final: no se preocupe por el rendimiento al comparar la estática con la escritura dinámica. Los JIT modernos como V8 y TraceMonkey se acercan peligrosamente al rendimiento del lenguaje estático. Además, el hecho de que Java en realidad se compile en un lenguaje intermedio inherentemente dinámico debería ser un indicio de que, en la mayoría de los casos, la tipificación dinámica no es la gran causa de muerte que algunas personas creen que es.
Quizás el mayor "beneficio" de la tipificación dinámica es la curva de aprendizaje menos profunda. No hay un sistema de tipos que aprender ni una sintaxis no trivial para los casos de esquina, como las restricciones de tipo. Eso hace que la escritura dinámica sea accesible para muchas más personas y sea factible para muchas personas para quienes los sistemas de tipo estático sofisticados están fuera de su alcance. En consecuencia, la escritura dinámica se ha popularizado en los contextos de educación (por ejemplo, Scheme / Python en MIT) y lenguajes específicos de dominio para no programadores (por ejemplo, Mathematica ). Los lenguajes dinámicos también se han popularizado en nichos donde tienen poca o ninguna competencia (por ejemplo, Javascript).
Los idiomas de tipo dinámico más concisos (por ejemplo, Perl, APL, J, K, Mathematica ) son específicos del dominio y pueden ser significativamente más concisos que los idiomas de tipo estático de propósito general más concisos (por ejemplo, OCaml ) en los nichos para los que fueron diseñados .
Las principales desventajas de la tipificación dinámica son:
Errores de tipo en tiempo de ejecución.
Puede ser muy difícil o incluso prácticamente imposible alcanzar el mismo nivel de corrección y requiere muchas más pruebas.
Sin documentación del compilador-verificado.
Rendimiento deficiente (generalmente en tiempo de ejecución pero a veces en tiempo de compilación, por ejemplo, el esquema de Stalin) y rendimiento impredecible debido a la dependencia de optimizaciones sofisticadas.
Personalmente, crecí en lenguajes dinámicos, pero no los tocaría con un polo de 40 ''como profesional a menos que no hubiera otras opciones viables.
Se trata de la herramienta adecuada para el trabajo. Tampoco es mejor el 100% del tiempo. Ambos sistemas fueron creados por el hombre y tienen fallas. Lo sentimos, pero apestamos y hacemos cosas perfectas.
Me gusta la escritura dinámica porque se sale de mi camino, pero sí, pueden aparecer errores de tiempo de ejecución que no planifiqué. Donde la escritura estática puede corregir los errores mencionados anteriormente, pero vuelve loco a un programador novato (en lenguajes escritos) tratando de convertir entre un carácter constante y una cadena.
Escritura estática: los lenguajes como Java y Scala son de tipo estático.
Las variables deben definirse e inicializarse antes de usarse en un código.
por ej. int x; x = 10;
System.out.println (x);
Escritura dinámica: Perl es un lenguaje de escritura dinámica.
Las variables no necesitan inicializarse antes de ser utilizadas en el código.
y = 10; usa esta variable en la parte posterior del código