language design - dynamically - ¿Por qué la escritura dinámica se asocia a menudo con lenguajes interpretados?
dynamic typing vs static typing (7)
Compiladores + tipos estáticos = código de máquina eficiente
Compiladores + tipos dinámicos = código de máquina ineficiente
Considere el siguiente pseudocódigo:
function foo(a, b) {
return a+b
}
Un lenguaje estático podrá saber (mediante declaración o inferencia) que a y b son enteros, y se compilarán hasta
%reg = addi a,b
O algo similar, de todos modos.
Un compilador para un lenguaje dinámico tendría que emitir código para
1. Comprueba los tipos de a y b
2. manejar cada caso o combinación de casos
%reg1 = typeof a
beq %reg1, int, a_int_case
beq %reg1, float, a_float_case
beq %reg1, string, a_string_case
label a_int_case
%reg1 = typeof b
beq %reg1, int, a_int_b_int_case
beq %reg1, float, a_int_b_float_case
beq %reg1, string, a_int_b_string_case
label a_int_b_int_case
%out = addi a,b
goto done
label a_int_b_float_case
%tmp = mkfloat a
%out = addf %tmp,b
goto done
... Etc. I can''t finish
Si bien podría generar un código de máquina más inteligente que eso, no podría ayudar a generar gran cantidad de código, lo que hace que la compilación no sea una gran ganancia para un lenguaje dinámico.
Dado que los intérpretes son mucho más fáciles de escribir, y la compilación no le hace mucho bien, ¿por qué no escribir un intérprete?
(Los compiladores Just-in-time realmente tienen información de tipo y pueden compilarse hasta en una sola declaración. En realidad, tienen más información que los sistemas de tipo estático, y en teoría pueden hacerlo aún mejor. Se simula todo ensamblador; cualquier parecido con el código real que podría funcionar en una máquina real es pura coincidencia.)
Preguntas sencillas: hago mucha programación (profesional y personalmente) en lenguajes compilados como C ++ / Java y en lenguajes interpretados como Python / Javascript. Personalmente, encuentro que mi código es casi siempre más robusto cuando programo en idiomas tipificados estáticamente. Sin embargo, casi todos los lenguajes interpretados que encuentro utilizan la tipificación dinámica (PHP, Perl, Python, etc.). Sé por qué los lenguajes compilados utilizan la escritura estática (la mayoría de las veces), pero no puedo entender la aversión a la escritura estática en el diseño de lenguaje interpretado.
¿Por qué la desconexión empinada? ¿Es parte de la naturaleza de las lenguas interpretadas? OOP?
Creo que es debido a la naturaleza de los lenguajes interpretados, quieren ser dinámicos, por lo que puede cambiar las cosas en tiempo de ejecución. Debido a esto, un compilador nunca sabe exactamente cuál es el estado del programa después de que se haya ejecutado la siguiente línea de código.
Imagina el siguiente escenario (en Python):
import random
foo = 1
def doSomeStuffWithFoo():
global foo
foo = random.randint(0, 1)
def asign():
global foo
if foo == 1:
return 20
else:
return "Test"
def toBeStaticallyAnalyzed():
myValue = asign()
# A "Compiler" may throw an error here because foo == 0, but at runtime foo maybe 1, so the compiler would be wrong with its assumption
myValue += 20
doSomeStuffWithFoo() # Foo could be 1 or 0 now... or 4 ;)
toBeStaticallyAnalyzed()
Como es de esperar, un compilador no tendría ningún sentido en esta situación. En realidad, podría advertirle sobre la posibilidad de que "myValue" sea algo más que un número. Pero luego en JavaScript eso fallaría porque si "myValue" es una cadena, 20 también se convertiría implícitamente en una cadena, por lo que no se produciría ningún error. Así que puedes obtener miles de advertencias inútiles por todas partes, y no creo que esa sea la intención de un compilador.
La flexibilidad siempre viene con un precio, debe analizar su programa más a fondo o programarlo más cuidadosamente, en otras palabras, usted es el COMPILADOR en situaciones como la anterior.
¿Entonces tu solución como compilador? - Solucionarlo con un "try: except" :)
Creo que la escritura estática lo hace más fácil para los compiladores y esa es la razón principal (si no solo la razón) de que esté presente en los lenguajes compilados.
Para los idiomas interpretados, es más fácil suponer que las variables no tienen tipo (solo los valores) porque no se consideran un placer para los datos que deben caber dentro, sino más bien una etiqueta para los datos que flotan en algún lugar del montón.
Si el programador desea, siempre puede afirmar que la variable tiene un valor del tipo dado (por ejemplo, en la asignación). No hay razón para construirlo en el lenguaje. Por supuesto que no es el mismo tipo de control que tiene para los lenguajes compilados.
Probablemente podría tener un lenguaje en el que tenga que declarar explícitamente el tipo de cada variable, pero si no lo hace, es mucho más fácil hacer cosas interesantes que con la tipificación estática requerirían del programador tipos genéricos complejos muy cuidadosamente diseñados.
Por otra parte. ¿Conoce algún lenguaje compilado dinámicamente escrito (estáticamente, no JIT)?
Interesante pregunta. Por cierto, soy el autor / mantenedor de phc (compilador para PHP), y estoy haciendo mi doctorado en compiladores para lenguajes dinámicos, así que espero poder ofrecer algunas ideas.
Creo que hay una suposición errónea aquí. Los autores de PHP, Perl, Python, Ruby, Lua, etc. no diseñaron "lenguajes interpretados", diseñaron lenguajes dinámicos y los implementaron utilizando intérpretes. Lo hicieron porque los intérpretes son mucho más fáciles de escribir que los compiladores.
La primera implementación de Java fue interpretada, y es un lenguaje tipado estáticamente. Los intérpretes existen para lenguajes estáticos: tanto Haskell como OCaml tienen intérpretes, y solía haber un intérprete popular para C, pero eso fue hace mucho tiempo. Son populares porque permiten un REPL , lo que puede facilitar el desarrollo.
Dicho esto, existe una aversión a la escritura estática en la comunidad de lenguaje dinámico, como es de esperar. Creen que los sistemas de tipo estático proporcionados por C, C ++ y Java son detallados y no merecen la pena. Creo que estoy de acuerdo con esto en cierta medida. Programar en Python es mucho más divertido que C ++.
Para abordar los puntos de los demás:
dlamblin dice : "Nunca sentí con firmeza que hubiera algo especial en la compilación frente a la interpretación que sugiriera dinámica sobre la tipificación estática". Bueno, estás muy equivocado allí. La compilación de lenguajes dinámicos es muy difícil. En su mayoría, se debe considerar la declaración
eval
, que se usa ampliamente en Javascript y Ruby. phc compila PHP con anticipación, pero aún necesitamos un intérprete de tiempo de ejecución para manejar laseval
.eval
tampoco puede analizarse estáticamente en un compilador de optimización, aunque existe una técnica genial si no necesita solidez.A la respuesta de Damblin a Andrew Hare : por supuesto, podría realizar un análisis estático en un intérprete y encontrar errores antes del tiempo de ejecución, que es exactamente lo que hace el
ghci
de Haskell. Espero que el estilo de intérprete usado en lenguajes funcionales lo requiera. Por supuesto, dlamblin tiene razón al decir que el análisis no es parte de la interpretación.La respuesta de Andrew Hare se basa en la suposición errónea de los interrogadores, y de manera similar tiene las cosas al revés. Sin embargo, plantea una pregunta interesante: "¿qué tan difícil es el análisis estático de lenguajes dinámicos?". Muy muy difícil. Básicamente, obtendrás un doctorado por describir cómo funciona, que es exactamente lo que estoy haciendo. Véase también el punto anterior.
La respuesta más correcta hasta ahora es la de Ivo Wetzel . Sin embargo, los puntos que describe pueden manejarse en tiempo de ejecución en un compilador, y existen muchos compiladores para Lisp y Scheme que tienen este tipo de enlace dinámico. Pero, sí, es complicado.
Los lenguajes interpretados tipificados dinámicamente le brindan más libertad en la forma en que programa. Permite que la meta programación sea factible. Permite la creación de variables en tiempo de ejecución. Permite la creación de hashes anónimos y matrices anónimas en cualquier momento dado durante el tiempo de ejecución sin declarar previamente nada de antemano. Permite que se ingrese información indeterminada en un hash sin declarar todas las claves de antemano. Puede tener subrutinas creadas a partir de una entrada aleatoria no determinada. También puede alimentar un código de programa que puede ejecutarse dinámicamente. Los lenguajes interpretados liberan las cadenas desde donde limita la programación en general. Usted está limitado a lo que escribe en el archivo fuente con idiomas tipificados estáticamente. Puede hacer más con menos en un lenguaje de tipo dinámico.
La mayoría de los robots que se fabrican hoy en día tratan más con los idiomas interpretados porque la información debe determinarse en tiempo de ejecución y se deben crear nuevas variables para almacenar esta información en tiempo de ejecución. El aprendizaje automático se basa en la información que se interpreta. Nosotros mismos como humanos somos intérpretes, por eso los robots se diseñan de esa manera. El futuro realmente se interpreta. Por supuesto, necesita lenguajes con tipos estáticos para crear intérpretes, de modo que los idiomas con tipos estáticos nunca desaparecerán a menos que los intérpretes se construyan con código ensamblador en el futuro. La mayoría de los intérpretes se basan en idiomas tipificados estáticamente en estos días.
Los lenguajes interpretados sobresalen en un entorno dinámico. Si puede interpretar el nuevo código / información en tiempo de ejecución, ¿por qué no? Si eres realmente bueno en la programación dinámica, entonces puedes crear código que pueda crear variables y hashes sin tener que escribir todo. Puede reducir drásticamente la cantidad de líneas si trabaja con grandes cantidades de datos. Puede usar un dumper de datos para imprimir toda su información, ya que los idiomas interpretados generalmente hacen un seguimiento del tipo de variables en tiempo de ejecución, lo que permite que esto sea posible. No puedes hacer esto en barebones c ++. La única vez que c ++ yc saben lo que sucede es en tiempo de compilación. Después de eso, estarás solo, a menos que implementes algo tú mismo.
¿Quién quiere estar atado tanto al archivo de origen en estos días, especialmente cuando trabaja en entornos dinámicos? Todo lo que haces es limitar tu potencial. Una vez que haya metido la cabeza en el código interpretado dinámicamente y regrese a cualquier lenguaje de tipo estático, le resultará más difícil silenciar su código porque aún piensa en una mentalidad ilimitada. Tu mente debe volver a estar limitada de nuevo a lo que está escrito en el archivo fuente.
En los estilos de programación: el código tipificado estáticamente produce resultados estáticos. El código escrito dinámicamente produce resultados dinámicos o estáticos.
Si va a programar algo que nunca cambia el comportamiento más allá de lo que se conoce, entonces los lenguajes tipificados estáticamente son excelentes para eso. Si se trata de comportamientos dinámicos, los idiomas tipificados dinámicamente son más adecuados para esos casos. Todo depende de la situación en su mayoría.
Cada idioma tiene sus altibajos. Sólo tengo que elegir y elegir sabiamente.
Los lenguajes interpretados utilizan la escritura dinámica porque no hay un paso de compilación para realizar el análisis estático. Los lenguajes compilados realizan análisis estáticos en el momento de la compilación, lo que significa que cualquier error de tipo se informa al desarrollador mientras funcionan.
Es más fácil de entender si considera que un lenguaje de tipo estático tiene un compilador que impone reglas de tipo fuera del contexto de ejecución. Los idiomas interpretados nunca se analizan de forma estática, por lo que el intérprete debe aplicar las reglas de tipo en el contexto de la ejecución.
Tal vez sea porque uno de mis principales idiomas interpretados es Perl y uno de mis lenguajes compilados es Objective-C, pero nunca sentí enérgicamente que hubiera algo especial en la compilación frente a la interpretación que sugiriera dinámica sobre la tipificación estática.
Creo que está claro que ambas partes están mirando a la otra y pensando: "Hay algunas ventajas en eso". Es más fácil en algunas aplicaciones obtener cierta flexibilidad de tipo dinámico, mientras que puede ser más fácil mantener algo que se escribe y se aplica estáticamente.
Sin embargo, no estoy de acuerdo con la explicación de Andrew Hare . Si bien un lenguaje puramente interpretado tendría que agregarse en un paso de preprocesamiento y, por lo tanto, no se debería interpretar simplemente para advertir al programador antes de la ejecución de errores tipográficos estáticos, no impide lanzar un error de tipo en tiempo de ejecución a medida que se produce. Por lo tanto, la falta de compilación no significa que no se pueda realizar una comprobación de tipos estática. Pero dado que obtener un error de tipo en el tiempo de ejecución no es tan útil como obtener uno en la compilación, o durante una verificación previa, puedo ver cómo la "ventaja" de la escritura estática en esa situación puede parecer más una molestia. y, por lo tanto, ser expulsado en favor de las ventajas que aporta la escritura dinámica.
Si supo desde el principio que prefiere mantener sus tipos estáticos porque personalmente, como resultado, escribe mejor un código más mantenible, y estaba diseñando su lenguaje interpretado, nada debería impedirle diseñar el lenguaje como uno tipificado estáticamente.
Para citar el artículo de wiki de lenguajes interpretados "Teóricamente, cualquier lenguaje se puede compilar o interpretar, por lo que esta designación se aplica únicamente debido a la práctica común de implementación y no a alguna propiedad subyacente de un idioma".
Hay un artículo de wiki decente solo sobre la escritura.